summaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
Diffstat (limited to 'app')
-rw-r--r--app/assets/images/authbuttons/bitbucket_32.pngbin0 -> 2713 bytes
-rw-r--r--app/assets/images/authbuttons/bitbucket_64.pngbin0 -> 2163 bytes
-rw-r--r--app/assets/images/authbuttons/github_32.pngbin1902 -> 1822 bytes
-rw-r--r--app/assets/images/authbuttons/github_64.pngbin4444 -> 4196 bytes
-rw-r--r--app/assets/images/authbuttons/gitlab_32.pngbin0 -> 1039 bytes
-rw-r--r--app/assets/images/authbuttons/gitlab_64.pngbin0 -> 3013 bytes
-rw-r--r--app/assets/images/authbuttons/google_32.pngbin1611 -> 1501 bytes
-rw-r--r--app/assets/images/authbuttons/google_64.pngbin3437 -> 3169 bytes
-rw-r--r--app/assets/images/authbuttons/twitter_32.pngbin1417 -> 1311 bytes
-rw-r--r--app/assets/images/authbuttons/twitter_64.pngbin3328 -> 3054 bytes
-rw-r--r--app/assets/images/bg-header.pngbin210 -> 90 bytes
-rw-r--r--app/assets/images/bg_fallback.pngbin2976 -> 167 bytes
-rw-r--r--app/assets/images/brand_logo.pngbin32119 -> 27059 bytes
-rw-r--r--app/assets/images/chosen-sprite.pngbin396 -> 367 bytes
-rw-r--r--app/assets/images/dark-scheme-preview.pngbin9873 -> 3996 bytes
-rw-r--r--app/assets/images/diff_note_add.pngbin691 -> 418 bytes
-rw-r--r--app/assets/images/gitorious-logo-black.pngbin0 -> 809 bytes
-rw-r--r--app/assets/images/gitorious-logo-blue.pngbin0 -> 495 bytes
-rw-r--r--app/assets/images/icon-link.pngbin1019 -> 726 bytes
-rw-r--r--app/assets/images/icon-search.pngbin331 -> 222 bytes
-rw-r--r--app/assets/images/icon_sprite.pngbin2782 -> 2636 bytes
-rw-r--r--app/assets/images/images.pngbin6644 -> 5849 bytes
-rw-r--r--app/assets/images/logo-black.pngbin2797 -> 0 bytes
-rw-r--r--app/assets/images/logo-white.pngbin7501 -> 7699 bytes
-rw-r--r--app/assets/images/monokai-scheme-preview.pngbin4332 -> 3711 bytes
-rw-r--r--app/assets/images/move.pngbin260 -> 197 bytes
-rw-r--r--app/assets/images/no_avatar.pngbin704 -> 621 bytes
-rw-r--r--app/assets/images/no_group_avatar.pngbin4884 -> 942 bytes
-rw-r--r--app/assets/images/slider_handles.pngbin4122 -> 1377 bytes
-rw-r--r--app/assets/images/solarized-dark-scheme-preview.pngbin9902 -> 3195 bytes
-rw-r--r--app/assets/images/solarized-light-scheme-preview.pngbin0 -> 3095 bytes
-rw-r--r--app/assets/images/switch_icon.pngbin1197 -> 231 bytes
-rw-r--r--app/assets/images/trans_bg.gifbin50 -> 49 bytes
-rw-r--r--app/assets/images/white-scheme-preview.pngbin10022 -> 3751 bytes
-rw-r--r--app/assets/javascripts/activities.js.coffee2
-rw-r--r--app/assets/javascripts/api.js.coffee29
-rw-r--r--app/assets/javascripts/application.js.coffee47
-rw-r--r--app/assets/javascripts/aside.js.coffee17
-rw-r--r--app/assets/javascripts/autosave.js.coffee33
-rw-r--r--app/assets/javascripts/blob/blob.js.coffee (renamed from app/assets/javascripts/blob.js.coffee)0
-rw-r--r--app/assets/javascripts/blob/edit_blob.js.coffee44
-rw-r--r--app/assets/javascripts/blob/new_blob.js.coffee21
-rw-r--r--app/assets/javascripts/calendar.js.coffee30
-rw-r--r--app/assets/javascripts/diff.js.coffee3
-rw-r--r--app/assets/javascripts/dispatcher.js.coffee20
-rw-r--r--app/assets/javascripts/dropzone_input.js.coffee240
-rw-r--r--app/assets/javascripts/groups_select.js.coffee41
-rw-r--r--app/assets/javascripts/importer_status.js.coffee35
-rw-r--r--app/assets/javascripts/issue.js.coffee11
-rw-r--r--app/assets/javascripts/issues.js.coffee6
-rw-r--r--app/assets/javascripts/markdown_area.js.coffee196
-rw-r--r--app/assets/javascripts/merge_request.js.coffee28
-rw-r--r--app/assets/javascripts/notes.js.coffee125
-rw-r--r--app/assets/javascripts/password_strength.js.coffee31
-rw-r--r--app/assets/javascripts/project.js.coffee8
-rw-r--r--app/assets/javascripts/project_avatar.js.coffee9
-rw-r--r--app/assets/javascripts/project_fork.js.coffee5
-rw-r--r--app/assets/javascripts/project_new.js.coffee14
-rw-r--r--app/assets/javascripts/project_show.js.coffee2
-rw-r--r--app/assets/javascripts/project_users_select.js.coffee2
-rw-r--r--app/assets/javascripts/protected_branches.js.coffee21
-rw-r--r--app/assets/javascripts/sidebar.js.coffee38
-rw-r--r--app/assets/javascripts/stat_graph_contributors_graph.js.coffee4
-rw-r--r--app/assets/javascripts/team_members.js.coffee4
-rw-r--r--app/assets/javascripts/zen_mode.js.coffee12
-rw-r--r--app/assets/stylesheets/application.scss7
-rw-r--r--app/assets/stylesheets/behaviors.scss26
-rw-r--r--app/assets/stylesheets/generic/avatar.scss29
-rw-r--r--app/assets/stylesheets/generic/buttons.scss5
-rw-r--r--app/assets/stylesheets/generic/calendar.scss95
-rw-r--r--app/assets/stylesheets/generic/common.scss53
-rw-r--r--app/assets/stylesheets/generic/files.scss3
-rw-r--r--app/assets/stylesheets/generic/forms.scss143
-rw-r--r--app/assets/stylesheets/generic/gfm.scss3
-rw-r--r--app/assets/stylesheets/generic/highlight.scss38
-rw-r--r--app/assets/stylesheets/generic/issue_box.scss117
-rw-r--r--app/assets/stylesheets/generic/lists.scss3
-rw-r--r--app/assets/stylesheets/generic/markdown_area.scss27
-rw-r--r--app/assets/stylesheets/generic/mobile.scss74
-rw-r--r--app/assets/stylesheets/generic/selects.scss12
-rw-r--r--app/assets/stylesheets/generic/sidebar.scss46
-rw-r--r--app/assets/stylesheets/generic/tables.scss20
-rw-r--r--app/assets/stylesheets/generic/timeline.scss59
-rw-r--r--app/assets/stylesheets/generic/typography.scss8
-rw-r--r--app/assets/stylesheets/generic/zen.scss98
-rw-r--r--app/assets/stylesheets/gl_bootstrap.scss7
-rw-r--r--app/assets/stylesheets/highlight/dark.scss277
-rw-r--r--app/assets/stylesheets/highlight/monokai.scss212
-rw-r--r--app/assets/stylesheets/highlight/solarized_dark.scss205
-rw-r--r--app/assets/stylesheets/highlight/solarized_light.scss106
-rw-r--r--app/assets/stylesheets/highlight/white.scss265
-rw-r--r--app/assets/stylesheets/main/layout.scss9
-rw-r--r--app/assets/stylesheets/main/mixins.scss15
-rw-r--r--app/assets/stylesheets/main/variables.scss20
-rw-r--r--app/assets/stylesheets/sections/commit.scss131
-rw-r--r--app/assets/stylesheets/sections/commits.scss172
-rw-r--r--app/assets/stylesheets/sections/dashboard.scss52
-rw-r--r--app/assets/stylesheets/sections/diff.scss27
-rw-r--r--app/assets/stylesheets/sections/editor.scss22
-rw-r--r--app/assets/stylesheets/sections/events.scss85
-rw-r--r--app/assets/stylesheets/sections/header.scss119
-rw-r--r--app/assets/stylesheets/sections/import.scss18
-rw-r--r--app/assets/stylesheets/sections/issuable.scss41
-rw-r--r--app/assets/stylesheets/sections/issues.scss33
-rw-r--r--app/assets/stylesheets/sections/login.scss91
-rw-r--r--app/assets/stylesheets/sections/markdown_area.scss9
-rw-r--r--app/assets/stylesheets/sections/merge_requests.scss66
-rw-r--r--app/assets/stylesheets/sections/nav.scss96
-rw-r--r--app/assets/stylesheets/sections/nav_sidebar.scss187
-rw-r--r--app/assets/stylesheets/sections/note_form.scss175
-rw-r--r--app/assets/stylesheets/sections/notes.scss225
-rw-r--r--app/assets/stylesheets/sections/profile.scss26
-rw-r--r--app/assets/stylesheets/sections/projects.scss50
-rw-r--r--app/assets/stylesheets/sections/tree.scss24
-rw-r--r--app/assets/stylesheets/sections/votes.scss10
-rw-r--r--app/assets/stylesheets/themes/dark-theme.scss63
-rw-r--r--app/assets/stylesheets/themes/ui_basic.scss19
-rw-r--r--app/assets/stylesheets/themes/ui_color.scss41
-rw-r--r--app/assets/stylesheets/themes/ui_gray.scss31
-rw-r--r--app/assets/stylesheets/themes/ui_mars.scss37
-rw-r--r--app/assets/stylesheets/themes/ui_modern.scss41
-rw-r--r--app/controllers/admin/application_settings_controller.rb34
-rw-r--r--app/controllers/admin/applications_controller.rb52
-rw-r--r--app/controllers/admin/dashboard_controller.rb6
-rw-r--r--app/controllers/admin/groups_controller.rb5
-rw-r--r--app/controllers/admin/keys_controller.rb34
-rw-r--r--app/controllers/admin/projects_controller.rb7
-rw-r--r--app/controllers/admin/services_controller.rb51
-rw-r--r--app/controllers/admin/users_controller.rb12
-rw-r--r--app/controllers/application_controller.rb96
-rw-r--r--app/controllers/dashboard_controller.rb34
-rw-r--r--app/controllers/explore/projects_controller.rb2
-rw-r--r--app/controllers/files_controller.rb16
-rw-r--r--app/controllers/groups_controller.rb46
-rw-r--r--app/controllers/import/base_controller.rb21
-rw-r--r--app/controllers/import/bitbucket_controller.rb79
-rw-r--r--app/controllers/import/github_controller.rb65
-rw-r--r--app/controllers/import/gitlab_controller.rb62
-rw-r--r--app/controllers/import/gitorious_controller.rb43
-rw-r--r--app/controllers/namespaces_controller.rb1
-rw-r--r--app/controllers/oauth/applications_controller.rb39
-rw-r--r--app/controllers/oauth/authorizations_controller.rb57
-rw-r--r--app/controllers/oauth/authorized_applications_controller.rb8
-rw-r--r--app/controllers/omniauth_callbacks_controller.rb12
-rw-r--r--app/controllers/passwords_controller.rb4
-rw-r--r--app/controllers/profiles/emails_controller.rb3
-rw-r--r--app/controllers/profiles/keys_controller.rb2
-rw-r--r--app/controllers/profiles/notifications_controller.rb22
-rw-r--r--app/controllers/profiles/passwords_controller.rb8
-rw-r--r--app/controllers/profiles_controller.rb8
-rw-r--r--app/controllers/projects/application_controller.rb8
-rw-r--r--app/controllers/projects/avatars_controller.rb29
-rw-r--r--app/controllers/projects/base_tree_controller.rb8
-rw-r--r--app/controllers/projects/blame_controller.rb5
-rw-r--r--app/controllers/projects/blob_controller.rb121
-rw-r--r--app/controllers/projects/branches_controller.rb15
-rw-r--r--app/controllers/projects/commit_controller.rb10
-rw-r--r--app/controllers/projects/commits_controller.rb7
-rw-r--r--app/controllers/projects/compare_controller.rb6
-rw-r--r--app/controllers/projects/deploy_keys_controller.rb9
-rw-r--r--app/controllers/projects/edit_tree_controller.rb60
-rw-r--r--app/controllers/projects/forks_controller.rb25
-rw-r--r--app/controllers/projects/graphs_controller.rb3
-rw-r--r--app/controllers/projects/hooks_controller.rb5
-rw-r--r--app/controllers/projects/imports_controller.rb51
-rw-r--r--app/controllers/projects/issues_controller.rb22
-rw-r--r--app/controllers/projects/labels_controller.rb18
-rw-r--r--app/controllers/projects/merge_requests_controller.rb43
-rw-r--r--app/controllers/projects/milestones_controller.rb11
-rw-r--r--app/controllers/projects/network_controller.rb5
-rw-r--r--app/controllers/projects/new_tree_controller.rb20
-rw-r--r--app/controllers/projects/notes_controller.rb4
-rw-r--r--app/controllers/projects/protected_branches_controller.rb25
-rw-r--r--app/controllers/projects/raw_controller.rb6
-rw-r--r--app/controllers/projects/refs_controller.rb29
-rw-r--r--app/controllers/projects/repositories_controller.rb10
-rw-r--r--app/controllers/projects/services_controller.rb22
-rw-r--r--app/controllers/projects/snippets_controller.rb9
-rw-r--r--app/controllers/projects/tags_controller.rb9
-rw-r--r--app/controllers/projects/team_members_controller.rb13
-rw-r--r--app/controllers/projects/tree_controller.rb14
-rw-r--r--app/controllers/projects/uploads_controller.rb35
-rw-r--r--app/controllers/projects/wikis_controller.rb37
-rw-r--r--app/controllers/projects_controller.rb156
-rw-r--r--app/controllers/registrations_controller.rb12
-rw-r--r--app/controllers/sessions_controller.rb22
-rw-r--r--app/controllers/snippets_controller.rb5
-rw-r--r--app/controllers/uploads_controller.rb24
-rw-r--r--app/controllers/users_controller.rb53
-rw-r--r--app/finders/issuable_finder.rb9
-rw-r--r--app/finders/notes_finder.rb27
-rw-r--r--app/finders/snippets_finder.rb2
-rw-r--r--app/finders/trending_projects_finder.rb2
-rw-r--r--app/helpers/appearances_helper.rb4
-rw-r--r--app/helpers/application_helper.rb136
-rw-r--r--app/helpers/application_settings_helper.rb21
-rw-r--r--app/helpers/blob_helper.rb68
-rw-r--r--app/helpers/branches_helper.rb9
-rw-r--r--app/helpers/commits_helper.rb89
-rw-r--r--app/helpers/compare_helper.rb11
-rw-r--r--app/helpers/dashboard_helper.rb54
-rw-r--r--app/helpers/diff_helper.rb33
-rw-r--r--app/helpers/emails_helper.rb42
-rw-r--r--app/helpers/events_helper.rb143
-rw-r--r--app/helpers/git_helper.rb5
-rw-r--r--app/helpers/gitlab_markdown_helper.rb21
-rw-r--r--app/helpers/gitlab_routing_helper.rb31
-rw-r--r--app/helpers/graph_helper.rb4
-rw-r--r--app/helpers/groups_helper.rb18
-rw-r--r--app/helpers/icons_helper.rb28
-rw-r--r--app/helpers/issues_helper.rb61
-rw-r--r--app/helpers/labels_helper.rb21
-rw-r--r--app/helpers/merge_requests_helper.rb18
-rw-r--r--app/helpers/milestones_helper.rb9
-rw-r--r--app/helpers/namespaces_helper.rb8
-rw-r--r--app/helpers/nav_helper.rb5
-rw-r--r--app/helpers/notes_helper.rb23
-rw-r--r--app/helpers/notifications_helper.rb8
-rw-r--r--app/helpers/oauth_helper.rb10
-rw-r--r--app/helpers/profile_helper.rb6
-rw-r--r--app/helpers/projects_helper.rb116
-rw-r--r--app/helpers/search_helper.rb22
-rw-r--r--app/helpers/selects_helper.rb9
-rw-r--r--app/helpers/snippets_helper.rb3
-rw-r--r--app/helpers/sorting_helper.rb96
-rw-r--r--app/helpers/submodule_helper.rb33
-rw-r--r--app/helpers/tab_helper.rb57
-rw-r--r--app/helpers/tags_helper.rb2
-rw-r--r--app/helpers/tree_helper.rb48
-rw-r--r--app/mailers/emails/issues.rb8
-rw-r--r--app/mailers/emails/merge_requests.rb22
-rw-r--r--app/mailers/emails/notes.rb13
-rw-r--r--app/mailers/emails/profile.rb9
-rw-r--r--app/mailers/emails/projects.rb14
-rw-r--r--app/mailers/notify.rb13
-rw-r--r--app/models/ability.rb26
-rw-r--r--app/models/application_setting.rb43
-rw-r--r--app/models/broadcast_message.rb2
-rw-r--r--app/models/commit.rb25
-rw-r--r--app/models/concerns/issuable.rb40
-rw-r--r--app/models/concerns/mentionable.rb18
-rw-r--r--app/models/concerns/sortable.rb35
-rw-r--r--app/models/email.rb2
-rw-r--r--app/models/event.rb115
-rw-r--r--app/models/external_issue.rb25
-rw-r--r--app/models/group.rb39
-rw-r--r--app/models/group_milestone.rb6
-rw-r--r--app/models/hooks/web_hook.rb11
-rw-r--r--app/models/identity.rb16
-rw-r--r--app/models/issue.rb1
-rw-r--r--app/models/key.rb7
-rw-r--r--app/models/label.rb2
-rw-r--r--app/models/member.rb1
-rw-r--r--app/models/members/group_member.rb14
-rw-r--r--app/models/members/project_member.rb23
-rw-r--r--app/models/merge_request.rb27
-rw-r--r--app/models/merge_request_diff.rb2
-rw-r--r--app/models/milestone.rb1
-rw-r--r--app/models/namespace.rb30
-rw-r--r--app/models/network/graph.rb4
-rw-r--r--app/models/note.rb110
-rw-r--r--app/models/notification.rb10
-rw-r--r--app/models/project.rb274
-rw-r--r--app/models/project_contributions.rb23
-rw-r--r--app/models/project_services/asana_service.rb117
-rw-r--r--app/models/project_services/assembla_service.rb3
-rw-r--r--app/models/project_services/bamboo_service.rb33
-rw-r--r--app/models/project_services/buildbox_service.rb4
-rw-r--r--app/models/project_services/campfire_service.rb7
-rw-r--r--app/models/project_services/ci_service.rb3
-rw-r--r--app/models/project_services/custom_issue_tracker_service.rb53
-rw-r--r--app/models/project_services/emails_on_push_service.rb3
-rw-r--r--app/models/project_services/flowdock_service.rb6
-rw-r--r--app/models/project_services/gemnasium_service.rb6
-rw-r--r--app/models/project_services/gitlab_ci_service.rb9
-rw-r--r--app/models/project_services/gitlab_issue_tracker_service.rb46
-rw-r--r--app/models/project_services/hipchat_service.rb21
-rw-r--r--app/models/project_services/issue_tracker_service.rb114
-rw-r--r--app/models/project_services/jira_service.rb53
-rw-r--r--app/models/project_services/pivotaltracker_service.rb3
-rw-r--r--app/models/project_services/pushover_service.rb7
-rw-r--r--app/models/project_services/redmine_service.rb39
-rw-r--r--app/models/project_services/slack_message.rb20
-rw-r--r--app/models/project_services/slack_service.rb3
-rw-r--r--app/models/project_services/teamcity_service.rb134
-rw-r--r--app/models/project_team.rb2
-rw-r--r--app/models/project_wiki.rb4
-rw-r--r--app/models/protected_branch.rb11
-rw-r--r--app/models/repository.rb96
-rw-r--r--app/models/service.rb33
-rw-r--r--app/models/snippet.rb13
-rw-r--r--app/models/user.rb153
-rw-r--r--app/models/wiki_page.rb2
-rw-r--r--app/services/base_service.rb13
-rw-r--r--app/services/create_branch_service.rb2
-rw-r--r--app/services/create_tag_service.rb16
-rw-r--r--app/services/delete_branch_service.rb2
-rw-r--r--app/services/event_create_service.rb72
-rw-r--r--app/services/files/create_service.rb28
-rw-r--r--app/services/files/delete_service.rb6
-rw-r--r--app/services/files/update_service.rb23
-rw-r--r--app/services/git_push_service.rb110
-rw-r--r--app/services/git_tag_push_service.rb33
-rw-r--r--app/services/gravatar_service.rb4
-rw-r--r--app/services/issuable_base_service.rb5
-rw-r--r--app/services/issues/base_service.rb2
-rw-r--r--app/services/issues/close_service.rb2
-rw-r--r--app/services/issues/update_service.rb9
-rw-r--r--app/services/merge_requests/auto_merge_service.rb7
-rw-r--r--app/services/merge_requests/base_merge_service.rb12
-rw-r--r--app/services/merge_requests/base_service.rb8
-rw-r--r--app/services/merge_requests/build_service.rb16
-rw-r--r--app/services/merge_requests/close_service.rb4
-rw-r--r--app/services/merge_requests/merge_service.rb7
-rw-r--r--app/services/merge_requests/refresh_service.rb94
-rw-r--r--app/services/merge_requests/reopen_service.rb4
-rw-r--r--app/services/merge_requests/update_service.rb14
-rw-r--r--app/services/notes/update_service.rb25
-rw-r--r--app/services/notification_service.rb73
-rw-r--r--app/services/oauth2/access_token_validation_service.rb41
-rw-r--r--app/services/projects/autocomplete_service.rb15
-rw-r--r--app/services/projects/create_service.rb71
-rw-r--r--app/services/projects/fork_service.rb33
-rw-r--r--app/services/projects/image_service.rb39
-rw-r--r--app/services/projects/participants_service.rb32
-rw-r--r--app/services/projects/transfer_service.rb2
-rw-r--r--app/services/projects/upload_service.rb22
-rw-r--r--app/services/system_hooks_service.rb25
-rw-r--r--app/services/test_hook_service.rb5
-rw-r--r--app/uploaders/attachment_uploader.rb10
-rw-r--r--app/uploaders/avatar_uploader.rb32
-rw-r--r--app/uploaders/file_uploader.rb43
-rw-r--r--app/views/admin/application_settings/_form.html.haml48
-rw-r--r--app/views/admin/application_settings/show.html.haml3
-rw-r--r--app/views/admin/applications/_delete_form.html.haml4
-rw-r--r--app/views/admin/applications/_form.html.haml24
-rw-r--r--app/views/admin/applications/edit.html.haml3
-rw-r--r--app/views/admin/applications/index.html.haml22
-rw-r--r--app/views/admin/applications/new.html.haml3
-rw-r--r--app/views/admin/applications/show.html.haml26
-rw-r--r--app/views/admin/dashboard/index.html.haml126
-rw-r--r--app/views/admin/groups/_form.html.haml11
-rw-r--r--app/views/admin/groups/index.html.haml21
-rw-r--r--app/views/admin/groups/show.html.haml4
-rw-r--r--app/views/admin/keys/show.html.haml1
-rw-r--r--app/views/admin/projects/index.html.haml56
-rw-r--r--app/views/admin/projects/show.html.haml10
-rw-r--r--app/views/admin/services/_form.html.haml40
-rw-r--r--app/views/admin/services/edit.html.haml1
-rw-r--r--app/views/admin/services/index.html.haml22
-rw-r--r--app/views/admin/users/index.html.haml35
-rw-r--r--app/views/admin/users/show.html.haml10
-rw-r--r--app/views/dashboard/_activities.html.haml7
-rw-r--r--app/views/dashboard/_groups.html.haml14
-rw-r--r--app/views/dashboard/_project.html.haml2
-rw-r--r--app/views/dashboard/_projects.html.haml11
-rw-r--r--app/views/dashboard/_projects_filter.html.haml145
-rw-r--r--app/views/dashboard/_sidebar.html.haml9
-rw-r--r--app/views/dashboard/_zero_authorized_projects.html.haml14
-rw-r--r--app/views/dashboard/issues.atom.builder19
-rw-r--r--app/views/dashboard/issues.html.haml10
-rw-r--r--app/views/dashboard/merge_requests.html.haml10
-rw-r--r--app/views/dashboard/projects.html.haml111
-rw-r--r--app/views/dashboard/show.atom.builder23
-rw-r--r--app/views/dashboard/show.html.haml7
-rw-r--r--[-rwxr-xr-x]app/views/devise/confirmations/new.html.haml7
-rw-r--r--app/views/devise/passwords/edit.html.haml15
-rw-r--r--[-rwxr-xr-x]app/views/devise/passwords/new.html.haml7
-rw-r--r--app/views/devise/registrations/new.html.haml30
-rw-r--r--app/views/devise/sessions/_new_base.html.haml8
-rw-r--r--app/views/devise/sessions/_new_ldap.html.haml1
-rw-r--r--app/views/devise/sessions/_oauth_providers.html.haml10
-rw-r--r--app/views/devise/sessions/new.html.haml47
-rw-r--r--app/views/devise/shared/_signin_box.html.haml42
-rw-r--r--app/views/devise/shared/_signup_box.html.haml26
-rw-r--r--app/views/doorkeeper/applications/_delete_form.html.haml4
-rw-r--r--app/views/doorkeeper/applications/_form.html.haml24
-rw-r--r--app/views/doorkeeper/applications/edit.html.haml2
-rw-r--r--app/views/doorkeeper/applications/index.html.haml16
-rw-r--r--app/views/doorkeeper/applications/new.html.haml2
-rw-r--r--app/views/doorkeeper/applications/show.html.haml26
-rw-r--r--app/views/doorkeeper/authorizations/error.html.haml3
-rw-r--r--app/views/doorkeeper/authorizations/new.html.haml28
-rw-r--r--app/views/doorkeeper/authorizations/show.html.haml3
-rw-r--r--app/views/doorkeeper/authorized_applications/_delete_form.html.haml4
-rw-r--r--app/views/doorkeeper/authorized_applications/index.html.haml16
-rw-r--r--app/views/events/_commit.html.haml2
-rw-r--r--app/views/events/_event.html.haml9
-rw-r--r--app/views/events/_event_last_push.html.haml2
-rw-r--r--app/views/events/_event_push.atom.haml2
-rw-r--r--app/views/events/event/_common.html.haml12
-rw-r--r--app/views/events/event/_created_project.html.haml27
-rw-r--r--app/views/events/event/_note.html.haml12
-rw-r--r--app/views/events/event/_push.html.haml6
-rw-r--r--app/views/explore/groups/index.html.haml23
-rw-r--r--app/views/explore/projects/_project.html.haml16
-rw-r--r--app/views/explore/projects/index.html.haml22
-rw-r--r--app/views/groups/_filter.html.haml12
-rw-r--r--app/views/groups/_new_group_member.html.haml8
-rw-r--r--app/views/groups/_projects.html.haml2
-rw-r--r--app/views/groups/_settings_nav.html.haml13
-rw-r--r--app/views/groups/edit.html.haml70
-rw-r--r--app/views/groups/issues.atom.builder13
-rw-r--r--app/views/groups/issues.html.haml10
-rw-r--r--app/views/groups/merge_requests.html.haml10
-rw-r--r--app/views/groups/milestones/_issue.html.haml4
-rw-r--r--app/views/groups/milestones/_merge_request.html.haml4
-rw-r--r--app/views/groups/milestones/index.html.haml72
-rw-r--r--app/views/groups/milestones/show.html.haml70
-rw-r--r--app/views/groups/projects.html.haml52
-rw-r--r--app/views/groups/show.atom.builder22
-rw-r--r--app/views/groups/show.html.haml54
-rw-r--r--app/views/help/index.html.haml6
-rw-r--r--app/views/help/show.html.haml2
-rw-r--r--app/views/import/base/create.js.haml25
-rw-r--r--app/views/import/bitbucket/status.html.haml46
-rw-r--r--app/views/import/github/status.html.haml46
-rw-r--r--app/views/import/gitlab/status.html.haml46
-rw-r--r--app/views/import/gitorious/status.html.haml46
-rw-r--r--app/views/layouts/_collapse_button.html.haml4
-rw-r--r--app/views/layouts/_head.html.haml14
-rw-r--r--app/views/layouts/_head_panel.html.haml12
-rw-r--r--app/views/layouts/_init_auto_complete.html.haml2
-rw-r--r--app/views/layouts/_page.html.haml23
-rw-r--r--app/views/layouts/_public_head_panel.html.haml20
-rw-r--r--app/views/layouts/_search.html.haml11
-rw-r--r--app/views/layouts/admin.html.haml13
-rw-r--r--app/views/layouts/application.html.haml12
-rw-r--r--app/views/layouts/devise.html.haml43
-rw-r--r--app/views/layouts/doorkeeper/admin.html.haml22
-rw-r--r--app/views/layouts/doorkeeper/application.html.haml15
-rw-r--r--app/views/layouts/errors.html.haml2
-rw-r--r--app/views/layouts/explore.html.haml6
-rw-r--r--app/views/layouts/group.html.haml12
-rw-r--r--app/views/layouts/nav/_admin.html.haml59
-rw-r--r--app/views/layouts/nav/_dashboard.html.haml33
-rw-r--r--app/views/layouts/nav/_group.html.haml51
-rw-r--r--app/views/layouts/nav/_profile.html.haml59
-rw-r--r--app/views/layouts/nav/_project.html.haml140
-rw-r--r--app/views/layouts/navless.html.haml4
-rw-r--r--app/views/layouts/notify.html.haml7
-rw-r--r--app/views/layouts/profile.html.haml12
-rw-r--r--app/views/layouts/project_settings.html.haml17
-rw-r--r--app/views/layouts/projects.html.haml13
-rw-r--r--app/views/layouts/public_group.html.haml10
-rw-r--r--app/views/layouts/public_projects.html.haml8
-rw-r--r--app/views/layouts/public_users.html.haml8
-rw-r--r--app/views/layouts/search.html.haml4
-rw-r--r--app/views/notify/_reassigned_issuable_email.text.erb2
-rw-r--r--app/views/notify/closed_issue_email.text.haml2
-rw-r--r--app/views/notify/closed_merge_request_email.text.haml2
-rw-r--r--app/views/notify/issue_status_changed_email.text.erb2
-rw-r--r--app/views/notify/merge_request_status_email.text.haml2
-rw-r--r--app/views/notify/merged_merge_request_email.text.haml2
-rw-r--r--app/views/notify/new_issue_email.text.erb2
-rw-r--r--app/views/notify/new_merge_request_email.text.erb2
-rw-r--r--app/views/notify/new_ssh_key_email.html.haml2
-rw-r--r--app/views/notify/new_ssh_key_email.text.erb4
-rw-r--r--app/views/notify/note_commit_email.text.erb2
-rw-r--r--app/views/notify/note_issue_email.text.erb2
-rw-r--r--app/views/notify/note_merge_request_email.text.erb2
-rw-r--r--app/views/notify/project_access_granted_email.html.haml2
-rw-r--r--app/views/notify/project_access_granted_email.text.erb2
-rw-r--r--app/views/notify/project_was_moved_email.html.haml2
-rw-r--r--app/views/notify/project_was_moved_email.text.erb2
-rw-r--r--app/views/notify/repository_push_email.html.haml10
-rw-r--r--app/views/notify/repository_push_email.text.haml4
-rw-r--r--app/views/profiles/accounts/show.html.haml9
-rw-r--r--app/views/profiles/applications.html.haml49
-rw-r--r--app/views/profiles/design.html.haml4
-rw-r--r--app/views/profiles/emails/index.html.haml10
-rw-r--r--app/views/profiles/groups/index.html.haml4
-rw-r--r--app/views/profiles/history.html.haml4
-rw-r--r--app/views/profiles/keys/_key.html.haml21
-rw-r--r--app/views/profiles/keys/_key_details.html.haml22
-rw-r--r--app/views/profiles/keys/_key_table.html.haml19
-rw-r--r--app/views/profiles/keys/index.html.haml18
-rw-r--r--app/views/profiles/keys/new.html.haml6
-rw-r--r--app/views/profiles/keys/show.html.haml23
-rw-r--r--app/views/profiles/notifications/show.html.haml77
-rw-r--r--app/views/profiles/passwords/edit.html.haml29
-rw-r--r--app/views/profiles/passwords/new.html.haml11
-rw-r--r--app/views/profiles/show.html.haml6
-rw-r--r--app/views/profiles/update.js.erb4
-rw-r--r--app/views/projects/_bitbucket_import_modal.html.haml13
-rw-r--r--app/views/projects/_dropdown.html.haml10
-rw-r--r--app/views/projects/_github_import_modal.html.haml13
-rw-r--r--app/views/projects/_gitlab_import_modal.html.haml13
-rw-r--r--app/views/projects/_home_panel.html.haml14
-rw-r--r--app/views/projects/_issuable_form.html.haml40
-rw-r--r--app/views/projects/_md_preview.html.haml13
-rw-r--r--app/views/projects/_settings_nav.html.haml32
-rw-r--r--app/views/projects/_visibility_level.html.haml2
-rw-r--r--app/views/projects/_zen.html.haml9
-rw-r--r--app/views/projects/blame/show.html.haml10
-rw-r--r--app/views/projects/blob/_actions.html.haml23
-rw-r--r--app/views/projects/blob/_blob.html.haml8
-rw-r--r--app/views/projects/blob/_download.html.haml2
-rw-r--r--app/views/projects/blob/_editor.html.haml25
-rw-r--r--app/views/projects/blob/_remove.html.haml2
-rw-r--r--app/views/projects/blob/_text.html.haml2
-rw-r--r--app/views/projects/blob/edit.html.haml31
-rw-r--r--app/views/projects/blob/new.html.haml19
-rw-r--r--app/views/projects/blob/preview.html.haml (renamed from app/views/projects/edit_tree/preview.html.haml)0
-rw-r--r--app/views/projects/branches/_branch.html.haml6
-rw-r--r--app/views/projects/branches/index.html.haml12
-rw-r--r--app/views/projects/branches/new.html.haml5
-rw-r--r--app/views/projects/commit/_commit_box.html.haml28
-rw-r--r--app/views/projects/commit/branches.html.haml16
-rw-r--r--app/views/projects/commits/_commit.html.haml18
-rw-r--r--app/views/projects/commits/_commits.html.haml14
-rw-r--r--app/views/projects/commits/_head.html.haml8
-rw-r--r--app/views/projects/commits/_inline_commit.html.haml4
-rw-r--r--app/views/projects/commits/show.atom.builder10
-rw-r--r--app/views/projects/commits/show.html.haml6
-rw-r--r--app/views/projects/compare/_form.html.haml2
-rw-r--r--app/views/projects/create.js.haml13
-rw-r--r--app/views/projects/deploy_keys/_deploy_key.html.haml9
-rw-r--r--app/views/projects/deploy_keys/_form.html.haml4
-rw-r--r--app/views/projects/deploy_keys/index.html.haml4
-rw-r--r--app/views/projects/deploy_keys/show.html.haml4
-rw-r--r--app/views/projects/diffs/_diffs.html.haml12
-rw-r--r--app/views/projects/diffs/_file.html.haml15
-rw-r--r--app/views/projects/diffs/_image.html.haml4
-rw-r--r--app/views/projects/diffs/_warning.html.haml10
-rw-r--r--app/views/projects/edit.html.haml57
-rw-r--r--app/views/projects/edit_tree/show.html.haml72
-rw-r--r--app/views/projects/empty.html.haml17
-rw-r--r--app/views/projects/fork.html.haml19
-rw-r--r--app/views/projects/forks/error.html.haml20
-rw-r--r--app/views/projects/forks/new.html.haml39
-rw-r--r--app/views/projects/graphs/_head.html.haml4
-rw-r--r--app/views/projects/hooks/index.html.haml6
-rw-r--r--app/views/projects/import.html.haml31
-rw-r--r--app/views/projects/imports/new.html.haml21
-rw-r--r--app/views/projects/imports/show.html.haml9
-rw-r--r--app/views/projects/issues/_discussion.html.haml37
-rw-r--r--app/views/projects/issues/_form.html.haml10
-rw-r--r--app/views/projects/issues/_head.html.haml36
-rw-r--r--app/views/projects/issues/_issue.html.haml32
-rw-r--r--app/views/projects/issues/_issue_context.html.haml43
-rw-r--r--app/views/projects/issues/_issues.html.haml63
-rw-r--r--app/views/projects/issues/index.atom.builder19
-rw-r--r--app/views/projects/issues/index.html.haml45
-rw-r--r--app/views/projects/issues/show.html.haml115
-rw-r--r--app/views/projects/issues/update.js.haml11
-rw-r--r--app/views/projects/labels/_form.html.haml6
-rw-r--r--app/views/projects/labels/_label.html.haml6
-rw-r--r--app/views/projects/labels/edit.html.haml2
-rw-r--r--app/views/projects/labels/index.html.haml6
-rw-r--r--app/views/projects/labels/new.html.haml2
-rw-r--r--app/views/projects/merge_requests/_discussion.html.haml33
-rw-r--r--app/views/projects/merge_requests/_form.html.haml4
-rw-r--r--app/views/projects/merge_requests/_head.html.haml2
-rw-r--r--app/views/projects/merge_requests/_merge_request.html.haml49
-rw-r--r--app/views/projects/merge_requests/_new_compare.html.haml12
-rw-r--r--app/views/projects/merge_requests/_new_submit.html.haml153
-rw-r--r--app/views/projects/merge_requests/_show.html.haml74
-rw-r--r--app/views/projects/merge_requests/automerge.js.haml3
-rw-r--r--app/views/projects/merge_requests/index.html.haml92
-rw-r--r--app/views/projects/merge_requests/show/_commits.html.haml31
-rw-r--r--app/views/projects/merge_requests/show/_context.html.haml36
-rw-r--r--app/views/projects/merge_requests/show/_diffs.html.haml2
-rw-r--r--app/views/projects/merge_requests/show/_how_to_merge.html.haml6
-rw-r--r--app/views/projects/merge_requests/show/_mr_accept.html.haml48
-rw-r--r--app/views/projects/merge_requests/show/_mr_box.html.haml22
-rw-r--r--app/views/projects/merge_requests/show/_mr_ci.html.haml6
-rw-r--r--app/views/projects/merge_requests/show/_mr_title.html.haml53
-rw-r--r--app/views/projects/merge_requests/show/_participants.html.haml7
-rw-r--r--app/views/projects/merge_requests/show/_remove_source_branch.html.haml4
-rw-r--r--app/views/projects/merge_requests/show/_state_widget.html.haml13
-rw-r--r--app/views/projects/merge_requests/update.js.haml8
-rw-r--r--app/views/projects/milestones/_form.html.haml21
-rw-r--r--app/views/projects/milestones/_issue.html.haml6
-rw-r--r--app/views/projects/milestones/_merge_request.html.haml6
-rw-r--r--app/views/projects/milestones/_milestone.html.haml10
-rw-r--r--app/views/projects/milestones/index.html.haml44
-rw-r--r--app/views/projects/milestones/show.html.haml69
-rw-r--r--app/views/projects/network/show.html.haml6
-rw-r--r--app/views/projects/new.html.haml87
-rw-r--r--app/views/projects/new_tree/show.html.haml43
-rw-r--r--app/views/projects/no_repo.html.haml22
-rw-r--r--app/views/projects/notes/_edit_form.html.haml14
-rw-r--r--app/views/projects/notes/_form.html.haml35
-rw-r--r--app/views/projects/notes/_note.html.haml68
-rw-r--r--app/views/projects/notes/_notes_with_form.html.haml2
-rw-r--r--app/views/projects/notes/discussions/_active.html.haml2
-rw-r--r--app/views/projects/notes/discussions/_commit.html.haml2
-rw-r--r--app/views/projects/notes/discussions/_diff.html.haml6
-rw-r--r--app/views/projects/protected_branches/_branches_list.html.haml34
-rw-r--r--app/views/projects/protected_branches/index.html.haml33
-rw-r--r--app/views/projects/refs/logs_tree.js.haml4
-rw-r--r--app/views/projects/repositories/_download_archive.html.haml14
-rw-r--r--app/views/projects/repositories/_feed.html.haml4
-rw-r--r--app/views/projects/services/_form.html.haml9
-rw-r--r--app/views/projects/services/index.html.haml27
-rw-r--r--app/views/projects/show.html.haml26
-rw-r--r--app/views/projects/snippets/edit.html.haml2
-rw-r--r--app/views/projects/snippets/index.html.haml2
-rw-r--r--app/views/projects/snippets/new.html.haml2
-rw-r--r--app/views/projects/snippets/show.html.haml10
-rw-r--r--app/views/projects/tags/_tag.html.haml6
-rw-r--r--app/views/projects/tags/index.html.haml2
-rw-r--r--app/views/projects/tags/new.html.haml5
-rw-r--r--app/views/projects/team_members/_form.html.haml11
-rw-r--r--app/views/projects/team_members/_team_member.html.haml6
-rw-r--r--app/views/projects/team_members/import.html.haml4
-rw-r--r--app/views/projects/team_members/index.html.haml4
-rw-r--r--app/views/projects/transfer.js.haml9
-rw-r--r--app/views/projects/tree/_blob_item.html.haml2
-rw-r--r--app/views/projects/tree/_submodule_item.html.haml10
-rw-r--r--app/views/projects/tree/_tree.html.haml12
-rw-r--r--app/views/projects/tree/_tree_commit_column.html.haml2
-rw-r--r--app/views/projects/tree/_tree_item.html.haml3
-rw-r--r--app/views/projects/update.js.haml2
-rw-r--r--app/views/projects/wikis/_form.html.haml21
-rw-r--r--app/views/projects/wikis/_main_links.html.haml4
-rw-r--r--app/views/projects/wikis/_nav.html.haml6
-rw-r--r--app/views/projects/wikis/_new.html.haml2
-rw-r--r--app/views/projects/wikis/edit.html.haml2
-rw-r--r--app/views/projects/wikis/history.html.haml9
-rw-r--r--app/views/projects/wikis/pages.html.haml2
-rw-r--r--app/views/projects/wikis/show.html.haml2
-rw-r--r--app/views/search/_project_filter.html.haml1
-rw-r--r--app/views/search/_results.html.haml2
-rw-r--r--app/views/search/results/_blob.html.haml4
-rw-r--r--app/views/search/results/_issue.html.haml2
-rw-r--r--app/views/search/results/_merge_request.html.haml2
-rw-r--r--app/views/search/results/_note.html.haml4
-rw-r--r--app/views/search/results/_project.html.haml2
-rw-r--r--app/views/search/results/_snippet_title.html.haml2
-rw-r--r--app/views/search/results/_wiki_blob.html.haml4
-rw-r--r--app/views/shared/_choose_group_avatar_button.html.haml2
-rw-r--r--app/views/shared/_clone_panel.html.haml16
-rw-r--r--app/views/shared/_event_filter.html.haml16
-rw-r--r--app/views/shared/_file_highlight.html.haml (renamed from app/views/shared/_file_hljs.html.haml)8
-rw-r--r--app/views/shared/_filter.html.haml50
-rw-r--r--app/views/shared/_group_form.html.haml25
-rw-r--r--app/views/shared/_issuable_filter.html.haml119
-rw-r--r--app/views/shared/_issues.html.haml2
-rw-r--r--app/views/shared/_merge_requests.html.haml2
-rw-r--r--app/views/shared/_milestones_filter.html.haml14
-rw-r--r--app/views/shared/_no_password.html.haml8
-rw-r--r--app/views/shared/_no_ssh.html.haml22
-rw-r--r--app/views/shared/_outdated_browser.html.haml8
-rw-r--r--app/views/shared/_project_filter.html.haml64
-rw-r--r--app/views/shared/_promo.html.haml2
-rw-r--r--app/views/shared/_ref_switcher.html.haml2
-rw-r--r--app/views/shared/_sort_dropdown.html.haml30
-rw-r--r--app/views/shared/snippets/_blob.html.haml2
-rw-r--r--app/views/shared/snippets/_form.html.haml2
-rw-r--r--app/views/snippets/_snippet.html.haml2
-rw-r--r--app/views/users/_groups.html.haml7
-rw-r--r--app/views/users/_projects.html.haml27
-rw-r--r--app/views/users/calendar.html.haml8
-rw-r--r--app/views/users/show.atom.builder12
-rw-r--r--app/views/users/show.html.haml26
-rw-r--r--app/workers/auto_merge_worker.rb13
-rw-r--r--app/workers/emails_on_push_worker.rb3
-rw-r--r--app/workers/project_service_worker.rb10
-rw-r--r--app/workers/project_web_hook_worker.rb3
-rw-r--r--app/workers/repository_import_worker.rb28
662 files changed, 10707 insertions, 6425 deletions
diff --git a/app/assets/images/authbuttons/bitbucket_32.png b/app/assets/images/authbuttons/bitbucket_32.png
new file mode 100644
index 00000000000..27702eb973d
--- /dev/null
+++ b/app/assets/images/authbuttons/bitbucket_32.png
Binary files differ
diff --git a/app/assets/images/authbuttons/bitbucket_64.png b/app/assets/images/authbuttons/bitbucket_64.png
new file mode 100644
index 00000000000..4b90a57bc7d
--- /dev/null
+++ b/app/assets/images/authbuttons/bitbucket_64.png
Binary files differ
diff --git a/app/assets/images/authbuttons/github_32.png b/app/assets/images/authbuttons/github_32.png
index c56eef05eb9..0445b567bbc 100644
--- a/app/assets/images/authbuttons/github_32.png
+++ b/app/assets/images/authbuttons/github_32.png
Binary files differ
diff --git a/app/assets/images/authbuttons/github_64.png b/app/assets/images/authbuttons/github_64.png
index 39de55bc796..dc7c03d1005 100644
--- a/app/assets/images/authbuttons/github_64.png
+++ b/app/assets/images/authbuttons/github_64.png
Binary files differ
diff --git a/app/assets/images/authbuttons/gitlab_32.png b/app/assets/images/authbuttons/gitlab_32.png
new file mode 100644
index 00000000000..f3b78cb6efb
--- /dev/null
+++ b/app/assets/images/authbuttons/gitlab_32.png
Binary files differ
diff --git a/app/assets/images/authbuttons/gitlab_64.png b/app/assets/images/authbuttons/gitlab_64.png
new file mode 100644
index 00000000000..ff2945fe89e
--- /dev/null
+++ b/app/assets/images/authbuttons/gitlab_64.png
Binary files differ
diff --git a/app/assets/images/authbuttons/google_32.png b/app/assets/images/authbuttons/google_32.png
index 6225cc9c2d7..b03c3ec5207 100644
--- a/app/assets/images/authbuttons/google_32.png
+++ b/app/assets/images/authbuttons/google_32.png
Binary files differ
diff --git a/app/assets/images/authbuttons/google_64.png b/app/assets/images/authbuttons/google_64.png
index 4d608f71008..94a0e089c6e 100644
--- a/app/assets/images/authbuttons/google_64.png
+++ b/app/assets/images/authbuttons/google_64.png
Binary files differ
diff --git a/app/assets/images/authbuttons/twitter_32.png b/app/assets/images/authbuttons/twitter_32.png
index 696eb02484d..a3d4964f40f 100644
--- a/app/assets/images/authbuttons/twitter_32.png
+++ b/app/assets/images/authbuttons/twitter_32.png
Binary files differ
diff --git a/app/assets/images/authbuttons/twitter_64.png b/app/assets/images/authbuttons/twitter_64.png
index 2893274766f..5c9f14cb077 100644
--- a/app/assets/images/authbuttons/twitter_64.png
+++ b/app/assets/images/authbuttons/twitter_64.png
Binary files differ
diff --git a/app/assets/images/bg-header.png b/app/assets/images/bg-header.png
index 9ecdaf4e2d5..639271c6faf 100644
--- a/app/assets/images/bg-header.png
+++ b/app/assets/images/bg-header.png
Binary files differ
diff --git a/app/assets/images/bg_fallback.png b/app/assets/images/bg_fallback.png
index d9066ad7d7b..e5fe659ba63 100644
--- a/app/assets/images/bg_fallback.png
+++ b/app/assets/images/bg_fallback.png
Binary files differ
diff --git a/app/assets/images/brand_logo.png b/app/assets/images/brand_logo.png
index 09b1689ca45..9c564bb6141 100644
--- a/app/assets/images/brand_logo.png
+++ b/app/assets/images/brand_logo.png
Binary files differ
diff --git a/app/assets/images/chosen-sprite.png b/app/assets/images/chosen-sprite.png
index d08e4b7e624..3d936b07d44 100644
--- a/app/assets/images/chosen-sprite.png
+++ b/app/assets/images/chosen-sprite.png
Binary files differ
diff --git a/app/assets/images/dark-scheme-preview.png b/app/assets/images/dark-scheme-preview.png
index 6dac6cd8ca1..2ef58e52549 100644
--- a/app/assets/images/dark-scheme-preview.png
+++ b/app/assets/images/dark-scheme-preview.png
Binary files differ
diff --git a/app/assets/images/diff_note_add.png b/app/assets/images/diff_note_add.png
index 8ec15b701fc..0084422e330 100644
--- a/app/assets/images/diff_note_add.png
+++ b/app/assets/images/diff_note_add.png
Binary files differ
diff --git a/app/assets/images/gitorious-logo-black.png b/app/assets/images/gitorious-logo-black.png
new file mode 100644
index 00000000000..78f17a9af79
--- /dev/null
+++ b/app/assets/images/gitorious-logo-black.png
Binary files differ
diff --git a/app/assets/images/gitorious-logo-blue.png b/app/assets/images/gitorious-logo-blue.png
new file mode 100644
index 00000000000..4962cffba31
--- /dev/null
+++ b/app/assets/images/gitorious-logo-blue.png
Binary files differ
diff --git a/app/assets/images/icon-link.png b/app/assets/images/icon-link.png
index 32ade0fe9a3..60021d5ac47 100644
--- a/app/assets/images/icon-link.png
+++ b/app/assets/images/icon-link.png
Binary files differ
diff --git a/app/assets/images/icon-search.png b/app/assets/images/icon-search.png
index 084b89e3a7c..3c1c146541d 100644
--- a/app/assets/images/icon-search.png
+++ b/app/assets/images/icon-search.png
Binary files differ
diff --git a/app/assets/images/icon_sprite.png b/app/assets/images/icon_sprite.png
index 9ad65fc443b..2e7a5023398 100644
--- a/app/assets/images/icon_sprite.png
+++ b/app/assets/images/icon_sprite.png
Binary files differ
diff --git a/app/assets/images/images.png b/app/assets/images/images.png
index da91f6b1f4c..ad146246caf 100644
--- a/app/assets/images/images.png
+++ b/app/assets/images/images.png
Binary files differ
diff --git a/app/assets/images/logo-black.png b/app/assets/images/logo-black.png
deleted file mode 100644
index 4a96572d570..00000000000
--- a/app/assets/images/logo-black.png
+++ /dev/null
Binary files differ
diff --git a/app/assets/images/logo-white.png b/app/assets/images/logo-white.png
index bc2ef601a53..917bcfcb7e7 100644
--- a/app/assets/images/logo-white.png
+++ b/app/assets/images/logo-white.png
Binary files differ
diff --git a/app/assets/images/monokai-scheme-preview.png b/app/assets/images/monokai-scheme-preview.png
index 3aeed886a02..fbb339c6a91 100644
--- a/app/assets/images/monokai-scheme-preview.png
+++ b/app/assets/images/monokai-scheme-preview.png
Binary files differ
diff --git a/app/assets/images/move.png b/app/assets/images/move.png
index 9d2d55ddf0b..6a0567f8f25 100644
--- a/app/assets/images/move.png
+++ b/app/assets/images/move.png
Binary files differ
diff --git a/app/assets/images/no_avatar.png b/app/assets/images/no_avatar.png
index dac3ab1bb89..8287acbce13 100644
--- a/app/assets/images/no_avatar.png
+++ b/app/assets/images/no_avatar.png
Binary files differ
diff --git a/app/assets/images/no_group_avatar.png b/app/assets/images/no_group_avatar.png
index a97d4515982..bfb31bb2184 100644
--- a/app/assets/images/no_group_avatar.png
+++ b/app/assets/images/no_group_avatar.png
Binary files differ
diff --git a/app/assets/images/slider_handles.png b/app/assets/images/slider_handles.png
index a6d477033fa..884378ec96a 100644
--- a/app/assets/images/slider_handles.png
+++ b/app/assets/images/slider_handles.png
Binary files differ
diff --git a/app/assets/images/solarized-dark-scheme-preview.png b/app/assets/images/solarized-dark-scheme-preview.png
index ae092ab5213..7ed7336896b 100644
--- a/app/assets/images/solarized-dark-scheme-preview.png
+++ b/app/assets/images/solarized-dark-scheme-preview.png
Binary files differ
diff --git a/app/assets/images/solarized-light-scheme-preview.png b/app/assets/images/solarized-light-scheme-preview.png
new file mode 100644
index 00000000000..c50db75449b
--- /dev/null
+++ b/app/assets/images/solarized-light-scheme-preview.png
Binary files differ
diff --git a/app/assets/images/switch_icon.png b/app/assets/images/switch_icon.png
index 6b8bde41bc9..c6b6c8d9521 100644
--- a/app/assets/images/switch_icon.png
+++ b/app/assets/images/switch_icon.png
Binary files differ
diff --git a/app/assets/images/trans_bg.gif b/app/assets/images/trans_bg.gif
index 5f6ed04a43c..1a1c9c15ec7 100644
--- a/app/assets/images/trans_bg.gif
+++ b/app/assets/images/trans_bg.gif
Binary files differ
diff --git a/app/assets/images/white-scheme-preview.png b/app/assets/images/white-scheme-preview.png
index d1866e00158..fc4c40b9227 100644
--- a/app/assets/images/white-scheme-preview.png
+++ b/app/assets/images/white-scheme-preview.png
Binary files differ
diff --git a/app/assets/javascripts/activities.js.coffee b/app/assets/javascripts/activities.js.coffee
index 4f76d8ce486..777c62dc1b7 100644
--- a/app/assets/javascripts/activities.js.coffee
+++ b/app/assets/javascripts/activities.js.coffee
@@ -12,7 +12,7 @@ class @Activities
toggleFilter: (sender) ->
- sender.parent().toggleClass "inactive"
+ sender.parent().toggleClass "active"
event_filters = $.cookie("event_filter")
filter = sender.attr("id").split("_")[0]
if event_filters
diff --git a/app/assets/javascripts/api.js.coffee b/app/assets/javascripts/api.js.coffee
index fafa5cdfaa4..27d04e7cac6 100644
--- a/app/assets/javascripts/api.js.coffee
+++ b/app/assets/javascripts/api.js.coffee
@@ -1,4 +1,6 @@
@Api =
+ groups_path: "/api/:version/groups.json"
+ group_path: "/api/:version/groups/:id.json"
users_path: "/api/:version/users.json"
user_path: "/api/:version/users/:id.json"
notes_path: "/api/:version/projects/:id/notes.json"
@@ -51,6 +53,33 @@
).done (users) ->
callback(users)
+ group: (group_id, callback) ->
+ url = Api.buildUrl(Api.group_path)
+ url = url.replace(':id', group_id)
+
+ $.ajax(
+ url: url
+ data:
+ private_token: gon.api_token
+ dataType: "json"
+ ).done (group) ->
+ callback(group)
+
+ # Return groups list. Filtered by query
+ # Only active groups retrieved
+ groups: (query, skip_ldap, callback) ->
+ url = Api.buildUrl(Api.groups_path)
+
+ $.ajax(
+ url: url
+ data:
+ private_token: gon.api_token
+ search: query
+ per_page: 20
+ dataType: "json"
+ ).done (groups) ->
+ callback(groups)
+
# Return project users list. Filtered by query
# Only active users retrieved
projectUsers: (project_id, query, callback) ->
diff --git a/app/assets/javascripts/application.js.coffee b/app/assets/javascripts/application.js.coffee
index e9a28c12159..c7acde2afe5 100644
--- a/app/assets/javascripts/application.js.coffee
+++ b/app/assets/javascripts/application.js.coffee
@@ -16,16 +16,16 @@
#= require jquery.scrollTo
#= require jquery.blockUI
#= require jquery.turbolinks
+#= require jquery.sticky-kit.min
#= require turbolinks
+#= require autosave
#= require bootstrap
-#= require password_strength
#= require select2
#= require raphael
#= require g.raphael-min
#= require g.bar-min
#= require chart-lib.min
#= require branch-graph
-#= require highlight.pack
#= require ace/ace
#= require ace/ext-searchbox
#= require d3
@@ -33,7 +33,6 @@
#= require nprogress
#= require nprogress-turbolinks
#= require dropzone
-#= require semantic-ui/sidebar
#= require mousetrap
#= require mousetrap/pause
#= require shortcuts
@@ -41,6 +40,7 @@
#= require shortcuts_dashboard_navigation
#= require shortcuts_issueable
#= require shortcuts_network
+#= require cal-heatmap
#= require_tree .
window.slugify = (text) ->
@@ -51,12 +51,6 @@ window.ajaxGet = (url) ->
window.showAndHide = (selector) ->
-window.errorMessage = (message) ->
- ehtml = $("<p>")
- ehtml.addClass("error_message")
- ehtml.html(message)
- ehtml
-
window.split = (val) ->
return val.split( /,\s*/ )
@@ -82,24 +76,18 @@ window.disableButtonIfEmptyField = (field_selector, button_selector) ->
# Disable button if any input field with given selector is empty
window.disableButtonIfAnyEmptyField = (form, form_selector, button_selector) ->
closest_submit = form.find(button_selector)
- empty = false
- form.find('input').filter(form_selector).each ->
- empty = true if rstrip($(this).val()) is ""
-
- if empty
- closest_submit.disable()
- else
- closest_submit.enable()
-
- form.keyup ->
- empty = false
+ updateButtons = ->
+ filled = true
form.find('input').filter(form_selector).each ->
- empty = true if rstrip($(this).val()) is ""
+ filled = rstrip($(this).val()) != "" || !$(this).attr('required')
- if empty
- closest_submit.disable()
- else
+ if filled
closest_submit.enable()
+ else
+ closest_submit.disable()
+
+ updateButtons()
+ form.keyup(updateButtons)
window.sanitize = (str) ->
return str.replace(/<(?:.|\n)*?>/gm, '')
@@ -115,8 +103,17 @@ window.unbindEvents = ->
$(document).unbind('scroll')
$(document).off('scroll')
+window.shiftWindow = ->
+ scrollBy 0, -50
+
document.addEventListener("page:fetch", unbindEvents)
+# Scroll the window to avoid the topnav bar
+# https://github.com/twitter/bootstrap/issues/1768
+if location.hash
+ setTimeout shiftWindow, 1
+window.addEventListener "hashchange", shiftWindow
+
$ ->
# Click a .one_click_select field, select the contents
$(".one_click_select").on 'click', -> $(@).select()
@@ -185,6 +182,8 @@ $ ->
form = btn.closest("form")
new ConfirmDangerModal(form, text)
+ new Aside()
+
(($) ->
# Disable an element and add the 'disabled' Bootstrap class
$.fn.extend disable: ->
diff --git a/app/assets/javascripts/aside.js.coffee b/app/assets/javascripts/aside.js.coffee
new file mode 100644
index 00000000000..85473101944
--- /dev/null
+++ b/app/assets/javascripts/aside.js.coffee
@@ -0,0 +1,17 @@
+class @Aside
+ constructor: ->
+ $(document).off "click", "a.show-aside"
+ $(document).on "click", 'a.show-aside', (e) ->
+ e.preventDefault()
+ btn = $(e.currentTarget)
+ icon = btn.find('i')
+ console.log('1')
+
+ if icon.hasClass('fa-angle-left')
+ btn.parent().find('section').hide()
+ btn.parent().find('aside').fadeIn()
+ icon.removeClass('fa-angle-left').addClass('fa-angle-right')
+ else
+ btn.parent().find('aside').hide()
+ btn.parent().find('section').fadeIn()
+ icon.removeClass('fa-angle-right').addClass('fa-angle-left')
diff --git a/app/assets/javascripts/autosave.js.coffee b/app/assets/javascripts/autosave.js.coffee
new file mode 100644
index 00000000000..3450f4b55f7
--- /dev/null
+++ b/app/assets/javascripts/autosave.js.coffee
@@ -0,0 +1,33 @@
+class @Autosave
+ constructor: (field, key) ->
+ @field = field
+
+ key = key.join("/") if key.join?
+ @key = "autosave/#{key}"
+
+ @field.data "autosave", this
+
+ @restore()
+
+ @field.on "input", => @save()
+
+ restore: ->
+ return unless window.localStorage?
+
+ text = window.localStorage.getItem @key
+ @field.val text if text?.length > 0
+ @field.trigger "input"
+
+ save: ->
+ return unless window.localStorage?
+
+ text = @field.val()
+ if text?.length > 0
+ window.localStorage.setItem @key, text
+ else
+ @reset()
+
+ reset: ->
+ return unless window.localStorage?
+
+ window.localStorage.removeItem @key \ No newline at end of file
diff --git a/app/assets/javascripts/blob.js.coffee b/app/assets/javascripts/blob/blob.js.coffee
index a5f15f80c5c..a5f15f80c5c 100644
--- a/app/assets/javascripts/blob.js.coffee
+++ b/app/assets/javascripts/blob/blob.js.coffee
diff --git a/app/assets/javascripts/blob/edit_blob.js.coffee b/app/assets/javascripts/blob/edit_blob.js.coffee
new file mode 100644
index 00000000000..6914ca759f6
--- /dev/null
+++ b/app/assets/javascripts/blob/edit_blob.js.coffee
@@ -0,0 +1,44 @@
+class @EditBlob
+ constructor: (assets_path, mode)->
+ ace.config.set "modePath", assets_path + '/ace'
+ ace.config.loadModule "ace/ext/searchbox"
+ if mode
+ ace_mode = mode
+ editor = ace.edit("editor")
+ editor.focus()
+ @editor = editor
+
+ if ace_mode
+ editor.getSession().setMode "ace/mode/" + ace_mode
+
+ disableButtonIfEmptyField "#commit_message", ".js-commit-button"
+ $(".js-commit-button").click ->
+ $("#file-content").val editor.getValue()
+ $(".file-editor form").submit()
+ return
+
+ editModePanes = $(".js-edit-mode-pane")
+ editModeLinks = $(".js-edit-mode a")
+ editModeLinks.click (event) ->
+ event.preventDefault()
+ currentLink = $(this)
+ paneId = currentLink.attr("href")
+ currentPane = editModePanes.filter(paneId)
+ editModeLinks.parent().removeClass "active hover"
+ currentLink.parent().addClass "active hover"
+ editModePanes.hide()
+ if paneId is "#preview"
+ currentPane.fadeIn 200
+ $.post currentLink.data("preview-url"),
+ content: editor.getValue()
+ , (response) ->
+ currentPane.empty().append response
+ return
+
+ else
+ currentPane.fadeIn 200
+ editor.focus()
+ return
+
+ editor: ->
+ return @editor
diff --git a/app/assets/javascripts/blob/new_blob.js.coffee b/app/assets/javascripts/blob/new_blob.js.coffee
new file mode 100644
index 00000000000..a6e27116b40
--- /dev/null
+++ b/app/assets/javascripts/blob/new_blob.js.coffee
@@ -0,0 +1,21 @@
+class @NewBlob
+ constructor: (assets_path, mode)->
+ ace.config.set "modePath", assets_path + '/ace'
+ ace.config.loadModule "ace/ext/searchbox"
+ if mode
+ ace_mode = mode
+ editor = ace.edit("editor")
+ editor.focus()
+ @editor = editor
+
+ if ace_mode
+ editor.getSession().setMode "ace/mode/" + ace_mode
+
+ disableButtonIfEmptyField "#commit_message", ".js-commit-button"
+ $(".js-commit-button").click ->
+ $("#file-content").val editor.getValue()
+ $(".file-editor form").submit()
+ return
+
+ editor: ->
+ return @editor
diff --git a/app/assets/javascripts/calendar.js.coffee b/app/assets/javascripts/calendar.js.coffee
new file mode 100644
index 00000000000..19ea4ccc4cf
--- /dev/null
+++ b/app/assets/javascripts/calendar.js.coffee
@@ -0,0 +1,30 @@
+class @calendar
+ options =
+ month: "short"
+ day: "numeric"
+ year: "numeric"
+
+ constructor: (timestamps, starting_year, starting_month) ->
+ cal = new CalHeatMap()
+ cal.init
+ itemName: ["commit"]
+ data: timestamps
+ start: new Date(starting_year, starting_month)
+ domainLabelFormat: "%b"
+ id: "cal-heatmap"
+ domain: "month"
+ subDomain: "day"
+ range: 12
+ tooltip: true
+ label:
+ position: "top"
+ legend: [
+ 0
+ 1
+ 4
+ 7
+ ]
+ legendCellPadding: 3
+ onClick: (date, count) ->
+ return
+ return
diff --git a/app/assets/javascripts/diff.js.coffee b/app/assets/javascripts/diff.js.coffee
index 52b4208524f..05f5af42571 100644
--- a/app/assets/javascripts/diff.js.coffee
+++ b/app/assets/javascripts/diff.js.coffee
@@ -1,6 +1,7 @@
class @Diff
UNFOLD_COUNT = 20
constructor: ->
+ $(document).off('click', '.js-unfold')
$(document).on('click', '.js-unfold', (event) =>
target = $(event.target)
unfoldBottom = target.hasClass('js-unfold-bottom')
@@ -36,6 +37,8 @@ class @Diff
)
)
+ $('.diff-header').stick_in_parent(recalc_every: 1, offset_top: $('.navbar').height())
+
lineNumbers: (line) ->
return ([0, 0]) unless line.children().length
lines = line.children().slice(0, 2)
diff --git a/app/assets/javascripts/dispatcher.js.coffee b/app/assets/javascripts/dispatcher.js.coffee
index ec4b7ea42cf..ed1bdd6ca33 100644
--- a/app/assets/javascripts/dispatcher.js.coffee
+++ b/app/assets/javascripts/dispatcher.js.coffee
@@ -4,7 +4,6 @@ $ ->
class Dispatcher
constructor: () ->
@initSearch()
- @initHighlight()
@initPageScripts()
initPageScripts: ->
@@ -27,23 +26,26 @@ class Dispatcher
new ZenMode()
when 'projects:milestones:show'
new Milestone()
- when 'projects:milestones:new'
+ when 'projects:milestones:new', 'projects:milestones:edit'
new ZenMode()
when 'projects:issues:new','projects:issues:edit'
GitLab.GfmAutoComplete.setup()
shortcut_handler = new ShortcutsNavigation()
new ZenMode()
+ new DropzoneInput($('.issue-form'))
when 'projects:merge_requests:new', 'projects:merge_requests:edit'
GitLab.GfmAutoComplete.setup()
new Diff()
shortcut_handler = new ShortcutsNavigation()
new ZenMode()
+ new DropzoneInput($('.merge-request-form'))
when 'projects:merge_requests:show'
new Diff()
shortcut_handler = new ShortcutsIssueable()
new ZenMode()
when "projects:merge_requests:diffs"
new Diff()
+ new ZenMode()
when 'projects:merge_requests:index'
shortcut_handler = new ShortcutsNavigation()
when 'dashboard:show'
@@ -52,14 +54,13 @@ class Dispatcher
when 'projects:commit:show'
new Commit()
new Diff()
+ new ZenMode()
shortcut_handler = new ShortcutsNavigation()
when 'projects:commits:show'
shortcut_handler = new ShortcutsNavigation()
when 'groups:show', 'projects:show'
new Activities()
shortcut_handler = new ShortcutsNavigation()
- when 'projects:teams:members:index'
- new TeamMembers()
when 'groups:members'
new GroupMembers()
new UsersSelect()
@@ -77,6 +78,8 @@ class Dispatcher
# Ensure we don't create a particular shortcut handler here. This is
# already created, where the network graph is created.
shortcut_handler = true
+ when 'projects:forks:new'
+ new ProjectFork()
when 'users:show'
new User()
@@ -94,6 +97,7 @@ class Dispatcher
new Profile()
when 'projects'
new Project()
+ new ProjectAvatar()
switch path[1]
when 'edit'
shortcut_handler = new ShortcutsNavigation()
@@ -108,6 +112,7 @@ class Dispatcher
new Wikis()
shortcut_handler = new ShortcutsNavigation()
new ZenMode()
+ new DropzoneInput($('.wiki-form'))
when 'snippets', 'labels', 'graphs'
shortcut_handler = new ShortcutsNavigation()
when 'team_members', 'deploy_keys', 'hooks', 'services', 'protected_branches'
@@ -126,10 +131,3 @@ class Dispatcher
project_ref = opts.data('autocomplete-project-ref')
new SearchAutocomplete(path, project_id, project_ref)
-
- initHighlight: ->
- $('.highlight pre code').each (i, e) ->
- $(e).html($.map($(e).html().split("\n"), (line, i) ->
- "<span class='line' id='LC" + (i + 1) + "'>" + line + "</span>"
- ).join("\n"))
- hljs.highlightBlock(e)
diff --git a/app/assets/javascripts/dropzone_input.js.coffee b/app/assets/javascripts/dropzone_input.js.coffee
new file mode 100644
index 00000000000..06e9f0001ae
--- /dev/null
+++ b/app/assets/javascripts/dropzone_input.js.coffee
@@ -0,0 +1,240 @@
+class @DropzoneInput
+ constructor: (form) ->
+ Dropzone.autoDiscover = false
+ alertClass = "alert alert-danger alert-dismissable div-dropzone-alert"
+ alertAttr = "class=\"close\" data-dismiss=\"alert\"" + "aria-hidden=\"true\""
+ divHover = "<div class=\"div-dropzone-hover\"></div>"
+ divSpinner = "<div class=\"div-dropzone-spinner\"></div>"
+ divAlert = "<div class=\"" + alertClass + "\"></div>"
+ iconPaperclip = "<i class=\"fa fa-paperclip div-dropzone-icon\"></i>"
+ iconSpinner = "<i class=\"fa fa-spinner fa-spin div-dropzone-icon\"></i>"
+ btnAlert = "<button type=\"button\"" + alertAttr + ">&times;</button>"
+ project_uploads_path = window.project_uploads_path or null
+
+ form_textarea = $(form).find("textarea.markdown-area")
+ form_textarea.wrap "<div class=\"div-dropzone\"></div>"
+ form_textarea.bind 'paste', (event) =>
+ handlePaste(event)
+
+ form_dropzone = $(form).find('.div-dropzone')
+ form_dropzone.parent().addClass "div-dropzone-wrapper"
+ form_dropzone.append divHover
+ $(".div-dropzone-hover").append iconPaperclip
+ form_dropzone.append divSpinner
+ $(".div-dropzone-spinner").append iconSpinner
+ $(".div-dropzone-spinner").css
+ "opacity": 0
+ "display": "none"
+
+ # Preview button
+ $(document).off "click", ".js-md-preview-button"
+ $(document).on "click", ".js-md-preview-button", (e) ->
+ ###
+ Shows the Markdown preview.
+
+ Lets the server render GFM into Html and displays it.
+ ###
+ e.preventDefault()
+ form = $(this).closest("form")
+ # toggle tabs
+ form.find(".js-md-write-button").parent().removeClass "active"
+ form.find(".js-md-preview-button").parent().addClass "active"
+
+ # toggle content
+ form.find(".md-write-holder").hide()
+ form.find(".md-preview-holder").show()
+
+ preview = form.find(".js-md-preview")
+ mdText = form.find(".markdown-area").val()
+ if mdText.trim().length is 0
+ preview.text "Nothing to preview."
+ else
+ preview.text "Loading..."
+ $.post($(this).data("url"),
+ md_text: mdText
+ ).success (previewData) ->
+ preview.html previewData
+
+ # Write button
+ $(document).off "click", ".js-md-write-button"
+ $(document).on "click", ".js-md-write-button", (e) ->
+ ###
+ Shows the Markdown textarea.
+ ###
+ e.preventDefault()
+ form = $(this).closest("form")
+ # toggle tabs
+ form.find(".js-md-write-button").parent().addClass "active"
+ form.find(".js-md-preview-button").parent().removeClass "active"
+
+ # toggle content
+ form.find(".md-write-holder").show()
+ form.find(".md-preview-holder").hide()
+
+ dropzone = form_dropzone.dropzone(
+ url: project_uploads_path
+ dictDefaultMessage: ""
+ clickable: true
+ paramName: "file"
+ maxFilesize: 10
+ uploadMultiple: false
+ headers:
+ "X-CSRF-Token": $("meta[name=\"csrf-token\"]").attr("content")
+
+ previewContainer: false
+
+ processing: ->
+ $(".div-dropzone-alert").alert "close"
+
+ dragover: ->
+ form_textarea.addClass "div-dropzone-focus"
+ form.find(".div-dropzone-hover").css "opacity", 0.7
+ return
+
+ dragleave: ->
+ form_textarea.removeClass "div-dropzone-focus"
+ form.find(".div-dropzone-hover").css "opacity", 0
+ return
+
+ drop: ->
+ form_textarea.removeClass "div-dropzone-focus"
+ form.find(".div-dropzone-hover").css "opacity", 0
+ form_textarea.focus()
+ return
+
+ success: (header, response) ->
+ child = $(dropzone[0]).children("textarea")
+ $(child).val $(child).val() + formatLink(response.link) + "\n"
+ return
+
+ error: (temp, errorMessage) ->
+ checkIfMsgExists = $(".error-alert").children().length
+ if checkIfMsgExists is 0
+ $(".error-alert").append divAlert
+ $(".div-dropzone-alert").append btnAlert + errorMessage
+ return
+
+ sending: ->
+ form_dropzone.find(".div-dropzone-spinner").css
+ "opacity": 0.7
+ "display": "inherit"
+ return
+
+ complete: ->
+ $(".dz-preview").remove()
+ $(".markdown-area").trigger "input"
+ $(".div-dropzone-spinner").css
+ "opacity": 0
+ "display": "none"
+ return
+ )
+
+ child = $(dropzone[0]).children("textarea")
+
+ formatLink = (link) ->
+ text = "[#{link.alt}](#{link.url})"
+ text = "!#{text}" if link.is_image
+ text
+
+ handlePaste = (event) ->
+ pasteEvent = event.originalEvent
+ if pasteEvent.clipboardData and pasteEvent.clipboardData.items
+ image = isImage(pasteEvent)
+ if image
+ event.preventDefault()
+
+ filename = getFilename(pasteEvent) or "image.png"
+ text = "{{" + filename + "}}"
+ pasteText(text)
+ uploadFile image.getAsFile(), filename
+
+ isImage = (data) ->
+ i = 0
+ while i < data.clipboardData.items.length
+ item = data.clipboardData.items[i]
+ if item.type.indexOf("image") isnt -1
+ return item
+ i++
+ return false
+
+ pasteText = (text) ->
+ caretStart = $(child)[0].selectionStart
+ caretEnd = $(child)[0].selectionEnd
+ textEnd = $(child).val().length
+
+ beforeSelection = $(child).val().substring 0, caretStart
+ afterSelection = $(child).val().substring caretEnd, textEnd
+ $(child).val beforeSelection + text + afterSelection
+ form_textarea.trigger "input"
+
+ getFilename = (e) ->
+ if window.clipboardData and window.clipboardData.getData
+ value = window.clipboardData.getData("Text")
+ else if e.clipboardData and e.clipboardData.getData
+ value = e.clipboardData.getData("text/plain")
+
+ value = value.split("\r")
+ value.first()
+
+ uploadFile = (item, filename) ->
+ formData = new FormData()
+ formData.append "file", item, filename
+ $.ajax
+ url: project_uploads_path
+ type: "POST"
+ data: formData
+ dataType: "json"
+ processData: false
+ contentType: false
+ headers:
+ "X-CSRF-Token": $("meta[name=\"csrf-token\"]").attr("content")
+
+ beforeSend: ->
+ showSpinner()
+ closeAlertMessage()
+
+ success: (e, textStatus, response) ->
+ insertToTextArea(filename, formatLink(response.responseJSON.link))
+
+ error: (response) ->
+ showError(response.responseJSON.message)
+
+ complete: ->
+ closeSpinner()
+
+ insertToTextArea = (filename, url) ->
+ $(child).val (index, val) ->
+ val.replace("{{" + filename + "}}", url + "\n")
+
+ appendToTextArea = (url) ->
+ $(child).val (index, val) ->
+ val + url + "\n"
+
+ showSpinner = (e) ->
+ form.find(".div-dropzone-spinner").css
+ "opacity": 0.7
+ "display": "inherit"
+
+ closeSpinner = ->
+ form.find(".div-dropzone-spinner").css
+ "opacity": 0
+ "display": "none"
+
+ showError = (message) ->
+ checkIfMsgExists = $(".error-alert").children().length
+ if checkIfMsgExists is 0
+ $(".error-alert").append divAlert
+ $(".div-dropzone-alert").append btnAlert + message
+
+ closeAlertMessage = ->
+ form.find(".div-dropzone-alert").alert "close"
+
+ form.find(".markdown-selector").click (e) ->
+ e.preventDefault()
+ $(@).closest('.gfm-form').find('.div-dropzone').click()
+ return
+
+ formatLink: (link) ->
+ text = "[#{link.alt}](#{link.url})"
+ text = "!#{text}" if link.is_image
+ text \ No newline at end of file
diff --git a/app/assets/javascripts/groups_select.js.coffee b/app/assets/javascripts/groups_select.js.coffee
new file mode 100644
index 00000000000..1084e2a17d1
--- /dev/null
+++ b/app/assets/javascripts/groups_select.js.coffee
@@ -0,0 +1,41 @@
+class @GroupsSelect
+ constructor: ->
+ $('.ajax-groups-select').each (i, select) =>
+ skip_ldap = $(select).hasClass('skip_ldap')
+
+ $(select).select2
+ placeholder: "Search for a group"
+ multiple: $(select).hasClass('multiselect')
+ minimumInputLength: 0
+ query: (query) ->
+ Api.groups query.term, skip_ldap, (groups) ->
+ data = { results: groups }
+ query.callback(data)
+
+ initSelection: (element, callback) ->
+ id = $(element).val()
+ if id isnt ""
+ Api.group(id, callback)
+
+
+ formatResult: (args...) =>
+ @formatResult(args...)
+ formatSelection: (args...) =>
+ @formatSelection(args...)
+ dropdownCssClass: "ajax-groups-dropdown"
+ escapeMarkup: (m) -> # we do not want to escape markup since we are displaying html in results
+ m
+
+ formatResult: (group) ->
+ if group.avatar_url
+ avatar = group.avatar_url
+ else
+ avatar = gon.default_avatar_url
+
+ "<div class='group-result'>
+ <div class='group-name'>#{group.name}</div>
+ <div class='group-path'>#{group.path}</div>
+ </div>"
+
+ formatSelection: (group) ->
+ group.name
diff --git a/app/assets/javascripts/importer_status.js.coffee b/app/assets/javascripts/importer_status.js.coffee
new file mode 100644
index 00000000000..e0e7771ab20
--- /dev/null
+++ b/app/assets/javascripts/importer_status.js.coffee
@@ -0,0 +1,35 @@
+class @ImporterStatus
+ constructor: (@jobs_url, @import_url) ->
+ this.initStatusPage()
+ this.setAutoUpdate()
+
+ initStatusPage: ->
+ $(".js-add-to-import").click (event) =>
+ new_namespace = null
+ tr = $(event.currentTarget).closest("tr")
+ id = tr.attr("id").replace("repo_", "")
+ if tr.find(".import-target input").length > 0
+ new_namespace = tr.find(".import-target input").prop("value")
+ tr.find(".import-target").empty().append(new_namespace + "/" + tr.find(".import-target").data("project_name"))
+ $.post @import_url, {repo_id: id, new_namespace: new_namespace}, dataType: 'script'
+
+ $(".js-import-all").click (event) =>
+ $(".js-add-to-import").each ->
+ $(this).click()
+
+ setAutoUpdate: ->
+ setInterval (=>
+ $.get @jobs_url, (data) =>
+ $.each data, (i, job) =>
+ job_item = $("#project_" + job.id)
+ status_field = job_item.find(".job-status")
+
+ if job.import_status == 'finished'
+ job_item.removeClass("active").addClass("success")
+ status_field.html('<span class="cgreen"><i class="fa fa-check"></i> done</span>')
+ else if job.import_status == 'started'
+ status_field.html("<i class='fa fa-spinner fa-spin'></i> started")
+ else
+ status_field.html(job.import_status)
+
+ ), 4000 \ No newline at end of file
diff --git a/app/assets/javascripts/issue.js.coffee b/app/assets/javascripts/issue.js.coffee
index 597b4695a6d..f2753170478 100644
--- a/app/assets/javascripts/issue.js.coffee
+++ b/app/assets/javascripts/issue.js.coffee
@@ -1,9 +1,9 @@
class @Issue
constructor: ->
$('.edit-issue.inline-update input[type="submit"]').hide()
- $(".issue-box .inline-update").on "change", "select", ->
+ $(".context .inline-update").on "change", "select", ->
$(this).submit()
- $(".issue-box .inline-update").on "change", "#issue_assignee_id", ->
+ $(".context .inline-update").on "change", "#issue_assignee_id", ->
$(this).submit()
if $("a.btn-close").length
@@ -15,3 +15,10 @@ class @Issue
"issue"
updateTaskState
)
+
+ $('.issue-details').waitForImages ->
+ $('.issuable-affix').affix offset:
+ top: ->
+ @top = $('.issue-details').outerHeight(true) + 25
+ bottom: ->
+ @bottom = $('.footer').outerHeight(true)
diff --git a/app/assets/javascripts/issues.js.coffee b/app/assets/javascripts/issues.js.coffee
index 2499ad5ad80..6513f4bcefc 100644
--- a/app/assets/javascripts/issues.js.coffee
+++ b/app/assets/javascripts/issues.js.coffee
@@ -15,7 +15,7 @@
$(this).html totalIssues + 1
else
$(this).html totalIssues - 1
- $("body").on "click", ".issues-filters .dropdown-menu a", ->
+ $("body").on "click", ".issues-other-filters .dropdown-menu a", ->
$('.issues-list').block(
message: null,
overlayCSS:
@@ -77,9 +77,9 @@
ids.push $(value).attr("data-id")
$("#update_issues_ids").val ids
- $(".issues-filters").hide()
+ $(".issues-other-filters").hide()
$(".issues_bulk_update").show()
else
$("#update_issues_ids").val []
$(".issues_bulk_update").hide()
- $(".issues-filters").show()
+ $(".issues-other-filters").show()
diff --git a/app/assets/javascripts/markdown_area.js.coffee b/app/assets/javascripts/markdown_area.js.coffee
deleted file mode 100644
index a0ebfc98ce6..00000000000
--- a/app/assets/javascripts/markdown_area.js.coffee
+++ /dev/null
@@ -1,196 +0,0 @@
-formatLink = (str) ->
- "![" + str.alt + "](" + str.url + ")"
-
-$(document).ready ->
- alertClass = "alert alert-danger alert-dismissable div-dropzone-alert"
- alertAttr = "class=\"close\" data-dismiss=\"alert\"" + "aria-hidden=\"true\""
- divHover = "<div class=\"div-dropzone-hover\"></div>"
- divSpinner = "<div class=\"div-dropzone-spinner\"></div>"
- divAlert = "<div class=\"" + alertClass + "\"></div>"
- iconPicture = "<i class=\"fa fa-picture-o div-dropzone-icon\"></i>"
- iconSpinner = "<i class=\"fa fa-spinner fa-spin div-dropzone-icon\"></i>"
- btnAlert = "<button type=\"button\"" + alertAttr + ">&times;</button>"
- project_image_path_upload = window.project_image_path_upload or null
-
- $("textarea.markdown-area").wrap "<div class=\"div-dropzone\"></div>"
-
- $(".div-dropzone").parent().addClass "div-dropzone-wrapper"
-
- $(".div-dropzone").append divHover
- $(".div-dropzone-hover").append iconPicture
- $(".div-dropzone").append divSpinner
- $(".div-dropzone-spinner").append iconSpinner
- $(".div-dropzone-spinner").css
- "opacity": 0
- "display": "none"
-
- dropzone = $(".div-dropzone").dropzone(
- url: project_image_path_upload
- dictDefaultMessage: ""
- clickable: true
- paramName: "markdown_img"
- maxFilesize: 10
- uploadMultiple: false
- acceptedFiles: "image/jpg,image/jpeg,image/gif,image/png"
- headers:
- "X-CSRF-Token": $("meta[name=\"csrf-token\"]").attr("content")
-
- previewContainer: false
-
- processing: ->
- $(".div-dropzone-alert").alert "close"
-
- dragover: ->
- $(".div-dropzone > textarea").addClass "div-dropzone-focus"
- $(".div-dropzone-hover").css "opacity", 0.7
- return
-
- dragleave: ->
- $(".div-dropzone > textarea").removeClass "div-dropzone-focus"
- $(".div-dropzone-hover").css "opacity", 0
- return
-
- drop: ->
- $(".div-dropzone > textarea").removeClass "div-dropzone-focus"
- $(".div-dropzone-hover").css "opacity", 0
- $(".div-dropzone > textarea").focus()
- return
-
- success: (header, response) ->
- child = $(dropzone[0]).children("textarea")
- $(child).val $(child).val() + formatLink(response.link) + "\n"
- return
-
- error: (temp, errorMessage) ->
- checkIfMsgExists = $(".error-alert").children().length
- if checkIfMsgExists is 0
- $(".error-alert").append divAlert
- $(".div-dropzone-alert").append btnAlert + errorMessage
- return
-
- sending: ->
- $(".div-dropzone-spinner").css
- "opacity": 0.7
- "display": "inherit"
- return
-
- complete: ->
- $(".dz-preview").remove()
- $(".markdown-area").trigger "input"
- $(".div-dropzone-spinner").css
- "opacity": 0
- "display": "none"
- return
- )
-
- child = $(dropzone[0]).children("textarea")
-
- formatLink = (str) ->
- "![" + str.alt + "](" + str.url + ")"
-
- handlePaste = (e) ->
- e.preventDefault()
- my_event = e.originalEvent
-
- if my_event.clipboardData and my_event.clipboardData.items
- processItem(my_event)
-
- processItem = (e) ->
- image = isImage(e)
- if image
- filename = getFilename(e) or "image.png"
- text = "{{" + filename + "}}"
- pasteText(text)
- uploadFile image.getAsFile(), filename
-
- else
- text = e.clipboardData.getData("text/plain")
- pasteText(text)
-
- isImage = (data) ->
- i = 0
- while i < data.clipboardData.items.length
- item = data.clipboardData.items[i]
- if item.type.indexOf("image") isnt -1
- return item
- i++
- return false
-
- pasteText = (text) ->
- caretStart = $(child)[0].selectionStart
- caretEnd = $(child)[0].selectionEnd
- textEnd = $(child).val().length
-
- beforeSelection = $(child).val().substring 0, caretStart
- afterSelection = $(child).val().substring caretEnd, textEnd
- $(child).val beforeSelection + text + afterSelection
- $(".markdown-area").trigger "input"
-
- getFilename = (e) ->
- if window.clipboardData and window.clipboardData.getData
- value = window.clipboardData.getData("Text")
- else if e.clipboardData and e.clipboardData.getData
- value = e.clipboardData.getData("text/plain")
-
- value = value.split("\r")
- value.first()
-
- uploadFile = (item, filename) ->
- formData = new FormData()
- formData.append "markdown_img", item, filename
- $.ajax
- url: project_image_path_upload
- type: "POST"
- data: formData
- dataType: "json"
- processData: false
- contentType: false
- headers:
- "X-CSRF-Token": $("meta[name=\"csrf-token\"]").attr("content")
-
- beforeSend: ->
- showSpinner()
- closeAlertMessage()
-
- success: (e, textStatus, response) ->
- insertToTextArea(filename, formatLink(response.responseJSON.link))
-
- error: (response) ->
- showError(response.responseJSON.message)
-
- complete: ->
- closeSpinner()
-
- insertToTextArea = (filename, url) ->
- $(child).val (index, val) ->
- val.replace("{{" + filename + "}}", url + "\n")
-
- appendToTextArea = (url) ->
- $(child).val (index, val) ->
- val + url + "\n"
-
- showSpinner = (e) ->
- $(".div-dropzone-spinner").css
- "opacity": 0.7
- "display": "inherit"
-
- closeSpinner = ->
- $(".div-dropzone-spinner").css
- "opacity": 0
- "display": "none"
-
- showError = (message) ->
- checkIfMsgExists = $(".error-alert").children().length
- if checkIfMsgExists is 0
- $(".error-alert").append divAlert
- $(".div-dropzone-alert").append btnAlert + message
-
- closeAlertMessage = ->
- $(".div-dropzone-alert").alert "close"
-
- $(".markdown-selector").click (e) ->
- e.preventDefault()
- $(@).closest('.gfm-form').find('.div-dropzone').click()
- return
-
- return
diff --git a/app/assets/javascripts/merge_request.js.coffee b/app/assets/javascripts/merge_request.js.coffee
index 46e06424e5a..d19f3af8c34 100644
--- a/app/assets/javascripts/merge_request.js.coffee
+++ b/app/assets/javascripts/merge_request.js.coffee
@@ -20,15 +20,22 @@ class @MergeRequest
if $("a.btn-close").length
$("li.task-list-item input:checkbox").prop("disabled", false)
+ $('.merge-request-details').waitForImages ->
+ $('.issuable-affix').affix offset:
+ top: ->
+ @top = $('.merge-request-details').outerHeight(true) + 91
+ bottom: ->
+ @bottom = $('.footer').outerHeight(true)
+
# Local jQuery finder
$: (selector) ->
this.$el.find(selector)
initContextWidget: ->
$('.edit-merge_request.inline-update input[type="submit"]').hide()
- $(".issue-box .inline-update").on "change", "select", ->
+ $(".context .inline-update").on "change", "select", ->
$(this).submit()
- $(".issue-box .inline-update").on "change", "#merge_request_assignee_id", ->
+ $(".context .inline-update").on "change", "#merge_request_assignee_id", ->
$(this).submit()
initMergeWidget: ->
@@ -89,6 +96,10 @@ class @MergeRequest
this.$('.merge-request-tabs .diffs-tab').addClass 'active'
this.loadDiff() unless @diffs_loaded
this.$('.diffs').show()
+ $(".diff-header").trigger("sticky_kit:recalc")
+ when 'commits'
+ this.$('.merge-request-tabs .commits-tab').addClass 'active'
+ this.$('.commits').show()
else
this.$('.merge-request-tabs .notes-tab').addClass 'active'
this.$('.notes').show()
@@ -132,3 +143,16 @@ class @MergeRequest
this.$('.automerge_widget').hide()
this.$('.merge-in-progress').hide()
this.$('.automerge_widget.already_cannot_be_merged').show()
+
+ mergeInProgress: ->
+ $.ajax
+ type: 'GET'
+ url: $('.merge-request').data('url')
+ success: (data) =>
+ switch data.state
+ when 'merged'
+ location.reload()
+ else
+ setTimeout(merge_request.mergeInProgress, 3000)
+ dataType: 'json'
+
diff --git a/app/assets/javascripts/notes.js.coffee b/app/assets/javascripts/notes.js.coffee
index 978f83dd442..90e6fd6d154 100644
--- a/app/assets/javascripts/notes.js.coffee
+++ b/app/assets/javascripts/notes.js.coffee
@@ -36,18 +36,9 @@ class @Notes
# delete note attachment
$(document).on "click", ".js-note-attachment-delete", @removeAttachment
- # Preview button
- $(document).on "click", ".js-note-preview-button", @previewNote
-
- # Preview button
- $(document).on "click", ".js-note-write-button", @writeNote
-
# 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
-
# update the file name when an attachment is selected
$(document).on "change", ".js-note-attachment-input", @updateFormAttachment
@@ -64,8 +55,9 @@ class @Notes
$(document).on "visibilitychange", @visibilityChange
@notes_forms = '.js-main-target-form textarea, .js-discussion-note-form textarea'
- $(document).on('keypress', @notes_forms, (e)->
- if e.keyCode == 10 || (e.ctrlKey && e.keyCode == 13)
+ # Chrome doesn't fire keypress or keyup for Command+Enter, so we need keydown.
+ $(document).on('keydown', @notes_forms, (e) ->
+ if e.keyCode == 10 || ((e.metaKey || e.ctrlKey) && e.keyCode == 13)
$(@).parents('form').submit()
)
@@ -77,14 +69,11 @@ class @Notes
$(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 "click", ".js-note-write-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"
$(document).off "visibilitychange"
- $(document).off "keypress", @notes_forms
+ $(document).off "keydown", @notes_forms
$(document).off "keyup", ".js-note-text"
$(document).off "click", ".js-note-target-reopen"
$(document).off "click", ".js-note-target-close"
@@ -122,10 +111,6 @@ class @Notes
if @isNewNote(note)
@note_ids.push(note.id)
$('ul.main-notes-list').append(note.html)
- code = "#note_" + note.id + " .highlight pre code"
- $(code).each (i, e) ->
- hljs.highlightBlock(e)
-
###
Check if note does not exists on page
@@ -166,47 +151,6 @@ class @Notes
@removeDiscussionNoteForm(form)
###
- Shows write note textarea.
- ###
- writeNote: (e) ->
- e.preventDefault()
- form = $(this).closest("form")
- # toggle tabs
- form.find(".js-note-write-button").parent().addClass "active"
- form.find(".js-note-preview-button").parent().removeClass "active"
-
- # toggle content
- form.find(".note-write-holder").show()
- form.find(".note-preview-holder").hide()
-
- ###
- Shows the note preview.
-
- Lets the server render GFM into Html and displays it.
- ###
- previewNote: (e) ->
- e.preventDefault()
- form = $(this).closest("form")
- # toggle tabs
- form.find(".js-note-write-button").parent().removeClass "active"
- form.find(".js-note-preview-button").parent().addClass "active"
-
- # toggle content
- form.find(".note-write-holder").hide()
- form.find(".note-preview-holder").show()
-
- 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.
@@ -220,17 +164,10 @@ class @Notes
form.find(".js-errors").remove()
# reset text and preview
- form.find(".js-note-write-button").click()
+ form.find(".js-md-write-button").click()
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()
+ form.find(".js-note-text").data("autosave").reset()
###
Shows the main form and does some setup on it.
@@ -268,23 +205,34 @@ class @Notes
setupNoteForm: (form) ->
disableButtonIfEmptyField form.find(".js-note-text"), form.find(".js-comment-button")
form.removeClass "js-new-note-form"
+ form.find('.div-dropzone').remove()
# setup preview buttons
- form.find(".js-note-write-button, .js-note-preview-button").tooltip placement: "left"
- previewButton = form.find(".js-note-preview-button")
- form.find(".js-note-text").on "input", ->
+ form.find(".js-md-write-button, .js-md-preview-button").tooltip placement: "left"
+ previewButton = form.find(".js-md-preview-button")
+
+ textarea = form.find(".js-note-text")
+
+ textarea.on "input", ->
if $(this).val().trim() isnt ""
previewButton.removeClass("turn-off").addClass "turn-on"
else
previewButton.removeClass("turn-on").addClass "turn-off"
+ new Autosave textarea, [
+ "Note"
+ form.find("#note_commit_id").val()
+ form.find("#note_line_code").val()
+ form.find("#note_noteable_type").val()
+ form.find("#note_noteable_id").val()
+ ]
# 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()
+ new DropzoneInput(form)
form.show()
-
###
Called in response to the new note form being submitted
@@ -308,11 +256,10 @@ class @Notes
Updates the current note field.
###
updateNote: (xhr, note, status) =>
- note_li = $("#note_" + note.id)
+ note_li = $(".note-row-" + note.id)
note_li.replaceWith(note.html)
- code = "#note_" + note.id + " .highlight pre code"
- $(code).each (i, e) ->
- hljs.highlightBlock(e)
+ note_li.find('.note-edit-form').hide()
+ note_li.find('.note-body > .note-text').show()
###
Called in response to clicking the edit note link
@@ -324,12 +271,20 @@ class @Notes
showEditForm: (e) ->
e.preventDefault()
note = $(this).closest(".note")
- note.find(".note-text").hide()
+ note.find(".note-body > .note-text").hide()
+ note.find(".note-header").hide()
+ base_form = note.find(".note-edit-form")
+ form = base_form.clone().insertAfter(base_form)
+ form.addClass('current-note-edit-form')
+ form.find('.div-dropzone').remove()
# Show the attachment delete link
note.find(".js-note-attachment-delete").show()
+
+ # Setup markdown form
GitLab.GfmAutoComplete.setup()
- form = note.find(".note-edit-form")
+ new DropzoneInput(form)
+
form.show()
textarea = form.find("textarea")
textarea.focus()
@@ -343,9 +298,9 @@ class @Notes
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()
+ note.find(".note-body > .note-text").show()
+ note.find(".note-header").show()
+ note.find(".current-note-edit-form").remove()
###
Called in response to deleting a note of any kind.
@@ -377,7 +332,7 @@ class @Notes
removeAttachment: ->
note = $(this).closest(".note")
note.find(".note-attachment").remove()
- note.find(".note-text").show()
+ note.find(".note-body > .note-text").show()
note.find(".js-note-attachment-delete").hide()
note.find(".note-edit-form").hide()
@@ -424,7 +379,7 @@ class @Notes
###
addDiffNote: (e) =>
e.preventDefault()
- link = e.target
+ link = e.currentTarget
form = $(".js-new-note-form")
row = $(link).closest("tr")
nextRow = row.next()
@@ -451,6 +406,8 @@ class @Notes
removeDiscussionNoteForm: (form)->
row = form.closest("tr")
+ form.find(".js-note-text").data("autosave").reset()
+
# show the reply button (will only work for replies)
form.prev(".js-discussion-reply-button").show()
if row.is(".js-temp-notes-holder")
diff --git a/app/assets/javascripts/password_strength.js.coffee b/app/assets/javascripts/password_strength.js.coffee
deleted file mode 100644
index 825f5630266..00000000000
--- a/app/assets/javascripts/password_strength.js.coffee
+++ /dev/null
@@ -1,31 +0,0 @@
-#= require pwstrength-bootstrap-1.2.2
-overwritten_messages =
- wordSimilarToUsername: "Your password should not contain your username"
-
-overwritten_rules =
- wordSequences: false
-
-options =
- showProgressBar: false
- showVerdicts: false
- showPopover: true
- showErrors: true
- showStatus: true
- errorMessages: overwritten_messages
-
-$(document).ready ->
- profileOptions = {}
- profileOptions.ui = options
- profileOptions.rules =
- activated: overwritten_rules
-
- deviseOptions = {}
- deviseOptions.common =
- usernameField: "#user_username"
- deviseOptions.ui = options
- deviseOptions.rules =
- activated: overwritten_rules
-
- $("#user_password_profile").pwstrength profileOptions
- $("#user_password_sign_up").pwstrength deviseOptions
- $("#user_password_recover").pwstrength deviseOptions
diff --git a/app/assets/javascripts/project.js.coffee b/app/assets/javascripts/project.js.coffee
index 5a9cc66c8f0..eb8c1fa1426 100644
--- a/app/assets/javascripts/project.js.coffee
+++ b/app/assets/javascripts/project.js.coffee
@@ -16,5 +16,11 @@ class @Project
$('.hide-no-ssh-message').on 'click', (e) ->
path = '/'
$.cookie('hide_no_ssh_message', 'false', { path: path })
- $(@).parents('.no-ssh-key-message').hide()
+ $(@).parents('.no-ssh-key-message').remove()
+ e.preventDefault()
+
+ $('.hide-no-password-message').on 'click', (e) ->
+ path = '/'
+ $.cookie('hide_no_password_message', 'false', { path: path })
+ $(@).parents('.no-password-message').remove()
e.preventDefault()
diff --git a/app/assets/javascripts/project_avatar.js.coffee b/app/assets/javascripts/project_avatar.js.coffee
new file mode 100644
index 00000000000..8bec6e2ccca
--- /dev/null
+++ b/app/assets/javascripts/project_avatar.js.coffee
@@ -0,0 +1,9 @@
+class @ProjectAvatar
+ constructor: ->
+ $('.js-choose-project-avatar-button').bind 'click', ->
+ form = $(this).closest('form')
+ form.find('.js-project-avatar-input').click()
+ $('.js-project-avatar-input').bind 'change', ->
+ form = $(this).closest('form')
+ filename = $(this).val().replace(/^.*[\\\/]/, '')
+ form.find('.js-avatar-filename').text(filename)
diff --git a/app/assets/javascripts/project_fork.js.coffee b/app/assets/javascripts/project_fork.js.coffee
new file mode 100644
index 00000000000..e15a1c4ef76
--- /dev/null
+++ b/app/assets/javascripts/project_fork.js.coffee
@@ -0,0 +1,5 @@
+class @ProjectFork
+ constructor: ->
+ $('.fork-thumbnail a').on 'click', ->
+ $('.fork-namespaces').hide()
+ $('.save-project-loader').show()
diff --git a/app/assets/javascripts/project_new.js.coffee b/app/assets/javascripts/project_new.js.coffee
index f4a2ca813d2..836269c44f9 100644
--- a/app/assets/javascripts/project_new.js.coffee
+++ b/app/assets/javascripts/project_new.js.coffee
@@ -9,17 +9,3 @@ class @ProjectNew
initEvents: ->
disableButtonIfEmptyField '#project_name', '.project-submit'
-
- $('#project_issues_enabled').change ->
- if ($(this).is(':checked') == true)
- $('#project_issues_tracker').removeAttr('disabled')
- else
- $('#project_issues_tracker').attr('disabled', 'disabled')
-
- $('#project_issues_tracker').change()
-
- $('#project_issues_tracker').change ->
- if ($(this).val() == gon.default_issues_tracker || $(this).is(':disabled'))
- $('#project_issues_tracker_id').attr('disabled', 'disabled')
- else
- $('#project_issues_tracker_id').removeAttr('disabled')
diff --git a/app/assets/javascripts/project_show.js.coffee b/app/assets/javascripts/project_show.js.coffee
index 02a7d7b731d..d0eaaad92b8 100644
--- a/app/assets/javascripts/project_show.js.coffee
+++ b/app/assets/javascripts/project_show.js.coffee
@@ -6,7 +6,7 @@ class @ProjectShow
new Flash('Star toggle failed. Try again later.', 'alert')
$("a[data-toggle='tab']").on "shown.bs.tab", (e) ->
- $.cookie "default_view", $(e.target).attr("href")
+ $.cookie "default_view", $(e.target).attr("href"), { expires: 30 }
defaultView = $.cookie("default_view")
if defaultView
diff --git a/app/assets/javascripts/project_users_select.js.coffee b/app/assets/javascripts/project_users_select.js.coffee
index 7fb33926096..885f0d58a6a 100644
--- a/app/assets/javascripts/project_users_select.js.coffee
+++ b/app/assets/javascripts/project_users_select.js.coffee
@@ -15,7 +15,7 @@ class @ProjectUsersSelect
name: 'Unassigned',
avatar: null,
username: 'none',
- id: ''
+ id: -1
}
data.results.unshift(nullUser)
diff --git a/app/assets/javascripts/protected_branches.js.coffee b/app/assets/javascripts/protected_branches.js.coffee
new file mode 100644
index 00000000000..691fd4f10d8
--- /dev/null
+++ b/app/assets/javascripts/protected_branches.js.coffee
@@ -0,0 +1,21 @@
+$ ->
+ $(":checkbox").change ->
+ name = $(this).attr("name")
+ if name == "developers_can_push"
+ id = $(this).val()
+ checked = $(this).is(":checked")
+ url = $(this).data("url")
+ $.ajax
+ type: "PUT"
+ url: url
+ dataType: "json"
+ data:
+ id: id
+ developers_can_push: checked
+
+ success: ->
+ new Flash("Branch updated.", "notice")
+ location.reload true
+
+ error: ->
+ new Flash("Failed to update branch!", "alert")
diff --git a/app/assets/javascripts/sidebar.js.coffee b/app/assets/javascripts/sidebar.js.coffee
index c084d730d62..7febcba0e94 100644
--- a/app/assets/javascripts/sidebar.js.coffee
+++ b/app/assets/javascripts/sidebar.js.coffee
@@ -1,26 +1,14 @@
-responsive_resize = ->
- current_width = $(window).width()
- if current_width < 985
- $('.responsive-side').addClass("ui right wide sidebar")
+$(document).on("click", '.toggle-nav-collapse', (e) ->
+ e.preventDefault()
+ collapsed = 'page-sidebar-collapsed'
+ expanded = 'page-sidebar-expanded'
+
+ if $('.page-with-sidebar').hasClass(collapsed)
+ $('.page-with-sidebar').removeClass(collapsed).addClass(expanded)
+ $('.toggle-nav-collapse i').removeClass('fa-angle-right').addClass('fa-angle-left')
+ $.cookie("collapsed_nav", "false", { path: '/' })
else
- $('.responsive-side').removeClass("ui right wide sidebar")
-
-$ ->
- # Depending on window size, set the sidebar offscreen.
- responsive_resize()
-
- $('.sidebar-expand-button').click ->
- $('.ui.sidebar')
- .sidebar({overlay: true})
- .sidebar('toggle')
-
- # Hide sidebar on click outside of sidebar
- $(document).mouseup (e) ->
- container = $(".ui.sidebar")
- container.sidebar "hide" if not container.is(e.target) and container.has(e.target).length is 0
- return
-
-# On resize, check if sidebar should be offscreen.
-$(window).resize ->
- responsive_resize()
- return
+ $('.page-with-sidebar').removeClass(expanded).addClass(collapsed)
+ $('.toggle-nav-collapse i').removeClass('fa-angle-left').addClass('fa-angle-right')
+ $.cookie("collapsed_nav", "true", { path: '/' })
+)
diff --git a/app/assets/javascripts/stat_graph_contributors_graph.js.coffee b/app/assets/javascripts/stat_graph_contributors_graph.js.coffee
index 9952fa0b00a..8b82d20c6c2 100644
--- a/app/assets/javascripts/stat_graph_contributors_graph.js.coffee
+++ b/app/assets/javascripts/stat_graph_contributors_graph.js.coffee
@@ -46,7 +46,7 @@ class @ContributorsGraph
class @ContributorsMasterGraph extends ContributorsGraph
constructor: (@data) ->
- @width = $('.container').width() - 70
+ @width = $('.container').width() - 345
@height = 200
@x = null
@y = null
@@ -119,7 +119,7 @@ class @ContributorsMasterGraph extends ContributorsGraph
class @ContributorsAuthorGraph extends ContributorsGraph
constructor: (@data) ->
- @width = $('.container').width()/2 - 100
+ @width = $('.container').width()/2 - 225
@height = 200
@x = null
@y = null
diff --git a/app/assets/javascripts/team_members.js.coffee b/app/assets/javascripts/team_members.js.coffee
deleted file mode 100644
index 32486f7da54..00000000000
--- a/app/assets/javascripts/team_members.js.coffee
+++ /dev/null
@@ -1,4 +0,0 @@
-class @TeamMembers
- constructor: ->
- $('.team-members .project-access-select').on "change", ->
- $(this.form).submit()
diff --git a/app/assets/javascripts/zen_mode.js.coffee b/app/assets/javascripts/zen_mode.js.coffee
index 0c9942a4014..0fb8f7ed75f 100644
--- a/app/assets/javascripts/zen_mode.js.coffee
+++ b/app/assets/javascripts/zen_mode.js.coffee
@@ -10,7 +10,15 @@ class @ZenMode
if not @active_checkbox
@scroll_position = window.pageYOffset
- $('body').on 'change', '.zennable input[type=checkbox]', (e) =>
+ $('body').on 'click', '.zen-enter-link', (e) =>
+ e.preventDefault()
+ $(e.currentTarget).closest('.zennable').find('.zen-toggle-comment').prop('checked', true)
+
+ $('body').on 'click', '.zen-leave-link', (e) =>
+ e.preventDefault()
+ $(e.currentTarget).closest('.zennable').find('.zen-toggle-comment').prop('checked', false)
+
+ $('body').on 'change', '.zen-toggle-comment', (e) =>
checkbox = e.currentTarget
if checkbox.checked
# Disable other keyboard shortcuts in ZEN mode
@@ -32,8 +40,6 @@ class @ZenMode
@active_zen_area = @active_checkbox.parent().find('textarea')
@active_zen_area.focus()
window.location.hash = ZenMode.fullscreen_prefix + @active_checkbox.prop('id')
- # Disable dropzone in ZEN mode
- Dropzone.forElement('.div-dropzone').disable()
exitZenMode: =>
if @active_zen_area isnt null
diff --git a/app/assets/stylesheets/application.scss b/app/assets/stylesheets/application.scss
index 0d404f15055..e5bb5e21bb0 100644
--- a/app/assets/stylesheets/application.scss
+++ b/app/assets/stylesheets/application.scss
@@ -6,9 +6,9 @@
*= require jquery.ui.autocomplete
*= require jquery.atwho
*= require select2
- *= require highlightjs.min
*= require_self
*= require dropzone/basic
+ *= require cal-heatmap
*/
@import "main/*";
@@ -55,8 +55,3 @@
* Styles for JS behaviors.
*/
@import "behaviors.scss";
-
-/**
-* Styles for responsive sidebar
-*/
-@import "semantic-ui/modules/sidebar";
diff --git a/app/assets/stylesheets/behaviors.scss b/app/assets/stylesheets/behaviors.scss
index be4c4d07f1c..469f4f296ae 100644
--- a/app/assets/stylesheets/behaviors.scss
+++ b/app/assets/stylesheets/behaviors.scss
@@ -1,12 +1,22 @@
// Details
//--------
-.js-details-container .content { display: none; }
-.js-details-container .content.hide { display: block; }
-.js-details-container.open .content { display: block; }
-.js-details-container.open .content.hide { display: none; }
+.js-details-container {
+ .content {
+ display: none;
+ &.hide { display: block; }
+ }
+ &.open .content {
+ display: block;
+ &.hide { display: none; }
+ }
+}
// Toggle between two states.
-.js-toggler-container .turn-on { display: block; }
-.js-toggler-container .turn-off { display: none; }
-.js-toggler-container.on .turn-on { display: none; }
-.js-toggler-container.on .turn-off { display: block; }
+.js-toggler-container {
+ .turn-on { display: block; }
+ .turn-off { display: none; }
+ &.on {
+ .turn-on { display: none; }
+ .turn-off { display: block; }
+ }
+}
diff --git a/app/assets/stylesheets/generic/avatar.scss b/app/assets/stylesheets/generic/avatar.scss
index 4f038b977e2..8595887c3b9 100644
--- a/app/assets/stylesheets/generic/avatar.scss
+++ b/app/assets/stylesheets/generic/avatar.scss
@@ -2,15 +2,21 @@
float: left;
margin-right: 12px;
width: 40px;
- padding: 1px;
- @include border-radius(4px);
+ height: 40px;
+ padding: 0;
+ @include border-radius($avatar_radius);
&.avatar-inline {
float: none;
- margin-left: 3px;
+ margin-left: 4px;
+ margin-bottom: 2px;
- &.s16 { margin-right: 2px; }
- &.s24 { margin-right: 2px; }
+ &.s16 { margin-right: 4px; }
+ &.s24 { margin-right: 4px; }
+ }
+
+ &.group-avatar, &.project-avatar, &.avatar-tile {
+ @include border-radius(0px);
}
&.s16 { width: 16px; height: 16px; margin-right: 6px; }
@@ -21,3 +27,16 @@
&.s90 { width: 90px; height: 90px; margin-right: 15px; }
&.s160 { width: 160px; height: 160px; margin-right: 20px; }
}
+
+.identicon {
+ text-align: center;
+ vertical-align: top;
+
+ &.s16 { font-size: 12px; line-height: 1.33; }
+ &.s24 { font-size: 14px; line-height: 1.8; }
+ &.s26 { font-size: 20px; line-height: 1.33; }
+ &.s32 { font-size: 22px; line-height: 32px; }
+ &.s60 { font-size: 32px; line-height: 60px; }
+ &.s90 { font-size: 36px; line-height: 90px; }
+ &.s160 { font-size: 96px; line-height: 1.33; }
+}
diff --git a/app/assets/stylesheets/generic/buttons.scss b/app/assets/stylesheets/generic/buttons.scss
index d098f1ecaa2..3b360275065 100644
--- a/app/assets/stylesheets/generic/buttons.scss
+++ b/app/assets/stylesheets/generic/buttons.scss
@@ -173,6 +173,11 @@
margin-right: 0px;
}
}
+
+ &.btn-lg {
+ font-size: 15px;
+ line-height: 1.4;
+ }
}
.btn-block {
diff --git a/app/assets/stylesheets/generic/calendar.scss b/app/assets/stylesheets/generic/calendar.scss
new file mode 100644
index 00000000000..9483b26164e
--- /dev/null
+++ b/app/assets/stylesheets/generic/calendar.scss
@@ -0,0 +1,95 @@
+.calendar_onclick_placeholder {
+ padding: 0 0 2px 0;
+}
+
+.calendar_commit_activity {
+ padding: 5px 0 0;
+}
+
+.calendar_onclick_second {
+ font-size: 14px;
+ display: block;
+}
+
+.calendar_onclick_hr {
+ padding: 0;
+ margin: 10px 0;
+}
+
+.calendar_commit_date {
+ color: #999;
+}
+
+.calendar_activity_summary {
+ font-size: 14px;
+}
+
+/**
+* This overwrites the default values of the cal-heatmap gem
+*/
+.calendar {
+ .qi {
+ background-color: #999;
+ fill: #fff;
+ }
+
+ .q1 {
+ background-color: #dae289;
+ fill: #ededed;
+ }
+
+ .q2 {
+ background-color: #cedb9c;
+ fill: #ACD5F2;
+ }
+
+ .q3 {
+ background-color: #b5cf6b;
+ fill: #7FA8D1;
+ }
+
+ .q4 {
+ background-color: #637939;
+ fill: #49729B;
+ }
+
+ .q5 {
+ background-color: #3b6427;
+ fill: #254E77;
+ }
+
+ .domain-background {
+ fill: none;
+ shape-rendering: crispedges;
+ }
+
+ .ch-tooltip {
+ position: absolute;
+ display: none;
+ margin-top: 22px;
+ margin-left: 1px;
+ font-size: 13px;
+ padding: 3px;
+ font-weight: 550;
+ background-color: #222;
+ span {
+ position: absolute;
+ width: 200px;
+ text-align: center;
+ visibility: hidden;
+ border-radius: 10px;
+ &:after {
+ content: '';
+ position: absolute;
+ top: 100%;
+ left: 50%;
+ margin-left: -8px;
+ width: 0;
+ height: 0;
+ border-top: 8px solid #000000;
+ border-right: 8px solid transparent;
+ border-left: 8px solid transparent;
+ }
+ }
+ }
+}
diff --git a/app/assets/stylesheets/generic/common.scss b/app/assets/stylesheets/generic/common.scss
index cd2f4e45e3c..3db821fdf76 100644
--- a/app/assets/stylesheets/generic/common.scss
+++ b/app/assets/stylesheets/generic/common.scss
@@ -54,6 +54,11 @@ pre {
text-shadow: none;
}
+.dropdown-menu-align-right {
+ left: auto;
+ right: 0px;
+}
+
.dropdown-menu > li > a:hover,
.dropdown-menu > li > a:focus {
background: $bg_primary;
@@ -207,24 +212,16 @@ li.note {
}
}
-.no-ssh-key-message {
- padding: 10px 0;
- background: #C67;
- margin: 0;
- color: #FFF;
- margin-top: -1px;
+.browser-alert {
+ padding: 10px;
text-align: center;
-
+ background: #C67;
+ color: #fff;
+ font-weight: bold;
a {
color: #fff;
text-decoration: underline;
}
-
- .links-xs {
- text-align: center;
- font-size: 16px;
- padding: 5px;
- }
}
.warning_message {
@@ -281,10 +278,6 @@ img.emoji {
height: 220px;
}
-.navless-container {
- margin-top: 20px;
-}
-
.description-block {
@extend .light-well;
@extend .light;
@@ -300,11 +293,17 @@ table {
.dashboard-intro-icon {
float: left;
+ text-align: center;
font-size: 32px;
color: #AAA;
- padding: 5px 0;
- width: 50px;
- min-height: 100px;
+ width: 60px;
+}
+
+.dashboard-intro-text {
+ display: inline-block;
+ margin-left: -60px;
+ padding-left: 60px;
+ width: 100%;
}
.broadcast-message {
@@ -330,14 +329,14 @@ table {
}
}
-@media (max-width: $screen-xs-max) {
- .container .content { margin-top: 20px; }
-}
-
.wiki .highlight, .note-body .highlight {
margin-bottom: 9px;
}
+.wiki .code {
+ overflow-x: auto;
+}
+
.footer-links a {
margin-right: 15px;
}
@@ -359,3 +358,9 @@ table {
.task-status {
margin-left: 10px;
}
+
+#nprogress .spinner {
+ top: auto !important;
+ bottom: 20px !important;
+ left: 20px !important;
+}
diff --git a/app/assets/stylesheets/generic/files.scss b/app/assets/stylesheets/generic/files.scss
index e2b0ef0c5ea..1ed41272ac5 100644
--- a/app/assets/stylesheets/generic/files.scss
+++ b/app/assets/stylesheets/generic/files.scss
@@ -42,7 +42,6 @@
}
.file-content {
background: #fff;
- font-size: 11px;
&.image_file {
background: #eee;
@@ -54,8 +53,6 @@
}
&.wiki {
- font-size: 14px;
- line-height: 1.6;
padding: 25px;
.highlight {
diff --git a/app/assets/stylesheets/generic/forms.scss b/app/assets/stylesheets/generic/forms.scss
index e8b23090b0f..c8982cdc00d 100644
--- a/app/assets/stylesheets/generic/forms.scss
+++ b/app/assets/stylesheets/generic/forms.scss
@@ -31,7 +31,12 @@ fieldset legend {
margin-bottom: 18px;
background-color: whitesmoke;
border-top: 1px solid #e5e5e5;
- padding-left: 17%;
+}
+
+@media (min-width: $screen-sm-min) {
+ .form-actions {
+ padding-left: 17%;
+ }
}
label {
@@ -88,139 +93,7 @@ label {
@include box-shadow(none);
}
-.issuable-description {
+.issuable-description,
+.wiki-content {
margin-top: 35px;
}
-
-.zennable {
- position: relative;
-
- input {
- display: none;
- }
-
- .collapse {
- display: none;
- opacity: 0.5;
-
- &:before {
- content: '\f066';
- font-family: FontAwesome;
- color: #000;
- font-size: 28px;
- position: relative;
- padding: 30px 40px 0 0;
- }
-
- &:hover {
- opacity: 0.8;
- }
- }
-
- .expand {
- opacity: 0.5;
-
- &:before {
- content: '\f065';
- font-family: FontAwesome;
- color: #000;
- font-size: 14px;
- line-height: 14px;
- padding-right: 20px;
- position: relative;
- vertical-align: middle;
- }
-
- &:hover {
- opacity: 0.8;
- }
- }
-
- input:checked ~ .zen-backdrop .expand {
- display: none;
- }
-
- input:checked ~ .zen-backdrop .collapse {
- display: block;
- position: absolute;
- top: 0;
- }
-
- label {
- position: absolute;
- top: -26px;
- right: 0;
- font-variant: small-caps;
- text-transform: uppercase;
- font-size: 10px;
- padding: 4px;
- font-weight: 500;
- letter-spacing: 1px;
-
- &:before {
- display: inline-block;
- width: 10px;
- height: 14px;
- }
- }
-
- input:checked ~ .zen-backdrop {
- background-color: white;
- position: fixed;
- top: 0;
- bottom: 0;
- left: 0;
- right: 0;
- z-index: 1031;
-
- textarea {
- border: none;
- box-shadow: none;
- border-radius: 0;
- color: #000;
- font-size: 20px;
- line-height: 26px;
- padding: 30px;
- display: block;
- outline: none;
- resize: none;
- height: 100vh;
- max-width: 900px;
- margin: 0 auto;
- }
- }
-
- .zen-backdrop textarea::-webkit-input-placeholder {
- color: white;
- }
-
- .zen-backdrop textarea:-moz-placeholder {
- color: white;
- }
-
- .zen-backdrop textarea::-moz-placeholder {
- color: white;
- }
-
- .zen-backdrop textarea:-ms-input-placeholder {
- color: white;
- }
-
- input:checked ~ .zen-backdrop textarea::-webkit-input-placeholder {
- color: #999;
- }
-
- input:checked ~ .zen-backdrop textarea:-moz-placeholder {
- color: #999;
- opacity: 1;
- }
-
- input:checked ~ .zen-backdrop textarea::-moz-placeholder {
- color: #999;
- opacity: 1;
- }
-
- input:checked ~ .zen-backdrop textarea:-ms-input-placeholder {
- color: #999;
- }
-}
diff --git a/app/assets/stylesheets/generic/gfm.scss b/app/assets/stylesheets/generic/gfm.scss
index e257f053618..1427b6a5ae4 100644
--- a/app/assets/stylesheets/generic/gfm.scss
+++ b/app/assets/stylesheets/generic/gfm.scss
@@ -4,6 +4,7 @@
.issue-form, .merge-request-form, .wiki-form {
.description {
height: 20em;
+ border-top-left-radius: 0;
}
}
@@ -17,4 +18,4 @@
.description {
height: 14em;
}
-} \ No newline at end of file
+}
diff --git a/app/assets/stylesheets/generic/highlight.scss b/app/assets/stylesheets/generic/highlight.scss
index 4110bddf4f3..0f8225d6823 100644
--- a/app/assets/stylesheets/generic/highlight.scss
+++ b/app/assets/stylesheets/generic/highlight.scss
@@ -1,4 +1,4 @@
-.highlighted-data {
+.file-content.code {
border: none;
box-shadow: none;
margin: 0px;
@@ -10,11 +10,16 @@
border: none;
border-radius: 0;
font-family: $monospace_font;
- font-size: 12px !important;
- line-height: 16px !important;
+ font-size: $code_font_size !important;
+ line-height: $code_line_height !important;
margin: 0;
+ overflow: auto;
+ overflow-y: hidden;
+ white-space: pre;
+ word-wrap: normal;
code {
+ font-family: $monospace_font;
white-space: pre;
word-wrap: normal;
padding: 0;
@@ -25,10 +30,6 @@
}
}
- .hljs {
- padding: 0;
- }
-
.line-numbers {
padding: 10px;
text-align: right;
@@ -37,8 +38,8 @@
a {
font-family: $monospace_font;
display: block;
- font-size: 12px !important;
- line-height: 16px !important;
+ font-size: $code_font_size !important;
+ line-height: $code_line_height !important;
white-space: nowrap;
i {
@@ -51,14 +52,19 @@
}
}
}
+}
- .highlight {
- overflow: auto;
- overflow-y: hidden;
+.note-text .code {
+ border: none;
+ box-shadow: none;
+ background: $box_bg;
+ padding: 1em;
+ overflow-x: auto;
- pre {
- white-space: pre;
- word-wrap: normal;
- }
+ code {
+ font-family: $monospace_font;
+ white-space: pre;
+ word-wrap: normal;
+ padding: 0;
}
}
diff --git a/app/assets/stylesheets/generic/issue_box.scss b/app/assets/stylesheets/generic/issue_box.scss
index 94149594e24..2563ab516e2 100644
--- a/app/assets/stylesheets/generic/issue_box.scss
+++ b/app/assets/stylesheets/generic/issue_box.scss
@@ -1,123 +1,32 @@
/**
- * Issue box:
- * Huge block (one per page) for storing title, descripion and other information.
+ * Issue box for showing Open/Closed state:
* Used for Issue#show page, MergeRequest#show page etc
*
- * CLasses:
- * .issue-box - Regular box
*/
.issue-box {
- color: #555;
- margin:20px 0;
- background: $box_bg;
- @include box-shadow(0 1px 1px rgba(0, 0, 0, 0.09));
+ display: inline-block;
+ padding: 7px 13px;
+ font-weight: normal;
+ margin-right: 5px;
&.issue-box-closed {
- .state {
- background-color: #F3CECE;
- border-color: $border_danger;
- }
- .state-label {
- background-color: $bg_danger;
- color: #FFF;
- }
+ background-color: $bg_danger;
+ color: #FFF;
}
&.issue-box-merged {
- .state {
- background-color: #B7CEE7;
- border-color: $border_primary;
- }
- .state-label {
- background-color: $bg_primary;
- color: #FFF;
- }
+ background-color: $bg_primary;
+ color: #FFF;
}
&.issue-box-open {
- .state {
- background-color: #D6F1D7;
- border-color: $bg_success;
- }
- .state-label {
- background-color: $bg_success;
- color: #FFF;
- }
+ background-color: $bg_success;
+ color: #FFF;
}
&.issue-box-expired {
- .state {
- background-color: #EEE9B3;
- border-color: #faebcc;
- }
- .state-label {
- background: #cea61b;
- color: #FFF;
- }
- }
-
- .control-group {
- margin-bottom: 0;
- }
-
- .state {
- background-color: #f9f9f9;
- }
-
- .title {
- font-size: 28px;
- font-weight: normal;
- line-height: 1.5;
- margin: 0;
- color: #333;
- padding: 10px 15px;
- }
-
- .context {
- border: none;
- border-top: 1px solid #eee;
- padding: 10px 15px;
-
- // Reset text align for children
- .text-right > * { text-align: left; }
-
- @media (max-width: $screen-xs-max) {
- // Don't right align on mobile
- .text-right { text-align: left; }
-
- .row .col-md-6 {
- padding-top: 5px;
- }
- }
- }
-
- .description {
- padding: 0 15px 10px 15px;
-
- code {
- white-space: pre-wrap;
- }
- }
-
- .title, .context, .description {
- .clearfix {
- margin: 0;
- }
- }
-
- .state-label {
- font-size: 14px;
- float: left;
- font-weight: bold;
- padding: 10px 15px;
- }
-
- .creator {
- float: right;
- padding: 10px 15px;
- a {
- text-decoration: underline;
- }
+ background: #cea61b;
+ color: #FFF;
}
}
diff --git a/app/assets/stylesheets/generic/lists.scss b/app/assets/stylesheets/generic/lists.scss
index 2653bfbf831..5950885c42c 100644
--- a/app/assets/stylesheets/generic/lists.scss
+++ b/app/assets/stylesheets/generic/lists.scss
@@ -69,12 +69,11 @@
}
.well-title {
- font-size: 14px;
+ font-size: $list-font-size;
line-height: 18px;
}
.row_title {
- font-weight: 500;
color: #444;
&:hover {
color: #444;
diff --git a/app/assets/stylesheets/generic/markdown_area.scss b/app/assets/stylesheets/generic/markdown_area.scss
index fbfa72c5e5e..5a87cc6c612 100644
--- a/app/assets/stylesheets/generic/markdown_area.scss
+++ b/app/assets/stylesheets/generic/markdown_area.scss
@@ -20,6 +20,7 @@
opacity: 0;
font-size: 50px;
transition: opacity 200ms ease-in-out;
+ pointer-events: none;
}
.div-dropzone-spinner {
@@ -50,3 +51,29 @@
margin-bottom: 0;
transition: opacity 200ms ease-in-out;
}
+
+.md-preview-holder {
+ background: #FFF;
+ border: 1px solid #ddd;
+ min-height: 100px;
+ padding: 5px;
+ font-size: 14px;
+ box-shadow: none;
+}
+
+.new_note,
+.edit_note,
+.issuable-description,
+.milestone-description,
+.wiki-content,
+.merge-request-form {
+ .nav-tabs {
+ margin-bottom: 0;
+ border: none;
+
+ li a,
+ li.active a {
+ border: 1px solid #DDD;
+ }
+ }
+}
diff --git a/app/assets/stylesheets/generic/mobile.scss b/app/assets/stylesheets/generic/mobile.scss
new file mode 100644
index 00000000000..b3727c33672
--- /dev/null
+++ b/app/assets/stylesheets/generic/mobile.scss
@@ -0,0 +1,74 @@
+/** Common mobile (screen XS, SM) styles **/
+@media (max-width: $screen-xs-max) {
+ .container .content {
+ margin-top: 20px;
+ }
+
+ .nav.nav-tabs > li > a {
+ padding: 10px;
+ font-size: 12px;
+ margin-right: 3px;
+
+ .badge {
+ display: none;
+ }
+ }
+
+ .issues-filters,
+ .dash-projects-filters,
+ .check-all-holder {
+ display: none;
+ }
+
+ .rss-btn {
+ display: none !important;
+ }
+
+ .project-home-panel {
+ .star-fork-buttons {
+ padding-top: 10px;
+ padding-right: 15px;
+ }
+ }
+
+ .project-home-links {
+ display: none;
+ }
+}
+
+@media (max-width: $screen-sm-max) {
+ .issues-filters {
+ .milestone-filter, .labels-filter {
+ display: none;
+ }
+ }
+
+ .page-title .new-issue-link {
+ display: none;
+ }
+
+ .issue_edited_ago, .note_edited_ago {
+ display: none;
+ }
+
+ aside {
+ display: none;
+ }
+
+ .show-aside {
+ display: block !important;
+ }
+}
+
+.show-aside {
+ display: none;
+ position: fixed;
+ right: 0px;
+ top: 30%;
+ padding: 5px 15px;
+ background: #EEE;
+ font-size: 20px;
+ color: #777;
+ z-index: 100;
+ @include box-shadow(0 1px 2px #DDD);
+}
diff --git a/app/assets/stylesheets/generic/selects.scss b/app/assets/stylesheets/generic/selects.scss
index e0f508d2695..d85e80a512b 100644
--- a/app/assets/stylesheets/generic/selects.scss
+++ b/app/assets/stylesheets/generic/selects.scss
@@ -116,6 +116,18 @@ select {
}
}
+.group-result {
+ .group-image {
+ float: left;
+ }
+ .group-name {
+ font-weight: bold;
+ }
+ .group-path {
+ color: #999;
+ }
+}
+
.user-result {
.user-image {
float: left;
diff --git a/app/assets/stylesheets/generic/sidebar.scss b/app/assets/stylesheets/generic/sidebar.scss
deleted file mode 100644
index f6311ef74e8..00000000000
--- a/app/assets/stylesheets/generic/sidebar.scss
+++ /dev/null
@@ -1,46 +0,0 @@
-.ui.sidebar {
- z-index: 1000 !important;
- background: #fff;
- padding: 10px;
- width: 285px;
-}
-
-.ui.right.sidebar {
- border-left: 1px solid #e1e1e1;
- border-right: 0;
-}
-
-.sidebar-expand-button {
- cursor: pointer;
- transition: all 0.4s;
- -moz-transition: all 0.4s;
- -webkit-transition: all 0.4s;
-}
-
-.fixed.sidebar-expand-button {
- background: #f9f9f9;
- color: #555;
- padding: 9px 12px 6px 14px;
- border: 1px solid #E1E1E1;
- border-right: 0;
- position: fixed;
- top: 108px;
- right: 0px;
- margin-right: 0;
- &:hover {
- background: #ddd;
- color: #333;
- padding-right: 25px;
- }
-}
-
-.btn.btn-default.sidebar-expand-button {
- margin-left: 12px;
- display: inline-block !important;
-}
-
-@media (min-width: 767px) {
-.btn.btn-default.sidebar-expand-button {
- display: none!important;
- }
-}
diff --git a/app/assets/stylesheets/generic/tables.scss b/app/assets/stylesheets/generic/tables.scss
new file mode 100644
index 00000000000..71a7d4abaee
--- /dev/null
+++ b/app/assets/stylesheets/generic/tables.scss
@@ -0,0 +1,20 @@
+table {
+ &.table {
+ tr {
+ td, th {
+ padding: 8px 10px;
+ line-height: 20px;
+ vertical-align: middle;
+ }
+ th {
+ font-weight: normal;
+ font-size: 15px;
+ border-bottom: 1px solid #CCC !important;
+ }
+ td {
+ border-color: #F1F1F1 !important;
+ border-bottom: 1px solid;
+ }
+ }
+ }
+}
diff --git a/app/assets/stylesheets/generic/timeline.scss b/app/assets/stylesheets/generic/timeline.scss
index f29cf25fa4c..f92a79f7a5f 100644
--- a/app/assets/stylesheets/generic/timeline.scss
+++ b/app/assets/stylesheets/generic/timeline.scss
@@ -42,7 +42,7 @@
background: #fff;
color: #737881;
float: left;
- @include border-radius(40px);
+ @include border-radius($avatar_radius);
@include box-shadow(0 0 0 3px #EEE);
overflow: hidden;
@@ -58,6 +58,10 @@
padding: 10px 15px;
margin-left: 60px;
+ img {
+ max-width: 100%;
+ }
+
&:after {
content: '';
display: block;
@@ -74,4 +78,57 @@
}
}
}
+
+ .system-note .timeline-entry-inner {
+ .timeline-icon {
+ background: none;
+ margin-left: 12px;
+ margin-top: 0;
+ @include box-shadow(none);
+
+ span {
+ margin: 0 2px;
+ font-size: 16px;
+ color: #eeeeee;
+ }
+ }
+
+ .timeline-content {
+ background: none;
+ margin-left: 45px;
+ padding: 0px 15px;
+
+ &:after { border: 0; }
+
+ .note-header {
+ span { font-size: 12px; }
+
+ .avatar {
+ margin-right: 5px;
+ }
+ }
+
+ .note-text {
+ font-size: 12px;
+ margin-left: 20px;
+ }
+ }
+ }
+}
+
+@media (max-width: $screen-xs-max) {
+ .timeline {
+ &:before {
+ background: none;
+ }
+ .timeline-entry .timeline-entry-inner {
+ .timeline-icon {
+ display: none;
+ }
+
+ .timeline-content {
+ margin-left: 0;
+ }
+ }
+ }
}
diff --git a/app/assets/stylesheets/generic/typography.scss b/app/assets/stylesheets/generic/typography.scss
index 385a627b4be..c547ebb3aaf 100644
--- a/app/assets/stylesheets/generic/typography.scss
+++ b/app/assets/stylesheets/generic/typography.scss
@@ -17,6 +17,10 @@ h3.page-title {
font-size: 22px;
}
+h4.page-title {
+ margin-top: 0px;
+}
+
h6 {
color: #888;
text-transform: uppercase;
@@ -128,3 +132,7 @@ a:focus {
textarea.js-gfm-input {
font-family: $monospace_font;
}
+
+.strikethrough {
+ text-decoration: line-through;
+}
diff --git a/app/assets/stylesheets/generic/zen.scss b/app/assets/stylesheets/generic/zen.scss
new file mode 100644
index 00000000000..26afc21a6ab
--- /dev/null
+++ b/app/assets/stylesheets/generic/zen.scss
@@ -0,0 +1,98 @@
+.zennable {
+ position: relative;
+
+ input {
+ display: none;
+ }
+
+ .zen-enter-link {
+ color: #888;
+ position: absolute;
+ top: -26px;
+ right: 4px;
+ }
+
+ .zen-leave-link {
+ display: none;
+ color: #888;
+ position: absolute;
+ top: 10px;
+ right: 10px;
+ padding: 5px;
+ font-size: 36px;
+
+ &:hover {
+ color: #111;
+ }
+ }
+
+ input:checked ~ .zen-backdrop .zen-enter-link {
+ display: none;
+ }
+
+ input:checked ~ .zen-backdrop .zen-leave-link {
+ display: block;
+ position: absolute;
+ top: 0;
+ }
+
+ input:checked ~ .zen-backdrop {
+ background-color: white;
+ position: fixed;
+ top: 0;
+ bottom: 0;
+ left: 0;
+ right: 0;
+ z-index: 1031;
+
+ textarea {
+ border: none;
+ box-shadow: none;
+ border-radius: 0;
+ color: #000;
+ font-size: 20px;
+ line-height: 26px;
+ padding: 30px;
+ display: block;
+ outline: none;
+ resize: none;
+ height: 100vh;
+ max-width: 900px;
+ margin: 0 auto;
+ }
+ }
+
+ .zen-backdrop textarea::-webkit-input-placeholder {
+ color: white;
+ }
+
+ .zen-backdrop textarea:-moz-placeholder {
+ color: white;
+ }
+
+ .zen-backdrop textarea::-moz-placeholder {
+ color: white;
+ }
+
+ .zen-backdrop textarea:-ms-input-placeholder {
+ color: white;
+ }
+
+ input:checked ~ .zen-backdrop textarea::-webkit-input-placeholder {
+ color: #999;
+ }
+
+ input:checked ~ .zen-backdrop textarea:-moz-placeholder {
+ color: #999;
+ opacity: 1;
+ }
+
+ input:checked ~ .zen-backdrop textarea::-moz-placeholder {
+ color: #999;
+ opacity: 1;
+ }
+
+ input:checked ~ .zen-backdrop textarea:-ms-input-placeholder {
+ color: #999;
+ }
+}
diff --git a/app/assets/stylesheets/gl_bootstrap.scss b/app/assets/stylesheets/gl_bootstrap.scss
index 9c5e76ab8e2..6efa56544a5 100644
--- a/app/assets/stylesheets/gl_bootstrap.scss
+++ b/app/assets/stylesheets/gl_bootstrap.scss
@@ -1,9 +1,6 @@
/*
* Twitter bootstrap with GitLab customizations/additions
*
- * Some unused bootstrap compontents like panels are not included.
- * Other components like tabs are modified to GitLab style.
- *
*/
$font-size-base: 13px !default;
@@ -148,6 +145,10 @@ $list-group-active-bg: $bg_primary;
color: #666;
}
+.nav-compact > li > a {
+ padding: 6px 12px;
+}
+
.nav-small > li > a {
padding: 3px 5px;
font-size: 12px;
diff --git a/app/assets/stylesheets/highlight/dark.scss b/app/assets/stylesheets/highlight/dark.scss
index ca51da3fdd4..fcd4d47bace 100644
--- a/app/assets/stylesheets/highlight/dark.scss
+++ b/app/assets/stylesheets/highlight/dark.scss
@@ -1,199 +1,84 @@
-.dark {
- background-color: #232323;
-
- .line.hll {
- background: #558;
- }
-
- .highlight{
- border-left: 1px solid #444;
- }
-
- .no-highlight {
- color: #DDD;
- }
+/* https://github.com/MozMorris/tomorrow-pygments */
+.code.dark {
+ pre.code,
+ .line-numbers,
.line-numbers a {
- color: #666;
- }
-
- pre {
- background-color: #232323;
- }
-
- .hljs {
- display: block;
- background: #232323;
- color: #E6E1DC;
- }
-
- .hljs-comment,
- .hljs-template_comment,
- .hljs-javadoc,
- .hljs-shebang {
- color: #BC9458;
- font-style: italic;
- }
-
- .hljs-keyword,
- .ruby .hljs-function .hljs-keyword,
- .hljs-request,
- .hljs-status,
- .nginx .hljs-title,
- .method,
- .hljs-list .hljs-title {
- color: #C26230;
- }
-
- .hljs-string,
- .hljs-number,
- .hljs-regexp,
- .hljs-tag .hljs-value,
- .hljs-cdata,
- .hljs-filter .hljs-argument,
- .hljs-attr_selector,
- .apache .hljs-cbracket,
- .hljs-date,
- .tex .hljs-command,
- .markdown .hljs-link_label {
- color: #A5C261;
- }
-
- .hljs-subst {
- color: #519F50;
- }
-
- .hljs-tag,
- .hljs-tag .hljs-keyword,
- .hljs-tag .hljs-title,
- .hljs-doctype,
- .hljs-sub .hljs-identifier,
- .hljs-pi,
- .input_number {
- color: #E8BF6A;
- }
-
- .hljs-identifier {
- color: #D0D0FF;
- }
-
- .hljs-class .hljs-title,
- .haskell .hljs-type,
- .smalltalk .hljs-class,
- .hljs-javadoctag,
- .hljs-yardoctag,
- .hljs-phpdoc {
- text-decoration: none;
- }
-
- .hljs-constant {
- color: #DA4939;
- }
-
-
- .hljs-symbol,
- .hljs-built_in,
- .ruby .hljs-symbol .hljs-string,
- .ruby .hljs-symbol .hljs-identifier,
- .markdown .hljs-link_url,
- .hljs-attribute {
- color: #6D9CBE;
- }
-
- .markdown .hljs-link_url {
- text-decoration: underline;
- }
-
-
-
- .hljs-params,
- .hljs-variable,
- .clojure .hljs-attribute {
- color: #D0D0FF;
- }
-
- .css .hljs-tag,
- .hljs-rules .hljs-property,
- .hljs-pseudo,
- .tex .hljs-special {
- color: #CDA869;
- }
-
- .css .hljs-class {
- color: #9B703F;
- }
-
- .hljs-rules .hljs-keyword {
- color: #C5AF75;
- }
-
- .hljs-rules .hljs-value {
- color: #CF6A4C;
- }
-
- .css .hljs-id {
- color: #8B98AB;
- }
-
- .hljs-annotation,
- .apache .hljs-sqbracket,
- .nginx .hljs-built_in {
- color: #9B859D;
- }
-
- .hljs-preprocessor,
- .hljs-preprocessor *,
- .hljs-pragma {
- color: #8996A8 !important;
- }
-
- .hljs-hexcolor,
- .css .hljs-value .hljs-number {
- color: #A5C261;
- }
-
- .hljs-title,
- .hljs-decorator,
- .css .hljs-function {
- color: #FFC66D;
- }
-
- .diff .hljs-header,
- .hljs-chunk {
- background-color: #2F33AB;
- color: #E6E1DC;
- display: inline-block;
- width: 100%;
- }
-
- .diff .hljs-change {
- background-color: #4A410D;
- color: #F8F8F8;
- display: inline-block;
- width: 100%;
- }
-
- .hljs-addition {
- background-color: #144212;
- color: #E6E1DC;
- display: inline-block;
- width: 100%;
- }
-
- .hljs-deletion {
- background-color: #600;
- color: #E6E1DC;
- display: inline-block;
- width: 100%;
- }
-
- .coffeescript .javascript,
- .javascript .xml,
- .tex .hljs-formula,
- .xml .javascript,
- .xml .vbscript,
- .xml .css,
- .xml .hljs-cdata {
- opacity: 0.7;
- }
+ background-color: #1d1f21 !important;
+ color: #c5c8c6 !important;
+ }
+
+ pre.code {
+ border-left: 1px solid #666;
+ }
+
+ // highlight line via anchor
+ pre.hll {
+ background-color: #fff !important;
+ }
+
+ .hll { background-color: #373b41 }
+ .c { color: #969896 } /* Comment */
+ .err { color: #cc6666 } /* Error */
+ .k { color: #b294bb } /* Keyword */
+ .l { color: #de935f } /* Literal */
+ .n { color: #c5c8c6 } /* Name */
+ .o { color: #8abeb7 } /* Operator */
+ .p { color: #c5c8c6 } /* Punctuation */
+ .cm { color: #969896 } /* Comment.Multiline */
+ .cp { color: #969896 } /* Comment.Preproc */
+ .c1 { color: #969896 } /* Comment.Single */
+ .cs { color: #969896 } /* Comment.Special */
+ .gd { color: #cc6666 } /* Generic.Deleted */
+ .ge { font-style: italic } /* Generic.Emph */
+ .gh { color: #c5c8c6; font-weight: bold } /* Generic.Heading */
+ .gi { color: #b5bd68 } /* Generic.Inserted */
+ .gp { color: #969896; font-weight: bold } /* Generic.Prompt */
+ .gs { font-weight: bold } /* Generic.Strong */
+ .gu { color: #8abeb7; font-weight: bold } /* Generic.Subheading */
+ .kc { color: #b294bb } /* Keyword.Constant */
+ .kd { color: #b294bb } /* Keyword.Declaration */
+ .kn { color: #8abeb7 } /* Keyword.Namespace */
+ .kp { color: #b294bb } /* Keyword.Pseudo */
+ .kr { color: #b294bb } /* Keyword.Reserved */
+ .kt { color: #f0c674 } /* Keyword.Type */
+ .ld { color: #b5bd68 } /* Literal.Date */
+ .m { color: #de935f } /* Literal.Number */
+ .s { color: #b5bd68 } /* Literal.String */
+ .na { color: #81a2be } /* Name.Attribute */
+ .nb { color: #c5c8c6 } /* Name.Builtin */
+ .nc { color: #f0c674 } /* Name.Class */
+ .no { color: #cc6666 } /* Name.Constant */
+ .nd { color: #8abeb7 } /* Name.Decorator */
+ .ni { color: #c5c8c6 } /* Name.Entity */
+ .ne { color: #cc6666 } /* Name.Exception */
+ .nf { color: #81a2be } /* Name.Function */
+ .nl { color: #c5c8c6 } /* Name.Label */
+ .nn { color: #f0c674 } /* Name.Namespace */
+ .nx { color: #81a2be } /* Name.Other */
+ .py { color: #c5c8c6 } /* Name.Property */
+ .nt { color: #8abeb7 } /* Name.Tag */
+ .nv { color: #cc6666 } /* Name.Variable */
+ .ow { color: #8abeb7 } /* Operator.Word */
+ .w { color: #c5c8c6 } /* Text.Whitespace */
+ .mf { color: #de935f } /* Literal.Number.Float */
+ .mh { color: #de935f } /* Literal.Number.Hex */
+ .mi { color: #de935f } /* Literal.Number.Integer */
+ .mo { color: #de935f } /* Literal.Number.Oct */
+ .sb { color: #b5bd68 } /* Literal.String.Backtick */
+ .sc { color: #c5c8c6 } /* Literal.String.Char */
+ .sd { color: #969896 } /* Literal.String.Doc */
+ .s2 { color: #b5bd68 } /* Literal.String.Double */
+ .se { color: #de935f } /* Literal.String.Escape */
+ .sh { color: #b5bd68 } /* Literal.String.Heredoc */
+ .si { color: #de935f } /* Literal.String.Interpol */
+ .sx { color: #b5bd68 } /* Literal.String.Other */
+ .sr { color: #b5bd68 } /* Literal.String.Regex */
+ .s1 { color: #b5bd68 } /* Literal.String.Single */
+ .ss { color: #b5bd68 } /* Literal.String.Symbol */
+ .bp { color: #c5c8c6 } /* Name.Builtin.Pseudo */
+ .vc { color: #cc6666 } /* Name.Variable.Class */
+ .vg { color: #cc6666 } /* Name.Variable.Global */
+ .vi { color: #cc6666 } /* Name.Variable.Instance */
+ .il { color: #de935f } /* Literal.Number.Integer.Long */
}
diff --git a/app/assets/stylesheets/highlight/monokai.scss b/app/assets/stylesheets/highlight/monokai.scss
index 36bc5df2f44..bcd2e716657 100644
--- a/app/assets/stylesheets/highlight/monokai.scss
+++ b/app/assets/stylesheets/highlight/monokai.scss
@@ -1,148 +1,84 @@
-.monokai {
- background-color: #272822;
-
- .highlight{
- border-left: 1px solid #444;
- }
-
- .line.hll {
- background: #558;
- }
-
- .no-highlight {
- color: #DDD;
- }
+/* https://github.com/richleland/pygments-css/blob/master/monokai.css */
+.code.monokai {
+ pre.highlight,
+ .line-numbers,
.line-numbers a {
- color: #666;
- }
-
- pre {
- background-color: #272822;
- color: #f8f8f2;
- }
-
- .hljs {
- display: block;
- background: #272822;
- }
-
- .hljs-tag,
- .hljs-tag .hljs-title,
- .hljs-keyword,
- .hljs-literal,
- .hljs-strong,
- .hljs-change,
- .hljs-winutils,
- .hljs-flow,
- .lisp .hljs-title,
- .clojure .hljs-built_in,
- .nginx .hljs-title,
- .tex .hljs-special {
- color: #F92672;
- }
-
- .hljs {
- color: #DDD;
+ background:#272822 !important;
+ color:#f8f8f2 !important;
}
- .hljs .hljs-constant,
- .asciidoc .hljs-code {
- color: #66D9EF;
+ pre.code {
+ border-left: 1px solid #555;
}
- .hljs-code,
- .hljs-class .hljs-title,
- .hljs-header {
- color: white;
+ // highlight line via anchor
+ pre.hll {
+ background-color: #49483e !important;
}
- .hljs-link_label,
- .hljs-attribute,
- .hljs-symbol,
- .hljs-symbol .hljs-string,
- .hljs-value,
- .hljs-regexp {
- color: #BF79DB;
- }
-
- .hljs-link_url,
- .hljs-tag .hljs-value,
- .hljs-string,
- .hljs-bullet,
- .hljs-subst,
- .hljs-title,
- .hljs-emphasis,
- .haskell .hljs-type,
- .hljs-preprocessor,
- .hljs-pragma,
- .ruby .hljs-class .hljs-parent,
- .hljs-built_in,
- .sql .hljs-aggregate,
- .django .hljs-template_tag,
- .django .hljs-variable,
- .smalltalk .hljs-class,
- .hljs-javadoc,
- .django .hljs-filter .hljs-argument,
- .smalltalk .hljs-localvars,
- .smalltalk .hljs-array,
- .hljs-attr_selector,
- .hljs-pseudo,
- .hljs-addition,
- .hljs-stream,
- .hljs-envvar,
- .apache .hljs-tag,
- .apache .hljs-cbracket,
- .tex .hljs-command,
- .hljs-prompt {
- color: #A6E22E;
- }
+ .hll { background-color: #49483e }
+ .c { color: #75715e } /* Comment */
+ .err { color: #960050; background-color: #1e0010 } /* Error */
+ .k { color: #66d9ef } /* Keyword */
+ .l { color: #ae81ff } /* Literal */
+ .n { color: #f8f8f2 } /* Name */
+ .o { color: #f92672 } /* Operator */
+ .p { color: #f8f8f2 } /* Punctuation */
+ .cm { color: #75715e } /* Comment.Multiline */
+ .cp { color: #75715e } /* Comment.Preproc */
+ .c1 { color: #75715e } /* Comment.Single */
+ .cs { color: #75715e } /* Comment.Special */
+ .ge { font-style: italic } /* Generic.Emph */
+ .gs { font-weight: bold } /* Generic.Strong */
+ .kc { color: #66d9ef } /* Keyword.Constant */
+ .kd { color: #66d9ef } /* Keyword.Declaration */
+ .kn { color: #f92672 } /* Keyword.Namespace */
+ .kp { color: #66d9ef } /* Keyword.Pseudo */
+ .kr { color: #66d9ef } /* Keyword.Reserved */
+ .kt { color: #66d9ef } /* Keyword.Type */
+ .ld { color: #e6db74 } /* Literal.Date */
+ .m { color: #ae81ff } /* Literal.Number */
+ .s { color: #e6db74 } /* Literal.String */
+ .na { color: #a6e22e } /* Name.Attribute */
+ .nb { color: #f8f8f2 } /* Name.Builtin */
+ .nc { color: #a6e22e } /* Name.Class */
+ .no { color: #66d9ef } /* Name.Constant */
+ .nd { color: #a6e22e } /* Name.Decorator */
+ .ni { color: #f8f8f2 } /* Name.Entity */
+ .ne { color: #a6e22e } /* Name.Exception */
+ .nf { color: #a6e22e } /* Name.Function */
+ .nl { color: #f8f8f2 } /* Name.Label */
+ .nn { color: #f8f8f2 } /* Name.Namespace */
+ .nx { color: #a6e22e } /* Name.Other */
+ .py { color: #f8f8f2 } /* Name.Property */
+ .nt { color: #f92672 } /* Name.Tag */
+ .nv { color: #f8f8f2 } /* Name.Variable */
+ .ow { color: #f92672 } /* Operator.Word */
+ .w { color: #f8f8f2 } /* Text.Whitespace */
+ .mf { color: #ae81ff } /* Literal.Number.Float */
+ .mh { color: #ae81ff } /* Literal.Number.Hex */
+ .mi { color: #ae81ff } /* Literal.Number.Integer */
+ .mo { color: #ae81ff } /* Literal.Number.Oct */
+ .sb { color: #e6db74 } /* Literal.String.Backtick */
+ .sc { color: #e6db74 } /* Literal.String.Char */
+ .sd { color: #e6db74 } /* Literal.String.Doc */
+ .s2 { color: #e6db74 } /* Literal.String.Double */
+ .se { color: #ae81ff } /* Literal.String.Escape */
+ .sh { color: #e6db74 } /* Literal.String.Heredoc */
+ .si { color: #e6db74 } /* Literal.String.Interpol */
+ .sx { color: #e6db74 } /* Literal.String.Other */
+ .sr { color: #e6db74 } /* Literal.String.Regex */
+ .s1 { color: #e6db74 } /* Literal.String.Single */
+ .ss { color: #e6db74 } /* Literal.String.Symbol */
+ .bp { color: #f8f8f2 } /* Name.Builtin.Pseudo */
+ .vc { color: #f8f8f2 } /* Name.Variable.Class */
+ .vg { color: #f8f8f2 } /* Name.Variable.Global */
+ .vi { color: #f8f8f2 } /* Name.Variable.Instance */
+ .il { color: #ae81ff } /* Literal.Number.Integer.Long */
- .hljs-comment,
- .java .hljs-annotation,
- .smartquote,
- .hljs-blockquote,
- .hljs-horizontal_rule,
- .python .hljs-decorator,
- .hljs-template_comment,
- .hljs-pi,
- .hljs-doctype,
- .hljs-deletion,
- .hljs-shebang,
- .apache .hljs-sqbracket,
- .tex .hljs-formula {
- color: #75715E;
- }
-
- .hljs-keyword,
- .hljs-literal,
- .css .hljs-id,
- .hljs-phpdoc,
- .hljs-title,
- .hljs-header,
- .haskell .hljs-type,
- .vbscript .hljs-built_in,
- .sql .hljs-aggregate,
- .rsl .hljs-built_in,
- .smalltalk .hljs-class,
- .diff .hljs-header,
- .hljs-chunk,
- .hljs-winutils,
- .bash .hljs-variable,
- .apache .hljs-tag,
- .tex .hljs-special,
- .hljs-request,
- .hljs-status {
- font-weight: bold;
- }
-
- .coffeescript .javascript,
- .javascript .xml,
- .tex .hljs-formula,
- .xml .javascript,
- .xml .vbscript,
- .xml .css,
- .xml .hljs-cdata {
- opacity: 0.5;
- }
+ .gh { } /* Generic Heading & Diff Header */
+ .gu { color: #75715e; } /* Generic.Subheading & Diff Unified/Comment? */
+ .gd { color: #f92672; } /* Generic.Deleted & Diff Deleted */
+ .gi { color: #a6e22e; } /* Generic.Inserted & Diff Inserted */
}
diff --git a/app/assets/stylesheets/highlight/solarized_dark.scss b/app/assets/stylesheets/highlight/solarized_dark.scss
index b9bec225188..4a6b759bd2c 100644
--- a/app/assets/stylesheets/highlight/solarized_dark.scss
+++ b/app/assets/stylesheets/highlight/solarized_dark.scss
@@ -1,125 +1,106 @@
-.solarized-dark {
- background-color: #002B36;
-
- .highlight{
- border-left: 1px solid #113b46;
- }
-
- .line.hll {
- background: #000;
- }
-
- .no-highlight {
- color: #DDD;
- }
-
- pre {
- background-color: #002B36;
- color: #eee;
- }
+/* https://gist.github.com/qguv/7936275 */
+.code.solarized-dark {
+ pre.code,
+ .line-numbers,
.line-numbers a {
- color: #666;
- }
-
- .hljs {
- display: block;
- background: #002b36;
- color: #839496;
+ background-color: #002b36 !important;
+ color: #93a1a1 !important;
}
- .hljs-comment,
- .hljs-template_comment,
- .diff .hljs-header,
- .hljs-doctype,
- .hljs-pi,
- .lisp .hljs-string,
- .hljs-javadoc {
- color: #586e75;
- }
-
- /* Solarized Green */
- .hljs-keyword,
- .hljs-winutils,
- .method,
- .hljs-addition,
- .css .hljs-tag,
- .hljs-request,
- .hljs-status,
- .nginx .hljs-title {
- color: #859900;
- }
-
- /* Solarized Cyan */
- .hljs-number,
- .hljs-command,
- .hljs-string,
- .hljs-tag .hljs-value,
- .hljs-rules .hljs-value,
- .hljs-phpdoc,
- .tex .hljs-formula,
- .hljs-regexp,
- .hljs-hexcolor,
- .hljs-link_url {
- color: #2aa198;
+ pre.code {
+ border-left: 1px solid #113b46;
}
- /* Solarized Blue */
- .hljs-title,
- .hljs-localvars,
- .hljs-chunk,
- .hljs-decorator,
- .hljs-built_in,
- .hljs-identifier,
- .vhdl .hljs-literal,
- .hljs-id,
- .css .hljs-function {
- color: #268bd2;
+ // highlight line via anchor
+ pre.hll {
+ background-color: #073642 !important;
}
- /* Solarized Yellow */
- .hljs-attribute,
- .hljs-variable,
- .lisp .hljs-body,
- .smalltalk .hljs-number,
- .hljs-constant,
- .hljs-class .hljs-title,
- .hljs-parent,
- .haskell .hljs-type,
- .hljs-link_reference {
- color: #b58900;
- }
+ /* Solarized Dark
- /* Solarized Orange */
- .hljs-preprocessor,
- .hljs-preprocessor .hljs-keyword,
- .hljs-pragma,
- .hljs-shebang,
- .hljs-symbol,
- .hljs-symbol .hljs-string,
- .diff .hljs-change,
- .hljs-special,
- .hljs-attr_selector,
- .hljs-subst,
- .hljs-cdata,
- .clojure .hljs-title,
- .css .hljs-pseudo,
- .hljs-header {
- color: #cb4b16;
- }
+ For use with Jekyll and Pygments
- /* Solarized Red */
- .hljs-deletion,
- .hljs-important {
- color: #dc322f;
- }
+ http://ethanschoonover.com/solarized
- /* Solarized Violet */
- .hljs-link_label {
- color: #6c71c4;
- }
+ SOLARIZED HEX ROLE
+ --------- -------- ------------------------------------------
+ base03 #002b36 background
+ base01 #586e75 comments / secondary content
+ base1 #93a1a1 body text / default code / primary content
+ orange #cb4b16 constants
+ red #dc322f regex, special keywords
+ blue #268bd2 reserved keywords
+ cyan #2aa198 strings, numbers
+ green #859900 operators, other keywords
+ */
- .tex .hljs-formula {
- background: #073642;
- }
+ .c { color: #586e75 } /* Comment */
+ .err { color: #93a1a1 } /* Error */
+ .g { color: #93a1a1 } /* Generic */
+ .k { color: #859900 } /* Keyword */
+ .l { color: #93a1a1 } /* Literal */
+ .n { color: #93a1a1 } /* Name */
+ .o { color: #859900 } /* Operator */
+ .x { color: #cb4b16 } /* Other */
+ .p { color: #93a1a1 } /* Punctuation */
+ .cm { color: #586e75 } /* Comment.Multiline */
+ .cp { color: #859900 } /* Comment.Preproc */
+ .c1 { color: #586e75 } /* Comment.Single */
+ .cs { color: #859900 } /* Comment.Special */
+ .gd { color: #2aa198 } /* Generic.Deleted */
+ .ge { color: #93a1a1; font-style: italic } /* Generic.Emph */
+ .gr { color: #dc322f } /* Generic.Error */
+ .gh { color: #cb4b16 } /* Generic.Heading */
+ .gi { color: #859900 } /* Generic.Inserted */
+ .go { color: #93a1a1 } /* Generic.Output */
+ .gp { color: #93a1a1 } /* Generic.Prompt */
+ .gs { color: #93a1a1; font-weight: bold } /* Generic.Strong */
+ .gu { color: #cb4b16 } /* Generic.Subheading */
+ .gt { color: #93a1a1 } /* Generic.Traceback */
+ .kc { color: #cb4b16 } /* Keyword.Constant */
+ .kd { color: #268bd2 } /* Keyword.Declaration */
+ .kn { color: #859900 } /* Keyword.Namespace */
+ .kp { color: #859900 } /* Keyword.Pseudo */
+ .kr { color: #268bd2 } /* Keyword.Reserved */
+ .kt { color: #dc322f } /* Keyword.Type */
+ .ld { color: #93a1a1 } /* Literal.Date */
+ .m { color: #2aa198 } /* Literal.Number */
+ .s { color: #2aa198 } /* Literal.String */
+ .na { color: #93a1a1 } /* Name.Attribute */
+ .nb { color: #B58900 } /* Name.Builtin */
+ .nc { color: #268bd2 } /* Name.Class */
+ .no { color: #cb4b16 } /* Name.Constant */
+ .nd { color: #268bd2 } /* Name.Decorator */
+ .ni { color: #cb4b16 } /* Name.Entity */
+ .ne { color: #cb4b16 } /* Name.Exception */
+ .nf { color: #268bd2 } /* Name.Function */
+ .nl { color: #93a1a1 } /* Name.Label */
+ .nn { color: #93a1a1 } /* Name.Namespace */
+ .nx { color: #93a1a1 } /* Name.Other */
+ .py { color: #93a1a1 } /* Name.Property */
+ .nt { color: #268bd2 } /* Name.Tag */
+ .nv { color: #268bd2 } /* Name.Variable */
+ .ow { color: #859900 } /* Operator.Word */
+ .w { color: #93a1a1 } /* Text.Whitespace */
+ .mf { color: #2aa198 } /* Literal.Number.Float */
+ .mh { color: #2aa198 } /* Literal.Number.Hex */
+ .mi { color: #2aa198 } /* Literal.Number.Integer */
+ .mo { color: #2aa198 } /* Literal.Number.Oct */
+ .sb { color: #586e75 } /* Literal.String.Backtick */
+ .sc { color: #2aa198 } /* Literal.String.Char */
+ .sd { color: #93a1a1 } /* Literal.String.Doc */
+ .s2 { color: #2aa198 } /* Literal.String.Double */
+ .se { color: #cb4b16 } /* Literal.String.Escape */
+ .sh { color: #93a1a1 } /* Literal.String.Heredoc */
+ .si { color: #2aa198 } /* Literal.String.Interpol */
+ .sx { color: #2aa198 } /* Literal.String.Other */
+ .sr { color: #dc322f } /* Literal.String.Regex */
+ .s1 { color: #2aa198 } /* Literal.String.Single */
+ .ss { color: #2aa198 } /* Literal.String.Symbol */
+ .bp { color: #268bd2 } /* Name.Builtin.Pseudo */
+ .vc { color: #268bd2 } /* Name.Variable.Class */
+ .vg { color: #268bd2 } /* Name.Variable.Global */
+ .vi { color: #268bd2 } /* Name.Variable.Instance */
+ .il { color: #2aa198 } /* Literal.Number.Integer.Long */
}
diff --git a/app/assets/stylesheets/highlight/solarized_light.scss b/app/assets/stylesheets/highlight/solarized_light.scss
new file mode 100644
index 00000000000..7254f4d7ac1
--- /dev/null
+++ b/app/assets/stylesheets/highlight/solarized_light.scss
@@ -0,0 +1,106 @@
+/* https://gist.github.com/qguv/7936275 */
+.code.solarized-light {
+
+ pre.code,
+ .line-numbers,
+ .line-numbers a {
+ background-color: #fdf6e3 !important;
+ color: #586e75 !important;
+ }
+
+ pre.code {
+ border-left: 1px solid #c5d0d4;
+ }
+
+ // highlight line via anchor
+ pre.hll {
+ background-color: #eee8d5 !important;
+ }
+
+ /* Solarized Light
+
+ For use with Jekyll and Pygments
+
+ http://ethanschoonover.com/solarized
+
+ SOLARIZED HEX ROLE
+ --------- -------- ------------------------------------------
+ base01 #586e75 body text / default code / primary content
+ base1 #93a1a1 comments / secondary content
+ base3 #fdf6e3 background
+ orange #cb4b16 constants
+ red #dc322f regex, special keywords
+ blue #268bd2 reserved keywords
+ cyan #2aa198 strings, numbers
+ green #859900 operators, other keywords
+ */
+
+ .c { color: #93a1a1 } /* Comment */
+ .err { color: #586e75 } /* Error */
+ .g { color: #586e75 } /* Generic */
+ .k { color: #859900 } /* Keyword */
+ .l { color: #586e75 } /* Literal */
+ .n { color: #586e75 } /* Name */
+ .o { color: #859900 } /* Operator */
+ .x { color: #cb4b16 } /* Other */
+ .p { color: #586e75 } /* Punctuation */
+ .cm { color: #93a1a1 } /* Comment.Multiline */
+ .cp { color: #859900 } /* Comment.Preproc */
+ .c1 { color: #93a1a1 } /* Comment.Single */
+ .cs { color: #859900 } /* Comment.Special */
+ .gd { color: #2aa198 } /* Generic.Deleted */
+ .ge { color: #586e75; font-style: italic } /* Generic.Emph */
+ .gr { color: #dc322f } /* Generic.Error */
+ .gh { color: #cb4b16 } /* Generic.Heading */
+ .gi { color: #859900 } /* Generic.Inserted */
+ .go { color: #586e75 } /* Generic.Output */
+ .gp { color: #586e75 } /* Generic.Prompt */
+ .gs { color: #586e75; font-weight: bold } /* Generic.Strong */
+ .gu { color: #cb4b16 } /* Generic.Subheading */
+ .gt { color: #586e75 } /* Generic.Traceback */
+ .kc { color: #cb4b16 } /* Keyword.Constant */
+ .kd { color: #268bd2 } /* Keyword.Declaration */
+ .kn { color: #859900 } /* Keyword.Namespace */
+ .kp { color: #859900 } /* Keyword.Pseudo */
+ .kr { color: #268bd2 } /* Keyword.Reserved */
+ .kt { color: #dc322f } /* Keyword.Type */
+ .ld { color: #586e75 } /* Literal.Date */
+ .m { color: #2aa198 } /* Literal.Number */
+ .s { color: #2aa198 } /* Literal.String */
+ .na { color: #586e75 } /* Name.Attribute */
+ .nb { color: #B58900 } /* Name.Builtin */
+ .nc { color: #268bd2 } /* Name.Class */
+ .no { color: #cb4b16 } /* Name.Constant */
+ .nd { color: #268bd2 } /* Name.Decorator */
+ .ni { color: #cb4b16 } /* Name.Entity */
+ .ne { color: #cb4b16 } /* Name.Exception */
+ .nf { color: #268bd2 } /* Name.Function */
+ .nl { color: #586e75 } /* Name.Label */
+ .nn { color: #586e75 } /* Name.Namespace */
+ .nx { color: #586e75 } /* Name.Other */
+ .py { color: #586e75 } /* Name.Property */
+ .nt { color: #268bd2 } /* Name.Tag */
+ .nv { color: #268bd2 } /* Name.Variable */
+ .ow { color: #859900 } /* Operator.Word */
+ .w { color: #586e75 } /* Text.Whitespace */
+ .mf { color: #2aa198 } /* Literal.Number.Float */
+ .mh { color: #2aa198 } /* Literal.Number.Hex */
+ .mi { color: #2aa198 } /* Literal.Number.Integer */
+ .mo { color: #2aa198 } /* Literal.Number.Oct */
+ .sb { color: #93a1a1 } /* Literal.String.Backtick */
+ .sc { color: #2aa198 } /* Literal.String.Char */
+ .sd { color: #586e75 } /* Literal.String.Doc */
+ .s2 { color: #2aa198 } /* Literal.String.Double */
+ .se { color: #cb4b16 } /* Literal.String.Escape */
+ .sh { color: #586e75 } /* Literal.String.Heredoc */
+ .si { color: #2aa198 } /* Literal.String.Interpol */
+ .sx { color: #2aa198 } /* Literal.String.Other */
+ .sr { color: #dc322f } /* Literal.String.Regex */
+ .s1 { color: #2aa198 } /* Literal.String.Single */
+ .ss { color: #2aa198 } /* Literal.String.Symbol */
+ .bp { color: #268bd2 } /* Name.Builtin.Pseudo */
+ .vc { color: #268bd2 } /* Name.Variable.Class */
+ .vg { color: #268bd2 } /* Name.Variable.Global */
+ .vi { color: #268bd2 } /* Name.Variable.Instance */
+ .il { color: #2aa198 } /* Literal.Number.Integer.Long */
+}
diff --git a/app/assets/stylesheets/highlight/white.scss b/app/assets/stylesheets/highlight/white.scss
index 815cf367ae8..4d6f5dfd91e 100644
--- a/app/assets/stylesheets/highlight/white.scss
+++ b/app/assets/stylesheets/highlight/white.scss
@@ -1,188 +1,83 @@
-.white {
- .line.hll {
- background: #FFA;
- }
-
- pre {
- background-color: #fff;
- color: #333;
- }
-
- .hljs {
- background: #FFF;
- }
+/* https://github.com/aahan/pygments-github-style */
+.code.white {
+ pre.highlight,
+ .line-numbers,
.line-numbers a {
- color: #999;
- }
-
- .hljs {
- display: block;
- background: #fff; color: black;
- }
-
- .hljs-comment,
- .hljs-template_comment,
- .hljs-javadoc,
- .hljs-comment * {
- color: #006a00;
- }
-
- .hljs-keyword,
- .hljs-literal,
- .nginx .hljs-title {
- color: #aa0d91;
- }
- .method,
- .hljs-list .hljs-title,
- .hljs-tag .hljs-title,
- .setting .hljs-value,
- .hljs-winutils,
- .tex .hljs-command,
- .http .hljs-title,
- .hljs-request,
- .hljs-status {
- color: #008;
- }
-
- .hljs-envvar,
- .tex .hljs-special {
- color: #660;
- }
-
- .hljs-string {
- color: #c41a16;
- }
- .hljs-tag .hljs-value,
- .hljs-cdata,
- .hljs-filter .hljs-argument,
- .hljs-attr_selector,
- .apache .hljs-cbracket,
- .hljs-date,
- .hljs-regexp {
- color: #080;
- }
-
- .hljs-sub .hljs-identifier,
- .hljs-pi,
- .hljs-tag,
- .hljs-tag .hljs-keyword,
- .hljs-decorator,
- .ini .hljs-title,
- .hljs-shebang,
- .hljs-prompt,
- .hljs-hexcolor,
- .hljs-rules .hljs-value,
- .hljs-symbol,
- .hljs-symbol .hljs-string,
- .hljs-number,
- .css .hljs-function,
- .clojure .hljs-title,
- .clojure .hljs-built_in,
- .hljs-function .hljs-title,
- .coffeescript .hljs-attribute {
- color: #1c00cf;
- }
-
- .hljs-class .hljs-title,
- .haskell .hljs-type,
- .smalltalk .hljs-class,
- .hljs-javadoctag,
- .hljs-yardoctag,
- .hljs-phpdoc,
- .hljs-typename,
- .hljs-tag .hljs-attribute,
- .hljs-doctype,
- .hljs-class .hljs-id,
- .hljs-built_in,
- .setting,
- .hljs-params,
- .clojure .hljs-attribute {
- color: #5c2699;
- }
-
- .hljs-variable {
- color: #3f6e74;
- }
- .css .hljs-tag,
- .hljs-rules .hljs-property,
- .hljs-pseudo,
- .hljs-subst {
- color: #000;
- }
-
- .css .hljs-class,
- .css .hljs-id {
- color: #9B703F;
- }
-
- .hljs-value .hljs-important {
- color: #ff7700;
- font-weight: bold;
- }
-
- .hljs-rules .hljs-keyword {
- color: #C5AF75;
- }
-
- .hljs-annotation,
- .apache .hljs-sqbracket,
- .nginx .hljs-built_in {
- color: #9B859D;
- }
-
- .hljs-preprocessor,
- .hljs-preprocessor *,
- .hljs-pragma {
- color: #643820;
- }
-
- .tex .hljs-formula {
- background-color: #EEE;
- font-style: italic;
- }
-
- .diff .hljs-header,
- .hljs-chunk {
- color: #808080;
- font-weight: bold;
- }
-
- .diff .hljs-change {
- background-color: #BCCFF9;
- }
-
- .hljs-addition {
- background-color: #BAEEBA;
- }
-
- .hljs-deletion {
- background-color: #FFC8BD;
- }
-
- .hljs-comment .hljs-yardoctag {
- font-weight: bold;
- }
-
- .method .hljs-id {
- color: #000;
- }
-}
-
-.shadow {
- @include box-shadow(0 5px 15px #000);
-}
-
-.file-content {
- &.code .white {
- .highlight {
- border-left: 1px solid #eee;
- }
- }
-
- &.wiki .white {
- .highlight, pre, .hljs {
- background: #F9F9F9;
- }
- }
+ background-color: #fff !important;
+ color: #333 !important;
+ }
+
+ pre.code {
+ border-left: 1px solid #bbb;
+ }
+
+ // highlight line via anchor
+ pre.hll {
+ background-color: #f8eec7 !important;
+ }
+
+ .hll { background-color: #f8f8f8 }
+ .c { color: #999988; font-style: italic; }
+ .err { color: #a61717; background-color: #e3d2d2; }
+ .k { font-weight: bold; }
+ .o { font-weight: bold; }
+ .cm { color: #999988; font-style: italic; }
+ .cp { color: #999999; font-weight: bold; }
+ .c1 { color: #999988; font-style: italic; }
+ .cs { color: #999999; font-weight: bold; font-style: italic; }
+ .gd { color: #000000; background-color: #ffdddd; }
+ .gd .x { color: #000000; background-color: #ffaaaa; }
+ .ge { font-style: italic; }
+ .gr { color: #aa0000; }
+ .gh { color: #999999; }
+ .gi { color: #000000; background-color: #ddffdd; }
+ .gi .x { color: #000000; background-color: #aaffaa; }
+ .go { color: #888888; }
+ .gp { color: #555555; }
+ .gs { font-weight: bold; }
+ .gu { color: #800080; font-weight: bold; }
+ .gt { color: #aa0000; }
+ .kc { font-weight: bold; }
+ .kd { font-weight: bold; }
+ .kn { font-weight: bold; }
+ .kp { font-weight: bold; }
+ .kr { font-weight: bold; }
+ .kt { color: #445588; font-weight: bold; }
+ .m { color: #009999; }
+ .s { color: #dd1144; }
+ .n { color: #333333; }
+ .na { color: teal; }
+ .nb { color: #0086b3; }
+ .nc { color: #445588; font-weight: bold; }
+ .no { color: teal; }
+ .ni { color: purple; }
+ .ne { color: #990000; font-weight: bold; }
+ .nf { color: #990000; font-weight: bold; }
+ .nn { color: #555555; }
+ .nt { color: navy; }
+ .nv { color: teal; }
+ .ow { font-weight: bold; }
+ .w { color: #bbbbbb; }
+ .mf { color: #009999; }
+ .mh { color: #009999; }
+ .mi { color: #009999; }
+ .mo { color: #009999; }
+ .sb { color: #dd1144; }
+ .sc { color: #dd1144; }
+ .sd { color: #dd1144; }
+ .s2 { color: #dd1144; }
+ .se { color: #dd1144; }
+ .sh { color: #dd1144; }
+ .si { color: #dd1144; }
+ .sx { color: #dd1144; }
+ .sr { color: #009926; }
+ .s1 { color: #dd1144; }
+ .ss { color: #990073; }
+ .bp { color: #999999; }
+ .vc { color: teal; }
+ .vg { color: teal; }
+ .vi { color: teal; }
+ .il { color: #009999; }
+ .gc { color: #999; background-color: #EAF2F5; }
}
diff --git a/app/assets/stylesheets/main/layout.scss b/app/assets/stylesheets/main/layout.scss
index 2800feb81f2..1085e68b7d4 100644
--- a/app/assets/stylesheets/main/layout.scss
+++ b/app/assets/stylesheets/main/layout.scss
@@ -2,10 +2,10 @@ html {
overflow-y: scroll;
&.touch .tooltip { display: none !important; }
-}
-body {
- padding-bottom: 20px;
+ body {
+ padding-top: 47px;
+ }
}
.container {
@@ -17,3 +17,6 @@ body {
margin: 0 0;
}
+.navless-container {
+ margin-top: 30px;
+}
diff --git a/app/assets/stylesheets/main/mixins.scss b/app/assets/stylesheets/main/mixins.scss
index 7f607fc4e8b..e54482d14c3 100644
--- a/app/assets/stylesheets/main/mixins.scss
+++ b/app/assets/stylesheets/main/mixins.scss
@@ -58,8 +58,8 @@
}
@mixin md-typography {
- font-size: 14px;
- line-height: 1.6;
+ font-size: 15px;
+ line-height: 1.5;
img {
max-width: 100%;
@@ -69,7 +69,12 @@
margin-top: 0;
}
- code { padding: 0 4px; }
+ code {
+ font-family: $monospace_font;
+ white-space: pre;
+ word-wrap: normal;
+ padding: 0;
+ }
h1 {
margin-top: 45px;
@@ -93,7 +98,7 @@
blockquote p {
color: #888;
- font-size: 14px;
+ font-size: 15px;
line-height: 1.5;
}
@@ -134,7 +139,7 @@
}
@mixin panel-colored {
- border: none;
+ border: 1px solid #EEE;
background: $box_bg;
@include box-shadow(0 1px 1px rgba(0, 0, 0, 0.09));
diff --git a/app/assets/stylesheets/main/variables.scss b/app/assets/stylesheets/main/variables.scss
index c71984a5665..acbf5be94a3 100644
--- a/app/assets/stylesheets/main/variables.scss
+++ b/app/assets/stylesheets/main/variables.scss
@@ -2,7 +2,7 @@
* General Colors
*/
$style_color: #474D57;
-$hover: #FFECDB;
+$hover: #FFF3EB;
$box_bg: #F9F9F9;
/*
@@ -44,6 +44,20 @@ $added: #63c363;
$deleted: #f77;
/**
- *
+ * NProgress customize
*/
-$nprogress-color: #3498db;
+$nprogress-color: #c0392b;
+
+/**
+ * Font sizes
+ */
+$list-font-size: 15px;
+
+/**
+ * Sidebar navigation width
+ */
+$sidebar_width: 230px;
+
+$avatar_radius: 50%;
+$code_font_size: 13px;
+$code_line_height: 1.5;
diff --git a/app/assets/stylesheets/sections/commit.scss b/app/assets/stylesheets/sections/commit.scss
new file mode 100644
index 00000000000..0e2d9571a45
--- /dev/null
+++ b/app/assets/stylesheets/sections/commit.scss
@@ -0,0 +1,131 @@
+.commit-title{
+ display: block;
+}
+
+.commit-title{
+ margin-bottom: 10px;
+}
+
+.commit-author, .commit-committer{
+ display: block;
+ color: #999;
+ font-weight: normal;
+ font-style: italic;
+}
+
+.commit-author strong, .commit-committer strong{
+ font-weight: bold;
+ font-style: normal;
+}
+
+.commit-description {
+ background: none;
+ border: none;
+ margin: 0;
+ padding: 0;
+ margin-top: 10px;
+}
+
+.commit-stat-summary {
+ color: #666;
+ font-size: 14px;
+ font-weight: normal;
+ padding: 10px 0;
+}
+
+.commit-info-row {
+ margin-bottom: 10px;
+ .avatar {
+ @extend .avatar-inline;
+ }
+ .commit-committer-link,
+ .commit-author-link {
+ color: #444;
+ font-weight: bold;
+ }
+}
+
+.commit-committer-link,
+.commit-author-link {
+ font-size: 13px;
+ color: #555;
+ &:hover {
+ color: #999;
+ }
+}
+
+.commit-box {
+ margin: 10px 0;
+ border-top: 1px solid #ddd;
+ border-bottom: 1px solid #ddd;
+ padding: 20px 0;
+
+ .commit-title {
+ margin: 0;
+ }
+
+ .commit-description {
+ margin-top: 15px;
+ }
+}
+
+.file-stats a {
+ color: $style_color;
+}
+
+.file-stats {
+ .new-file {
+ a {
+ color: #090;
+ }
+ i {
+ color: #1BCF00;
+ }
+ }
+ .renamed-file {
+ i {
+ color: #FE9300;
+ }
+ }
+ .deleted-file {
+ a {
+ color: #B00;
+ }
+ i {
+ color: #EE0000;
+ }
+ }
+ .edit-file{
+ i{
+ color: #555;
+ }
+ }
+}
+
+/*
+ * Commit message textarea for web editor and
+ * custom merge request message
+ */
+.commit-message-container {
+ background-color: $body-bg;
+ position: relative;
+ font-family: $monospace_font;
+ $left: 12px;
+ .max-width-marker {
+ width: 72ch;
+ color: rgba(0, 0, 0, 0.0);
+ font-family: inherit;
+ left: $left;
+ height: 100%;
+ border-right: 1px solid mix($input-border, white);
+ position: absolute;
+ z-index: 1;
+ }
+ > textarea {
+ background-color: rgba(0, 0, 0, 0.0);
+ font-family: inherit;
+ padding-left: $left;
+ position: relative;
+ z-index: 2;
+ }
+}
diff --git a/app/assets/stylesheets/sections/commits.scss b/app/assets/stylesheets/sections/commits.scss
index 684e8377a7b..683aca73593 100644
--- a/app/assets/stylesheets/sections/commits.scss
+++ b/app/assets/stylesheets/sections/commits.scss
@@ -1,77 +1,3 @@
-/**
- * Commit file
- */
-.commit-committer-link,
-.commit-author-link {
- font-size: 13px;
- color: #555;
- &:hover {
- color: #999;
- }
-}
-
-/** COMMIT BLOCK **/
-.commit-title{
- display: block;
-}
-.commit-title{
- margin-bottom: 10px;
-}
-.commit-author, .commit-committer{
- display: block;
- color: #999;
- font-weight: normal;
- font-style: italic;
-}
-.commit-author strong, .commit-committer strong{
- font-weight: bold;
- font-style: normal;
-}
-
-
-.file-stats a {
- color: $style_color;
-}
-
-.file-stats {
- .new-file {
- a {
- color: #090;
- }
- i {
- color: #1BCF00;
- }
- }
- .renamed-file {
- i {
- color: #FE9300;
- }
- }
- .deleted-file {
- a {
- color: #B00;
- }
- i {
- color: #EE0000;
- }
- }
- .edit-file{
- i{
- color: #555;
- }
- }
-}
-
-.label_commit {
- @include border-radius(4px);
- padding: 2px 4px;
- font-size: 13px;
- background: #474D57;
- color: #fff;
- font-family: $monospace_font;
-}
-
-
.commits-compare-switch{
background: image-url("switch_icon.png") no-repeat center center;
width: 32px;
@@ -85,61 +11,40 @@
background-color: #EEE;
}
-.commit-description {
- background: none;
- border: none;
- margin: 0;
- padding: 0;
- margin-top: 10px;
-}
-.commit-box {
+.lists-separator {
margin: 10px 0;
- border-top: 1px solid #ddd;
- border-bottom: 1px solid #ddd;
- padding: 20px 0;
+ border-color: #DDD;
+}
- .commit-title {
+.commits-row {
+ ul {
margin: 0;
- font-size: 20px;
+
+ li.commit {
+ padding: 8px 0;
+ }
}
- .commit-description {
- margin-top: 15px;
+ .commits-row-date {
+ font-size: 15px;
+ line-height: 20px;
+ margin-bottom: 5px;
}
}
+.commits-feed-holder {
+ float: right;
-.commit-stat-summary {
- color: #666;
- font-size: 14px;
- font-weight: normal;
- padding: 10px 0;
-}
-
-.commit-info-row {
- margin-bottom: 10px;
- .avatar {
- @extend .avatar-inline;
- }
- .commit-committer-link,
- .commit-author-link {
- color: #444;
- font-weight: bold;
+ .btn {
+ padding: 4px 12px;
}
}
-.lists-separator {
- margin: 10px 0;
- border-top: 1px dashed #CCC;
-}
-
-/**
- * COMMIT ROW
- */
li.commit {
.commit-row-title {
- font-size: 14px;
+ font-size: $list-font-size;
+ line-height: 20px;
margin-bottom: 2px;
.notes_count {
@@ -157,10 +62,9 @@ li.commit {
}
.commit-row-message {
- color: #333;
- font-weight: 500;
+ color: #444;
+
&:hover {
- color: #444;
text-decoration: underline;
}
}
@@ -195,13 +99,14 @@ li.commit {
.commit-row-info {
color: #777;
+ line-height: 24px;
a {
color: #777;
}
.committed_ago {
- float: right;
+ display: inline-block;
}
}
@@ -216,34 +121,3 @@ li.commit {
}
}
}
-
-.commits-feed-holder {
- float: right;
- .btn {
- padding: 4px 12px;
- }
-}
-
-.commit-message-container {
- background-color: $body-bg;
- position: relative;
- font-family: $monospace_font;
- $left: 12px;
- .max-width-marker {
- width: 72ch;
- color: rgba(0, 0, 0, 0.0);
- font-family: inherit;
- left: $left;
- height: 100%;
- border-right: 1px solid mix($input-border, white);
- position: absolute;
- z-index: 1;
- }
- > textarea {
- background-color: rgba(0, 0, 0, 0.0);
- font-family: inherit;
- padding-left: $left;
- position: relative;
- z-index: 2;
- }
-}
diff --git a/app/assets/stylesheets/sections/dashboard.scss b/app/assets/stylesheets/sections/dashboard.scss
index d181d83e857..d8fd83d44b7 100644
--- a/app/assets/stylesheets/sections/dashboard.scss
+++ b/app/assets/stylesheets/sections/dashboard.scss
@@ -23,20 +23,6 @@
}
}
-.dashboard {
- .dash-filter {
- width: 205px;
- float: left;
- height: inherit;
- }
-}
-
-@media (max-width: 1200px) {
- .dashboard .dash-filter {
- width: 140px;
- }
-}
-
.dash-sidebar-tabs {
margin-bottom: 2px;
border: none;
@@ -98,7 +84,6 @@
margin-left: 10px;
float: left;
margin-right: 15px;
- font-size: 20px;
margin-bottom: 15px;
i {
@@ -106,8 +91,43 @@
}
}
+.dash-project-avatar {
+ float: left;
+
+ .avatar {
+ margin-top: -8px;
+ margin-left: -15px;
+ @include border-radius(0px);
+ }
+ .identicon {
+ line-height: 40px;
+ }
+}
+
.dash-project-access-icon {
float: left;
- margin-right: 3px;
+ margin-right: 5px;
width: 16px;
}
+
+.dash-new-project {
+ background: $bg_success;
+ border: 1px solid $border_success;
+
+ a {
+ color: #FFF;
+ }
+}
+
+.dash-new-group {
+ background: $bg_success;
+ border: 1px solid $border_success;
+
+ a {
+ color: #FFF;
+ }
+}
+
+.dash-list .str-truncated {
+ max-width: 72%;
+}
diff --git a/app/assets/stylesheets/sections/diff.scss b/app/assets/stylesheets/sections/diff.scss
index 758f15c8013..54311a68852 100644
--- a/app/assets/stylesheets/sections/diff.scss
+++ b/app/assets/stylesheets/sections/diff.scss
@@ -8,6 +8,7 @@
border-bottom: 1px solid #CCC;
padding: 5px 5px 5px 10px;
color: #555;
+ z-index: 10;
> span {
font-family: $monospace_font;
@@ -37,15 +38,15 @@
overflow-y: hidden;
background: #FFF;
color: #333;
- font-size: 12px;
+ font-size: $code_font_size;
.old {
span.idiff {
- background-color: #F99;
+ background-color: #f8cbcb;
}
}
.new {
span.idiff {
- background-color: #8F8;
+ background-color: #a6f3a6;
}
}
.unfold {
@@ -64,8 +65,8 @@
margin: 0px;
padding: 0px;
td {
- line-height: 18px;
- font-size: 12px;
+ line-height: $code_line_height;
+ font-size: $code_font_size;
}
}
@@ -84,7 +85,7 @@
padding: 0px;
border: none;
background: #F5F5F5;
- color: #666;
+ color: rgba(0,0,0,0.3);
padding: 0px 5px;
border-right: 1px solid #ccc;
text-align: right;
@@ -96,7 +97,7 @@
float: left;
width: 35px;
font-weight: normal;
- color: #666;
+ color: rgba(0,0,0,0.3);
&:hover {
text-decoration: underline;
}
@@ -114,13 +115,13 @@
.line_holder {
&.old .old_line,
&.old .new_line {
- background: #FCC;
- border-color: #E7BABA;
+ background: #ffdddd;
+ border-color: #f1c0c0;
}
&.new .old_line,
&.new .new_line {
- background: #CFC;
- border-color: #B9ECB9;
+ background: #dbffdb;
+ border-color: #c1e9c1;
}
}
.line_content {
@@ -129,10 +130,10 @@
padding: 0px 0.5em;
border: none;
&.new {
- background: #CFD;
+ background: #eaffea;
}
&.old {
- background: #FDD;
+ background: #ffecec;
}
&.matched {
color: #ccc;
diff --git a/app/assets/stylesheets/sections/editor.scss b/app/assets/stylesheets/sections/editor.scss
index f62f46ee168..88aa256e56e 100644
--- a/app/assets/stylesheets/sections/editor.scss
+++ b/app/assets/stylesheets/sections/editor.scss
@@ -31,4 +31,26 @@
margin: 5px 8px 0 8px;
}
}
+
+ .file-title {
+ @extend .monospace;
+ font-size: 14px;
+ padding: 5px;
+ }
+
+ .editor-ref {
+ background: #f5f5f5;
+ padding: 11px 15px;
+ border-right: 1px solid #CCC;
+ display: inline-block;
+ margin: -5px -5px;
+ margin-right: 10px;
+ }
+
+ .editor-file-name {
+ .new-file-name {
+ display: inline-block;
+ width: 200px;
+ }
+ }
}
diff --git a/app/assets/stylesheets/sections/events.scss b/app/assets/stylesheets/sections/events.scss
index 656aa5b18a6..a477359dc88 100644
--- a/app/assets/stylesheets/sections/events.scss
+++ b/app/assets/stylesheets/sections/events.scss
@@ -47,7 +47,7 @@
.event-title {
@include str-truncated(72%);
color: #333;
- font-weight: normal;
+ font-weight: 500;
font-size: 14px;
.author_name {
color: #333;
@@ -55,25 +55,28 @@
}
.event-body {
margin-left: 35px;
- margin-right: 100px;
+ margin-right: 80px;
+ color: #777;
- .event-info {
- color: #666;
- }
.event-note {
- color: #666;
margin-top: 5px;
+ word-wrap: break-word;
.md {
font-size: 13px;
+
+ iframe.twitter-share-button {
+ vertical-align: bottom;
+ }
}
pre {
border: none;
background: #f9f9f9;
border-radius: 0;
- color: #666;
+ color: #777;
margin: 0 20px;
+ overflow: hidden;
}
.note-image-attach {
@@ -120,7 +123,6 @@
padding: 3px;
padding-left: 0;
border: none;
- color: #666;
.commit-row-title {
font-size: 12px;
}
@@ -144,49 +146,50 @@
}
}
-/**
- * Event filter
- *
- */
-.event_filter {
- position: absolute;
- width: 40px;
- margin-left: -55px;
-
- .filter_icon {
- a {
- text-align:center;
- background: $bg_primary;
- margin-bottom: 10px;
- float: left;
- padding: 9px 6px;
- font-size: 18px;
- width: 40px;
- color: #FFF;
- @include border-radius(3px);
- }
-
- &.inactive {
- a {
- color: #DDD;
- background: #f9f9f9;
- }
- }
- }
-}
/*
* Last push widget
*/
.event-last-push {
+ overflow: auto;
.event-last-push-text {
- @include str-truncated(75%);
+ @include str-truncated(100%);
+ float:left;
+ margin-right: -150px;
+ padding-right: 150px;
line-height: 24px;
}
}
@media (max-width: $screen-xs-max) {
- .event-item .event-title {
- @include str-truncated(65%);
+ .event-item {
+ .event-title {
+ white-space: normal;
+ overflow: visible;
+ max-width: 100%;
+ }
+ .avatar {
+ display: none;
+ }
+
+ .event-body {
+ margin: 0;
+ border-left: 2px solid #DDD;
+ padding-left: 10px;
+ }
+
+ .event-item-timestamp {
+ display: none;
+ }
+ }
+}
+
+.event_filter {
+
+ li a {
+ padding: 5px 10px;
+ background: rgba(0,0,0,0.045);
+ margin-left: 4px;
}
+
}
diff --git a/app/assets/stylesheets/sections/header.scss b/app/assets/stylesheets/sections/header.scss
index e0e0d60c387..26b4d04106e 100644
--- a/app/assets/stylesheets/sections/header.scss
+++ b/app/assets/stylesheets/sections/header.scss
@@ -4,9 +4,11 @@
*/
header {
&.navbar-gitlab {
+ z-index: 100;
margin-bottom: 0;
min-height: 40px;
border: none;
+ width: 100%;
.navbar-inner {
filter: none;
@@ -52,13 +54,12 @@ header {
border-width: 0;
font-size: 18px;
- .app_logo { margin-left: -15px; }
-
.title {
@include str-truncated(70%);
}
.navbar-collapse {
+ margin-top: 47px;
padding-right: 0;
padding-left: 0;
}
@@ -83,7 +84,10 @@ header {
}
}
- z-index: 10;
+ .container {
+ width: 100% !important;
+ padding: 0px;
+ }
/**
*
@@ -96,18 +100,14 @@ header {
a {
float: left;
- padding: 0px;
- margin: 0 6px;
-
- h1 {
- margin: 0;
- background: image-url('logo-black.png') no-repeat center center;
- background-size: 32px;
- float: left;
- height: 46px;
- width: 40px;
- @include header-font;
- text-indent: -9999px;
+ padding: 5px 0;
+ height: 46px;
+ width: 52px;
+ text-align: center;
+
+ img {
+ width: 36px;
+ height: 36px;
}
}
&:hover {
@@ -130,13 +130,13 @@ header {
}
.profile-pic {
- position: relative;
- top: -1px;
- padding-right: 0px !important;
+ padding: 0px !important;
+ width: 46px;
+ height: 46px;
+ margin-left: 5px;
img {
- width: 26px;
- height: 26px;
- @include border-radius(4px);
+ width: 46px;
+ height: 46px;
}
}
@@ -169,83 +169,6 @@ header {
@include transition(all 0.15s ease-in 0s);
}
}
-
-
- /*
- * Dark header
- *
- */
- &.header-dark {
- &.navbar-gitlab {
- .navbar-inner {
- background: #708090;
- border-bottom: 1px solid #AAA;
-
- .navbar-toggle { color: #fff; }
-
- .nav > li > a {
- color: #AAA;
-
- &:hover, &:focus, &:active {
- background: none;
- color: #FFF;
- }
- }
- }
- }
-
- .turbolink-spinner {
- color: #FFF;
- }
-
- .search {
- .search-input {
- background-color: #D2D5DA;
- background-color: rgba(255, 255, 255, 0.5);
- border: 1px solid #AAA;
-
- &:focus {
- background-color: white;
- }
- }
- }
- .search-input::-webkit-input-placeholder {
- color: #666;
- }
- .app_logo {
- a {
- h1 {
- background: image-url('logo-white.png') no-repeat center center;
- background-size: 32px;
- color: #fff;
- }
- }
- }
- .title {
- a {
- color: #FFF;
- &:hover {
- text-decoration: underline;
- }
- }
- color: #fff;
- }
- }
-
- .app_logo {
- .separator {
- margin-left: 0;
- margin-right: 0;
- }
- }
-
- .separator {
- float: left;
- height: 46px;
- width: 2px;
- margin-left: 10px;
- margin-right: 10px;
- }
}
.search .search-input {
diff --git a/app/assets/stylesheets/sections/import.scss b/app/assets/stylesheets/sections/import.scss
new file mode 100644
index 00000000000..3df4bb84bd2
--- /dev/null
+++ b/app/assets/stylesheets/sections/import.scss
@@ -0,0 +1,18 @@
+i.icon-gitorious {
+ display: inline-block;
+ background-position: 0px 0px;
+ background-size: contain;
+ background-repeat: no-repeat;
+}
+
+i.icon-gitorious-small {
+ background-image: image-url('gitorious-logo-blue.png');
+ width: 13px;
+ height: 13px;
+}
+
+i.icon-gitorious-big {
+ background-image: image-url('gitorious-logo-black.png');
+ width: 18px;
+ height: 18px;
+}
diff --git a/app/assets/stylesheets/sections/issuable.scss b/app/assets/stylesheets/sections/issuable.scss
new file mode 100644
index 00000000000..d8d12338859
--- /dev/null
+++ b/app/assets/stylesheets/sections/issuable.scss
@@ -0,0 +1,41 @@
+@media (max-width: $screen-sm-max) {
+ .issuable-affix {
+ margin-top: 20px;
+ }
+}
+
+@media (max-width: $screen-md-max) {
+ .issuable-affix {
+ position: static;
+ }
+}
+
+@media (min-width: $screen-md-max) {
+ .issuable-affix {
+ &.affix-top {
+ position: static;
+ }
+
+ &.affix {
+ position: fixed;
+ top: 70px;
+ width: 220px;
+ }
+ }
+}
+
+.issuable-context-title {
+ font-size: 15px;
+ line-height: 1.4;
+ margin-bottom: 5px;
+
+ .avatar {
+ margin-left: 0;
+ }
+
+ label {
+ color: #666;
+ font-weight: normal;
+ margin-right: 4px;
+ }
+}
diff --git a/app/assets/stylesheets/sections/issues.scss b/app/assets/stylesheets/sections/issues.scss
index ebf8a6125c7..b909725bff5 100644
--- a/app/assets/stylesheets/sections/issues.scss
+++ b/app/assets/stylesheets/sections/issues.scss
@@ -5,7 +5,8 @@
.issue-title {
margin-bottom: 5px;
- font-size: 14px;
+ font-size: $list-font-size;
+ font-weight: bold;
}
.issue-info {
@@ -94,8 +95,15 @@
}
}
-.issue-show-labels .color-label {
- padding: 6px 10px;
+.issue-show-labels {
+ a {
+ margin-right: 5px;
+ margin-bottom: 5px;
+ display: inline-block;
+ .color-label {
+ padding: 6px 10px;
+ }
+ }
}
form.edit-issue {
@@ -151,4 +159,23 @@ form.edit-issue {
}
}
}
+
+ .issue {
+ &:hover .issue-actions {
+ display: none !important;
+ }
+
+ .issue-updated-at {
+ display: none;
+ }
+ }
+}
+
+h2.issue-title {
+ margin-top: 0;
+ font-weight: bold;
+}
+
+.context .select2-container {
+ width: 100% !important;
}
diff --git a/app/assets/stylesheets/sections/login.scss b/app/assets/stylesheets/sections/login.scss
index 1bcb1f6d68e..d366300511e 100644
--- a/app/assets/stylesheets/sections/login.scss
+++ b/app/assets/stylesheets/sections/login.scss
@@ -1,48 +1,69 @@
/* Login Page */
.login-page {
- h1 {
- font-size: 3em;
- font-weight: 200;
+ .container {
+ max-width: 960px;
+ }
+
+ .navbar-gitlab .container {
+ max-width: none;
+ }
+
+ .brand-holder {
+ font-size: 18px;
+ line-height: 1.5;
+
+ p {
+ color: #888;
+ }
+
+ h1:first-child {
+ font-weight: normal;
+ margin-bottom: 30px;
+ }
+
+ img {
+ max-width: 100%;
+ margin-bottom: 30px;
+ }
+
+ a {
+ font-weight: bold;
+ }
}
.login-box{
- padding: 0 15px;
+ background: #fafafa;
+ border-radius: 10px;
+ box-shadow: 0 0px 2px #CCC;
+ padding: 15px;
.login-heading h3 {
font-weight: 300;
- line-height: 2;
+ line-height: 1.5;
+ margin: 0 0 10px 0;
}
.login-footer {
margin-top: 10px;
- }
- .btn {
- padding: 12px !important;
- @extend .btn-block;
+ p:last-child {
+ margin-bottom: 0;
+ }
}
- }
- .brand-image {
- img {
- max-width: 100%;
- margin-bottom: 20px;
+ a.forgot {
+ float: right;
+ padding-top: 6px
}
- &.default-brand-image {
- margin: 0 80px;
+ .nav .active a {
+ background: transparent;
}
}
- .login-logo {
- margin: 10px 0 30px 0;
- display: block;
- }
-
.form-control {
- background-color: #F5F5F5;
- font-size: 16px;
- padding: 14px 10px;
+ font-size: 14px;
+ padding: 10px 8px;
width: 100%;
height: auto;
@@ -68,19 +89,27 @@
}
}
- .login-box a.forgot {
- float: right;
- padding-top: 6px
- }
-
.devise-errors {
h2 {
+ margin-top: 0;
font-size: 14px;
color: #a00;
}
}
- .brand-holder {
- border-right: 1px solid #EEE;
+ .remember-me {
+ margin-top: -10px;
+
+ label {
+ font-weight: normal;
+ }
+ }
+}
+
+@media (max-width: $screen-xs-max) {
+ .login-page {
+ .col-sm-5.pull-right {
+ float: none !important;
+ }
}
}
diff --git a/app/assets/stylesheets/sections/markdown_area.scss b/app/assets/stylesheets/sections/markdown_area.scss
new file mode 100644
index 00000000000..8ee8eaa4ee7
--- /dev/null
+++ b/app/assets/stylesheets/sections/markdown_area.scss
@@ -0,0 +1,9 @@
+.markdown-area {
+ background: #FFF;
+ border: 1px solid #ddd;
+ min-height: 100px;
+ padding: 5px;
+ font-size: 14px;
+ box-shadow: none;
+ width: 100%;
+}
diff --git a/app/assets/stylesheets/sections/merge_requests.scss b/app/assets/stylesheets/sections/merge_requests.scss
index ec844cc00b0..0d2d8b0173e 100644
--- a/app/assets/stylesheets/sections/merge_requests.scss
+++ b/app/assets/stylesheets/sections/merge_requests.scss
@@ -11,25 +11,42 @@
}
}
- .accept-group {
- label {
- margin: 5px;
+ .accept-merge-holder {
+ margin-top: 5px;
+
+ .accept-action {
+ display: inline-block;
+
+ .accept_merge_request {
+ padding: 10px 20px;
+ }
+ }
+
+ .accept-control {
+ display: inline-block;
+ margin: 0;
margin-left: 20px;
+ padding: 10px 0;
+ line-height: 20px;
+ font-weight: bold;
+
+ .remove_source_checkbox {
+ margin: 0;
+ font-weight: bold;
+ }
}
}
}
-.merge-request .merge-request-tabs{
- border-bottom: 2px solid $border_primary;
- margin: 20px 0;
-
- li {
- a {
- padding: 15px 40px;
- font-size: 14px;
- margin-bottom: -2px;
- border-bottom: 2px solid $border_primary;
- @include border-radius(0px);
+@media(min-width: $screen-sm-max) {
+ .merge-request .merge-request-tabs{
+ margin: 20px 0;
+
+ li {
+ a {
+ padding: 15px 40px;
+ font-size: 14px;
+ }
}
}
}
@@ -73,7 +90,8 @@
.merge-request-title {
margin-bottom: 5px;
- font-size: 14px;
+ font-size: $list-font-size;
+ font-weight: bold;
}
.merge-request-info {
@@ -106,6 +124,8 @@
.mr-state-widget {
background: $box_bg;
margin-bottom: 20px;
+ color: #666;
+ border: 1px solid #EEE;
@include box-shadow(0 1px 1px rgba(0, 0, 0, 0.09));
.ci_widget {
@@ -150,7 +170,6 @@
padding: 10px 15px;
h4 {
- font-size: 20px;
font-weight: normal;
}
@@ -169,10 +188,13 @@
}
}
-.merge-request-show-labels .label {
- padding: 6px 10px;
-}
-
-.mr-commits .commit {
- padding: 10px 15px;
+.merge-request-show-labels {
+ a {
+ margin-right: 5px;
+ margin-bottom: 5px;
+ display: inline-block;
+ .color-label {
+ padding: 6px 10px;
+ }
+ }
}
diff --git a/app/assets/stylesheets/sections/nav.scss b/app/assets/stylesheets/sections/nav.scss
deleted file mode 100644
index 31c0a0835db..00000000000
--- a/app/assets/stylesheets/sections/nav.scss
+++ /dev/null
@@ -1,96 +0,0 @@
-.main-nav {
- background: #f5f5f5;
- margin: 20px 0;
- margin-top: 0;
- padding-top: 4px;
- border-bottom: 1px solid #E9E9E9;
-
- ul {
- padding: 0;
- margin: auto;
- .count {
- font-weight: normal;
- display: inline-block;
- height: 15px;
- padding: 1px 6px;
- height: auto;
- font-size: 0.82em;
- line-height: 14px;
- text-align: center;
- color: #777;
- background: #eee;
- @include border-radius(8px);
- }
- .label {
- background: $hover;
- text-shadow: none;
- color: $style_color;
- }
- li {
- list-style-type: none;
- margin: 0;
- display: table-cell;
- width: 1%;
- &.active {
- a {
- color: $link_color;
- font-weight: bold;
- border-bottom: 3px solid $link_color;
- }
- }
-
- &:hover {
- a {
- color: $link_hover_color;
- border-bottom: 3px solid $link_hover_color;
- }
- }
- }
- a {
- display: block;
- text-align: center;
- font-weight: bold;
- height: 42px;
- line-height: 39px;
- color: #777;
- text-shadow: 0 1px 1px white;
- text-decoration: none;
- overflow: hidden;
- margin-bottom: -1px;
- }
- }
-
- @media (max-width: $screen-xs-max) {
- font-size: 18px;
- margin: 0;
-
- max-height: none;
-
- &, .container {
- padding: 0;
- border-top: 0;
- }
-
- ul {
- height: auto;
-
- li {
- display: list-item;
- width: auto;
- padding: 5px 0;
-
- &.active {
- background-color: $link_hover_color;
-
- a {
- color: #fff;
- font-weight: normal;
- text-shadow: none;
-
- &:after { display: none; }
- }
- }
- }
- }
- }
-}
diff --git a/app/assets/stylesheets/sections/nav_sidebar.scss b/app/assets/stylesheets/sections/nav_sidebar.scss
new file mode 100644
index 00000000000..335f1379662
--- /dev/null
+++ b/app/assets/stylesheets/sections/nav_sidebar.scss
@@ -0,0 +1,187 @@
+.page-with-sidebar {
+ background: #F5F5F5;
+
+ .sidebar-wrapper {
+ position: fixed;
+ top: 0;
+ left: 0;
+ height: 100%;
+ border-right: 1px solid #EAEAEA;
+ }
+}
+
+.sidebar-wrapper {
+ z-index: 99;
+ background: #F5F5F5;
+}
+
+.content-wrapper {
+ width: 100%;
+ padding: 15px;
+ background: #FFF;
+}
+
+.nav-sidebar {
+ margin: 0;
+ list-style: none;
+
+ &.navbar-collapse {
+ padding: 0px !important;
+ }
+}
+
+.nav-sidebar li a .count {
+ float: right;
+ background: #eee;
+ padding: 0px 8px;
+ @include border-radius(6px);
+}
+
+.nav-sidebar li {
+ &.active a {
+ color: #333;
+ background: #FFF !important;
+ font-weight: bold;
+ border: 1px solid #EEE;
+ border-right: 1px solid transparent;
+ border-left: 3px solid $style_color;
+
+ &.no-highlight {
+ background: none !important;
+ border: none;
+ }
+
+ i {
+ color: #444;
+ }
+ }
+}
+
+.nav-sidebar li {
+ &.separate-item {
+ border-top: 1px solid #ddd;
+ padding-top: 10px;
+ margin-top: 10px;
+ }
+
+ a {
+ color: #555;
+ display: block;
+ text-decoration: none;
+ padding: 8px 15px;
+ font-size: 13px;
+ line-height: 20px;
+ text-shadow: 0 1px 2px #FFF;
+ padding-left: 20px;
+
+ &:hover {
+ text-decoration: none;
+ color: #333;
+ background: #EEE;
+ }
+
+ &:active, &:focus {
+ text-decoration: none;
+ }
+
+ i {
+ width: 20px;
+ color: #888;
+ margin-right: 23px;
+ }
+ }
+}
+
+.sidebar-subnav {
+ margin-left: 0px;
+ padding-left: 0px;
+
+ li {
+ list-style: none;
+ }
+}
+
+@mixin expanded-sidebar {
+ padding-left: $sidebar_width;
+
+ .sidebar-wrapper {
+ width: $sidebar_width;
+
+ .nav-sidebar {
+ margin-top: 29px;
+ position: fixed;
+ top: 45px;
+ width: $sidebar_width;
+ }
+ }
+
+ .content-wrapper {
+ padding: 20px;
+ }
+}
+
+@mixin folded-sidebar {
+ padding-left: 50px;
+
+ .sidebar-wrapper {
+ width: 52px;
+
+ .nav-sidebar {
+ margin-top: 29px;
+ position: fixed;
+ top: 45px;
+ width: 52px;
+
+ li a {
+ padding-left: 18px;
+ font-size: 14px;
+ padding: 8px 15px;
+ text-align: center;
+
+
+ & > span {
+ display: none;
+ }
+ }
+ }
+
+ .collapse-nav a {
+ left: 0px;
+ padding: 5px 23px 3px 22px;
+ }
+ }
+}
+
+.collapse-nav a {
+ position: fixed;
+ top: 47px;
+ padding: 5px 13px 3px 13px;
+ left: 197px;
+ background: #EEE;
+ color: black;
+ border: 1px solid rgba(0,0,0,0.035);
+}
+
+@media (max-width: $screen-md-max) {
+ .page-sidebar-collapsed {
+ @include folded-sidebar;
+ }
+
+ .page-sidebar-expanded {
+ @include folded-sidebar;
+ }
+
+ .collapse-nav {
+ display: none;
+ }
+}
+
+@media(min-width: $screen-md-max) {
+ .page-sidebar-collapsed {
+ @include folded-sidebar;
+ }
+
+ .page-sidebar-expanded {
+ @include expanded-sidebar;
+ }
+}
diff --git a/app/assets/stylesheets/sections/note_form.scss b/app/assets/stylesheets/sections/note_form.scss
new file mode 100644
index 00000000000..a0522030785
--- /dev/null
+++ b/app/assets/stylesheets/sections/note_form.scss
@@ -0,0 +1,175 @@
+/**
+ * Note Form
+ */
+
+.comment-btn {
+ @extend .btn-create;
+}
+.reply-btn {
+ @extend .btn-primary;
+}
+.diff-file .diff-content {
+ tr.line_holder:hover {
+ &> td.line_content {
+ background: $hover !important;
+ border-color: darken($hover, 10%) !important;
+ }
+ &> td.new_line,
+ &> td.old_line {
+ background: darken($hover, 4%) !important;
+ border-color: darken($hover, 10%) !important;
+ }
+ }
+
+ tr.line_holder:hover > td .line_note_link {
+ opacity: 1.0;
+ filter: alpha(opacity=100);
+ }
+}
+.diff-file,
+.discussion {
+ .new_note {
+ margin: 0;
+ border: none;
+ }
+}
+.new_note {
+ display: none;
+}
+
+.new_note, .edit_note {
+ .buttons {
+ float: left;
+ margin-top: 8px;
+ }
+ .clearfix {
+ margin-bottom: 0;
+ }
+
+ .note-preview-holder {
+ > p {
+ overflow-x: auto;
+ }
+ }
+
+ img {
+ max-width: 100%;
+ }
+
+ .note_text {
+ width: 100%;
+ }
+}
+
+/* loading indicator */
+.notes-busy {
+ margin: 18px;
+}
+
+.note-image-attach {
+ @extend .col-md-4;
+ @extend .thumbnail;
+ margin-left: 45px;
+ float: none;
+}
+
+.common-note-form {
+ margin: 0;
+ background: #F9F9F9;
+ padding: 5px;
+ border: 1px solid #DDD;
+}
+
+.note-form-actions {
+ background: #F9F9F9;
+ height: 45px;
+
+ .note-form-option {
+ margin-top: 8px;
+ margin-left: 30px;
+ @extend .pull-left;
+ }
+
+ .js-notify-commit-author {
+ float: left;
+ }
+
+ .write-preview-btn {
+ // makes the "absolute" position for links relative to this
+ position: relative;
+
+ // preview/edit buttons
+ > a {
+ position: absolute;
+ right: 5px;
+ top: 8px;
+ }
+ }
+}
+
+.note-edit-form {
+ display: none;
+ font-size: 13px;
+
+ .form-actions {
+ padding-left: 20px;
+
+ .btn-save {
+ float: left;
+ }
+
+ .note-form-option {
+ float: left;
+ padding: 2px 0 0 25px;
+ }
+ }
+}
+
+.js-note-attachment-delete {
+ display: none;
+}
+
+.parallel-comment {
+ padding: 6px;
+}
+
+.error-alert > .alert {
+ margin-top: 5px;
+ margin-bottom: 5px;
+}
+
+.discussion-body,
+.diff-file {
+ .notes .note {
+ border-color: #ddd;
+ padding: 10px 15px;
+ }
+
+ .discussion-reply-holder {
+ background: #f9f9f9;
+ padding: 10px 15px;
+ border-top: 1px solid #DDD;
+ }
+}
+
+.discussion-notes-count {
+ font-size: 16px;
+}
+
+.edit_note {
+ .markdown-area {
+ min-height: 140px;
+ }
+ .note-form-actions {
+ background: transparent;
+ }
+}
+
+.comment-hints {
+ color: #999;
+ background: #FFF;
+ padding: 5px;
+ margin-top: -11px;
+ border: 1px solid #DDD;
+ font-size: 13px;
+}
diff --git a/app/assets/stylesheets/sections/notes.scss b/app/assets/stylesheets/sections/notes.scss
index 7eb42fddade..40adc8b3ba7 100644
--- a/app/assets/stylesheets/sections/notes.scss
+++ b/app/assets/stylesheets/sections/notes.scss
@@ -36,13 +36,16 @@ ul.notes {
font-size: 13px;
}
.author {
- color: #555;
+ color: #333;
font-weight: bold;
font-size: 14px;
&:hover {
- color: $link_hover_color;
+ color: $link_color;
}
}
+ .author-username {
+ font-size: 14px;
+ }
}
.discussion {
@@ -58,7 +61,28 @@ ul.notes {
font-size: 14px;
}
.note-body {
- @include md-typography;
+ overflow: auto;
+ .note-text {
+ overflow: auto;
+ word-wrap: break-word;
+ @include md-typography;
+
+ a[href*="/uploads/"] {
+ &:before {
+ margin-right: 4px;
+
+ font: normal normal normal 14px/1 FontAwesome;
+ font-size: inherit;
+ text-rendering: auto;
+ -webkit-font-smoothing: antialiased;
+ content: "\f0c6";
+ }
+
+ &:hover:before {
+ text-decoration: none;
+ }
+ }
+ }
}
.note-header {
padding-bottom: 3px;
@@ -73,7 +97,7 @@ ul.notes {
.diff-file .notes_holder {
font-size: 13px;
line-height: 18px;
- font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
+ font-family: $regular_font;
td {
border: 1px solid #ddd;
@@ -152,19 +176,26 @@ ul.notes {
}
.add-diff-note {
- background: image-url("diff_note_add.png") no-repeat left 0;
- border: none;
- height: 22px;
- margin-left: -65px;
+ margin-top: -4px;
+ @include border-radius(40px);
+ background: #FFF;
+ padding: 4px;
+ font-size: 16px;
+ color: $link_color;
+ margin-left: -60px;
position: absolute;
- width: 22px;
z-index: 10;
+ transition: all 0.2s ease;
+
// "hide" it by default
opacity: 0.0;
filter: alpha(opacity=0);
&:hover {
+ font-size: 24px;
+ background: $bg_primary;
+ color: #FFF;
@include show-add-diff-note;
}
}
@@ -179,179 +210,3 @@ ul.notes {
}
}
-/**
- * Note Form
- */
-
-.comment-btn {
- @extend .btn-create;
-}
-.reply-btn {
- @extend .btn-primary;
-}
-.diff-file .diff-content {
- tr.line_holder:hover {
- &> td.line_content {
- background: $hover !important;
- border-color: darken($hover, 10%) !important;
- }
- &> td.new_line,
- &> td.old_line {
- background: darken($hover, 4%) !important;
- border-color: darken($hover, 10%) !important;
- }
- }
-
- tr.line_holder:hover > td .line_note_link {
- opacity: 1.0;
- filter: alpha(opacity=100);
- }
-}
-.diff-file,
-.discussion {
- .new_note {
- margin: 0;
- border: none;
- }
-}
-.new_note {
- display: none;
- .buttons {
- float: left;
- margin-top: 8px;
- }
- .clearfix {
- margin-bottom: 0;
- }
-
- .note-preview-holder,
- .note_text {
- background: #FFF;
- border: 1px solid #ddd;
- min-height: 100px;
- padding: 5px;
- font-size: 14px;
- box-shadow: none;
- }
-
- .note-preview-holder {
- > p {
- overflow-x: auto;
- }
- }
-
- .note_text {
- width: 100%;
- }
- .nav-tabs {
- margin-bottom: 0;
- border: none;
-
- li a,
- li.active a {
- border: 1px solid #DDD;
- }
- }
-}
-
-/* loading indicator */
-.notes-busy {
- margin: 18px;
-}
-
-.note-image-attach {
- @extend .col-md-4;
- @extend .thumbnail;
- margin-left: 45px;
- float: none;
-}
-
-.common-note-form {
- margin: 0;
- background: #F9F9F9;
- padding: 5px;
- border: 1px solid #DDD;
-}
-
-.note-form-actions {
- background: #F9F9F9;
- height: 45px;
-
- .note-form-option {
- margin-top: 8px;
- margin-left: 30px;
- @extend .pull-left;
- }
-
- .js-notify-commit-author {
- float: left;
- }
-
- .write-preview-btn {
- // makes the "absolute" position for links relative to this
- position: relative;
-
- // preview/edit buttons
- > a {
- position: absolute;
- right: 5px;
- top: 8px;
- }
- }
-}
-
-.note-edit-form {
- display: none;
-
- .note_text {
- border: 1px solid #DDD;
- box-shadow: none;
- font-size: 14px;
- height: 80px;
- width: 100%;
- }
-
- .form-actions {
- padding-left: 20px;
-
- .btn-save {
- float: left;
- }
-
- .note-form-option {
- float: left;
- padding: 2px 0 0 25px;
- }
- }
-}
-
-.js-note-attachment-delete {
- display: none;
-}
-
-.parallel-comment {
- padding: 6px;
-}
-
-.error-alert > .alert {
- margin-top: 5px;
- margin-bottom: 5px;
-}
-
-.discussion-body,
-.diff-file {
- .notes .note {
- border-color: #ddd;
- padding: 10px 15px;
- }
-
- .discussion-reply-holder {
- background: #f9f9f9;
- padding: 10px 15px;
- border-top: 1px solid #DDD;
- }
-}
-
-.discussion-notes-count {
- font-size: 16px;
-}
diff --git a/app/assets/stylesheets/sections/profile.scss b/app/assets/stylesheets/sections/profile.scss
index b9f4e317e9c..0ab62b7ae49 100644
--- a/app/assets/stylesheets/sections/profile.scss
+++ b/app/assets/stylesheets/sections/profile.scss
@@ -102,29 +102,3 @@
}
}
}
-
-.profile-groups-avatars {
- margin: 0 5px 10px 0;
-
- img {
- width: 50px;
- height: 50px;
- }
-}
-
-//CSS for password-strength indicator
-#password-strength {
- margin-bottom: 0;
-}
-
-.has-success input {
- background-color: #D6F1D7 !important;
-}
-
-.has-error input {
- background-color: #F3CECE !important;
-}
-
-.has-warning input {
- background-color: #FFE9A4 !important;
-}
diff --git a/app/assets/stylesheets/sections/projects.scss b/app/assets/stylesheets/sections/projects.scss
index b4ee5ccc8d7..8bad9b139f4 100644
--- a/app/assets/stylesheets/sections/projects.scss
+++ b/app/assets/stylesheets/sections/projects.scss
@@ -16,6 +16,8 @@
.project-home-panel {
margin-bottom: 15px;
+ position: relative;
+ padding-left: 85px;
&.empty-project {
border-bottom: 0px;
@@ -23,6 +25,21 @@
margin-bottom: 0px;
}
+ .project-identicon-holder {
+ position: absolute;
+ left: 0;
+
+ .avatar {
+ width: 70px;
+ height: 70px;
+ }
+
+ .identicon {
+ font-size: 45px;
+ line-height: 1.6;
+ }
+ }
+
.project-home-dropdown {
margin-left: 10px;
float: right;
@@ -94,6 +111,8 @@
color: $link_color;
&.active {
+ background-color: #f5f5f5;
+ border: 1px solid rgba(0,0,0,0.195);
color: #333;
font-weight: bold;
}
@@ -270,3 +289,34 @@ ul.nav.nav-projects-tabs {
color: #999;
}
}
+
+.fork-namespaces {
+ .thumbnail {
+
+ &.fork-exists-thumbnail {
+ border-color: #EEE;
+
+ .caption {
+ color: #999;
+ }
+ }
+
+ &.fork-thumbnail {
+ border-color: #AAA;
+
+ &:hover {
+ background-color: $hover;
+ }
+ }
+
+ a {
+ text-decoration: none;
+ }
+ }
+}
+
+table.table.protected-branches-list tr.no-border {
+ th, td {
+ border: 0;
+ }
+}
diff --git a/app/assets/stylesheets/sections/tree.scss b/app/assets/stylesheets/sections/tree.scss
index 678a6cd716d..60a1c00b04b 100644
--- a/app/assets/stylesheets/sections/tree.scss
+++ b/app/assets/stylesheets/sections/tree.scss
@@ -17,19 +17,6 @@
@include border-radius(0);
tr {
- td, th {
- padding: 8px 10px;
- line-height: 20px;
- }
- th {
- font-weight: normal;
- font-size: 15px;
- border-bottom: 1px solid #CCC !important;
- }
- td {
- border-color: #F1F1F1 !important;
- border-bottom: 1px solid;
- }
&:hover {
td {
background: $hover;
@@ -111,6 +98,11 @@
background: #f1f1f1;
border-left: 1px solid #DDD;
}
+ td.lines {
+ code {
+ font-family: $monospace_font;
+ }
+ }
}
}
@@ -128,13 +120,13 @@
}
.readme-holder {
- border-top: 1px dashed #CCC;
- padding-top: 10px;
-
.readme-file-title {
font-size: 14px;
+ font-weight: bold;
margin-bottom: 20px;
color: #777;
+ border-bottom: 1px solid #DDD;
+ padding: 10px 0;
}
}
diff --git a/app/assets/stylesheets/sections/votes.scss b/app/assets/stylesheets/sections/votes.scss
index d683e33e1f0..ba0a519dca6 100644
--- a/app/assets/stylesheets/sections/votes.scss
+++ b/app/assets/stylesheets/sections/votes.scss
@@ -37,13 +37,3 @@
margin: 0 8px;
}
-.votes-holder {
- float: right;
- width: 250px;
-
- @media (max-width: $screen-xs-max) {
- width: 100%;
- margin-top: 5px;
- margin-bottom: 10px;
- }
-}
diff --git a/app/assets/stylesheets/themes/dark-theme.scss b/app/assets/stylesheets/themes/dark-theme.scss
new file mode 100644
index 00000000000..b7b22a8724e
--- /dev/null
+++ b/app/assets/stylesheets/themes/dark-theme.scss
@@ -0,0 +1,63 @@
+@mixin dark-theme($color-light, $color, $color-darker, $color-dark) {
+ header {
+ &.navbar-gitlab {
+ .navbar-inner {
+ background: $color;
+
+ .navbar-toggle {
+ color: #FFF;
+ }
+
+ .app_logo, .navbar-toggle {
+ &:hover {
+ background-color: $color-darker;
+ }
+ }
+
+ .app_logo {
+ background-color: $color-dark;
+ }
+
+ .title {
+ color: #FFF;
+
+ a {
+ color: #FFF;
+ &:hover {
+ text-decoration: underline;
+ }
+ }
+ }
+
+ .search {
+ .search-input {
+ background-color: $color-light;
+ background-color: rgba(255, 255, 255, 0.5);
+ border: 1px solid $color-light;
+
+ &:focus {
+ background-color: white;
+ }
+ }
+ }
+
+ .search-input::-webkit-input-placeholder {
+ color: #666;
+ }
+
+ .nav > li > a {
+ color: $color-light;
+
+ &:hover, &:focus, &:active {
+ background: none;
+ color: #FFF;
+ }
+ }
+
+ .search-input {
+ border-color: $color-light;
+ }
+ }
+ }
+ }
+}
diff --git a/app/assets/stylesheets/themes/ui_basic.scss b/app/assets/stylesheets/themes/ui_basic.scss
index 3e3744fdc33..097d5c5b73c 100644
--- a/app/assets/stylesheets/themes/ui_basic.scss
+++ b/app/assets/stylesheets/themes/ui_basic.scss
@@ -9,17 +9,22 @@
.navbar-inner {
background: #F1F1F1;
border-bottom: 1px solid #DDD;
+
+ .title {
+ color: #555;
+
+ a {
+ color: #555;
+ &:hover {
+ text-decoration: underline;
+ }
+ }
+ }
+
.nav > li > a {
color: $style_color;
}
- .separator {
- background: #F9F9F9;
- border-left: 1px solid #DDD;
- }
}
}
}
- .main-nav {
- background: #FFF;
- }
}
diff --git a/app/assets/stylesheets/themes/ui_color.scss b/app/assets/stylesheets/themes/ui_color.scss
index a08f3ff3d48..7ac6903b2e4 100644
--- a/app/assets/stylesheets/themes/ui_color.scss
+++ b/app/assets/stylesheets/themes/ui_color.scss
@@ -1,43 +1,6 @@
/**
- * This file represent some UI that can be changed
- * during web app restyle or theme select.
- *
- * Next items should be placed there
- * - link colors
- * - header restyles
- *
+ * Violet GitLab UI theme
*/
.ui_color {
- /*
- * Application Header
- *
- */
- header {
- @extend .header-dark;
- &.navbar-gitlab {
- .navbar-inner {
- background: #548;
- border-bottom: 1px solid #436;
- .app_logo, .navbar-toggle {
- &:hover {
- background-color: #436;
- }
- }
- .separator {
- background: #436;
- border-left: 1px solid #659;
- }
- .nav > li > a {
- color: #98C;
- }
- .search-input {
- border-color: #98C;
- }
- }
- }
- }
-
- .nav-pills > li.active > a, .nav-pills > li.active > a:hover, .nav-pills > li.active > a:focus {
- background: #659;
- }
+ @include dark-theme(#98C, #548, #436, #325);
}
diff --git a/app/assets/stylesheets/themes/ui_gray.scss b/app/assets/stylesheets/themes/ui_gray.scss
index 959febad6fe..9257e5f4d40 100644
--- a/app/assets/stylesheets/themes/ui_gray.scss
+++ b/app/assets/stylesheets/themes/ui_gray.scss
@@ -1,33 +1,6 @@
/**
- * This file represent some UI that can be changed
- * during web app restyle or theme select.
- *
- * Next items should be placed there
- * - link colors
- * - header restyles
- *
+ * Gray GitLab UI theme
*/
.ui_gray {
- /*
- * Application Header
- *
- */
- header {
- @extend .header-dark;
- &.navbar-gitlab {
- .navbar-inner {
- background: #373737;
- border-bottom: 1px solid #272727;
- .app_logo, .navbar-toggle {
- &:hover {
- background-color: #272727;
- }
- }
- .separator {
- background: #272727;
- border-left: 1px solid #474747;
- }
- }
- }
- }
+ @include dark-theme(#979797, #373737, #272727, #222222);
}
diff --git a/app/assets/stylesheets/themes/ui_mars.scss b/app/assets/stylesheets/themes/ui_mars.scss
index 9af5adbf10a..4caf5843d9b 100644
--- a/app/assets/stylesheets/themes/ui_mars.scss
+++ b/app/assets/stylesheets/themes/ui_mars.scss
@@ -1,39 +1,6 @@
/**
- * This file represent some UI that can be changed
- * during web app restyle or theme select.
- *
- * Next items should be placed there
- * - link colors
- * - header restyles
- *
+ * Classic GitLab UI theme
*/
.ui_mars {
- /*
- * Application Header
- *
- */
- header {
- @extend .header-dark;
- &.navbar-gitlab {
- .navbar-inner {
- background: #474D57;
- border-bottom: 1px solid #373D47;
- .app_logo, .navbar-toggle {
- &:hover {
- background-color: #373D47;
- }
- }
- .separator {
- background: #373D47;
- border-left: 1px solid #575D67;
- }
- .nav > li > a {
- color: #979DA7;
- }
- .search-input {
- border-color: #979DA7;
- }
- }
- }
- }
+ @include dark-theme(#979DA7, #474D57, #373D47, #24272D);
}
diff --git a/app/assets/stylesheets/themes/ui_modern.scss b/app/assets/stylesheets/themes/ui_modern.scss
index 308a03477db..70449882317 100644
--- a/app/assets/stylesheets/themes/ui_modern.scss
+++ b/app/assets/stylesheets/themes/ui_modern.scss
@@ -1,43 +1,6 @@
/**
- * This file represent some UI that can be changed
- * during web app restyle or theme select.
- *
- * Next items should be placed there
- * - link colors
- * - header restyles
- *
+ * Modern GitLab UI theme
*/
.ui_modern {
- /*
- * Application Header
- *
- */
- header {
- @extend .header-dark;
- &.navbar-gitlab {
- .navbar-inner {
- background: #019875;
- border-bottom: 1px solid #019875;
- .app_logo, .navbar-toggle {
- &:hover {
- background-color: #018865;
- }
- }
- .separator {
- background: #018865;
- border-left: 1px solid #11A885;
- }
- .nav > li > a {
- color: #ADC;
- }
- .search-input {
- border-color: #8ba;
- }
- }
- }
- }
-
- .nav-pills > li.active > a, .nav-pills > li.active > a:hover, .nav-pills > li.active > a:focus {
- background: #019875;
- }
+ @include dark-theme(#ADC, #019875, #018865, #017855);
}
diff --git a/app/controllers/admin/application_settings_controller.rb b/app/controllers/admin/application_settings_controller.rb
new file mode 100644
index 00000000000..2b0c500e97a
--- /dev/null
+++ b/app/controllers/admin/application_settings_controller.rb
@@ -0,0 +1,34 @@
+class Admin::ApplicationSettingsController < Admin::ApplicationController
+ before_filter :set_application_setting
+
+ def show
+ end
+
+ def update
+ if @application_setting.update_attributes(application_setting_params)
+ redirect_to admin_application_settings_path,
+ notice: 'Application settings saved successfully'
+ else
+ render :show
+ end
+ end
+
+ private
+
+ def set_application_setting
+ @application_setting = ApplicationSetting.current
+ end
+
+ def application_setting_params
+ params.require(:application_setting).permit(
+ :default_projects_limit,
+ :default_branch_protection,
+ :signup_enabled,
+ :signin_enabled,
+ :gravatar_enabled,
+ :twitter_sharing_enabled,
+ :sign_in_text,
+ :home_page_url
+ )
+ end
+end
diff --git a/app/controllers/admin/applications_controller.rb b/app/controllers/admin/applications_controller.rb
new file mode 100644
index 00000000000..471d24934a0
--- /dev/null
+++ b/app/controllers/admin/applications_controller.rb
@@ -0,0 +1,52 @@
+class Admin::ApplicationsController < Admin::ApplicationController
+ before_action :set_application, only: [:show, :edit, :update, :destroy]
+
+ def index
+ @applications = Doorkeeper::Application.where("owner_id IS NULL")
+ end
+
+ def show
+ end
+
+ def new
+ @application = Doorkeeper::Application.new
+ end
+
+ def edit
+ end
+
+ def create
+ @application = Doorkeeper::Application.new(application_params)
+
+ if @application.save
+ flash[:notice] = I18n.t(:notice, scope: [:doorkeeper, :flash, :applications, :create])
+ redirect_to admin_application_url(@application)
+ else
+ render :new
+ end
+ end
+
+ def update
+ if @application.update(application_params)
+ redirect_to admin_application_path(@application), notice: 'Application was successfully updated.'
+ else
+ render :edit
+ end
+ end
+
+ def destroy
+ @application.destroy
+ redirect_to admin_applications_url, notice: 'Application was successfully destroyed.'
+ end
+
+ private
+
+ def set_application
+ @application = Doorkeeper::Application.where("owner_id IS NULL").find(params[:id])
+ end
+
+ # Only allow a trusted parameter "white list" through.
+ def application_params
+ params[:doorkeeper_application].permit(:name, :redirect_uri)
+ end
+end
diff --git a/app/controllers/admin/dashboard_controller.rb b/app/controllers/admin/dashboard_controller.rb
index be19139c9b1..c491e5c7550 100644
--- a/app/controllers/admin/dashboard_controller.rb
+++ b/app/controllers/admin/dashboard_controller.rb
@@ -1,7 +1,7 @@
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)
+ @projects = Project.limit(10)
+ @users = User.limit(10)
+ @groups = Group.limit(10)
end
end
diff --git a/app/controllers/admin/groups_controller.rb b/app/controllers/admin/groups_controller.rb
index e6d0c9323c1..65dc027c8eb 100644
--- a/app/controllers/admin/groups_controller.rb
+++ b/app/controllers/admin/groups_controller.rb
@@ -2,7 +2,8 @@ class Admin::GroupsController < Admin::ApplicationController
before_filter :group, only: [:edit, :show, :update, :destroy, :project_update, :project_teams_update]
def index
- @groups = Group.order('name ASC')
+ @groups = Group.all
+ @groups = @groups.sort(@sort = params[:sort])
@groups = @groups.search(params[:name]) if params[:name].present?
@groups = @groups.page(params[:page]).per(20)
end
@@ -21,7 +22,7 @@ class Admin::GroupsController < Admin::ApplicationController
def create
@group = Group.new(group_params)
- @group.path = @group.name.dup.parameterize if @group.name
+ @group.name = @group.path.dup unless @group.name
if @group.save
@group.add_owner(current_user)
diff --git a/app/controllers/admin/keys_controller.rb b/app/controllers/admin/keys_controller.rb
new file mode 100644
index 00000000000..21111bb44f5
--- /dev/null
+++ b/app/controllers/admin/keys_controller.rb
@@ -0,0 +1,34 @@
+class Admin::KeysController < Admin::ApplicationController
+ before_filter :user, only: [:show, :destroy]
+
+ def show
+ @key = user.keys.find(params[:id])
+
+ respond_to do |format|
+ format.html
+ format.js { render nothing: true }
+ end
+ end
+
+ def destroy
+ key = user.keys.find(params[:id])
+
+ respond_to do |format|
+ if key.destroy
+ format.html { redirect_to [:admin, user], notice: 'User key was successfully removed.' }
+ else
+ format.html { redirect_to [:admin, user], alert: 'Failed to remove user key.' }
+ end
+ end
+ end
+
+ protected
+
+ def user
+ @user ||= User.find_by!(username: params[:user_id])
+ end
+
+ def key_params
+ params.require(:user_id, :id)
+ end
+end
diff --git a/app/controllers/admin/projects_controller.rb b/app/controllers/admin/projects_controller.rb
index 7c2388e81be..2b1fc862b7f 100644
--- a/app/controllers/admin/projects_controller.rb
+++ b/app/controllers/admin/projects_controller.rb
@@ -25,13 +25,16 @@ class Admin::ProjectsController < Admin::ApplicationController
def transfer
::Projects::TransferService.new(@project, current_user, params.dup).execute
- redirect_to [:admin, @project.reload]
+ @project.reload
+ redirect_to admin_namespace_project_path(@project.namespace, @project)
end
protected
def project
- @project = Project.find_with_namespace(params[:id])
+ @project = Project.find_with_namespace(
+ [params[:namespace_id], '/', params[:id]].join('')
+ )
@project || render_404
end
diff --git a/app/controllers/admin/services_controller.rb b/app/controllers/admin/services_controller.rb
new file mode 100644
index 00000000000..e80cabd6e18
--- /dev/null
+++ b/app/controllers/admin/services_controller.rb
@@ -0,0 +1,51 @@
+class Admin::ServicesController < Admin::ApplicationController
+ before_filter :service, only: [:edit, :update]
+
+ def index
+ @services = services_templates
+ end
+
+ def edit
+ unless service.present?
+ redirect_to admin_application_settings_services_path,
+ alert: "Service is unknown or it doesn't exist"
+ end
+ end
+
+ def update
+ if service.update_attributes(application_services_params[:service])
+ redirect_to admin_application_settings_services_path,
+ notice: 'Application settings saved successfully'
+ else
+ render :edit
+ end
+ end
+
+ private
+
+ def services_templates
+ templates = []
+
+ Service.available_services_names.each do |service_name|
+ service_template = service_name.concat("_service").camelize.constantize
+ templates << service_template.where(template: true).first_or_create
+ end
+
+ templates
+ end
+
+ def service
+ @service ||= Service.where(id: params[:id], template: true).first
+ end
+
+ def application_services_params
+ params.permit(:id,
+ service: [
+ :title, :token, :type, :active, :api_key, :subdomain,
+ :room, :recipients, :project_url, :webhook,
+ :user_key, :device, :priority, :sound, :bamboo_url, :username, :password,
+ :build_key, :server, :teamcity_url, :build_type,
+ :description, :issues_url, :new_issue_url, :restrict_to_branch
+ ])
+ end
+end
diff --git a/app/controllers/admin/users_controller.rb b/app/controllers/admin/users_controller.rb
index baad9095b70..ecedb31a7f8 100644
--- a/app/controllers/admin/users_controller.rb
+++ b/app/controllers/admin/users_controller.rb
@@ -2,15 +2,16 @@ class Admin::UsersController < Admin::ApplicationController
before_filter :user, only: [:show, :edit, :update, :destroy]
def index
- @users = User.filter(params[:filter])
+ @users = User.order_name_asc.filter(params[:filter])
@users = @users.search(params[:name]) if params[:name].present?
@users = @users.sort(@sort = params[:sort])
- @users = @users.alphabetically.page(params[:page])
+ @users = @users.page(params[:page])
end
def show
@personal_projects = user.personal_projects
@joined_projects = user.projects.joined(@user)
+ @keys = user.keys
end
def new
@@ -101,6 +102,9 @@ class Admin::UsersController < Admin::ApplicationController
email = user.emails.find(params[:email_id])
email.destroy
+ user.set_notification_email
+ user.save if user.notification_email_changed?
+
respond_to do |format|
format.html { redirect_to :back, notice: "Successfully removed email." }
format.js { render nothing: true }
@@ -117,8 +121,8 @@ class Admin::UsersController < Admin::ApplicationController
params.require(:user).permit(
:email, :remember_me, :bio, :name, :username,
:skype, :linkedin, :twitter, :website_url, :color_scheme_id, :theme_id, :force_random_password,
- :extern_uid, :provider, :password_expires_at, :avatar, :hide_no_ssh_key,
- :projects_limit, :can_create_group, :admin
+ :extern_uid, :provider, :password_expires_at, :avatar, :hide_no_ssh_key, :hide_no_password,
+ :projects_limit, :can_create_group, :admin, :key_id
)
end
end
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index f1e1bebe5ce..df1a588313e 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -1,6 +1,9 @@
require 'gon'
class ApplicationController < ActionController::Base
+ include Gitlab::CurrentSettings
+ include GitlabRoutingHelper
+
before_filter :authenticate_user_from_token!
before_filter :authenticate_user!
before_filter :reject_blocked!
@@ -13,7 +16,8 @@ class ApplicationController < ActionController::Base
protect_from_forgery with: :exception
- helper_method :abilities, :can?
+ helper_method :abilities, :can?, :current_application_settings
+ helper_method :github_import_enabled?, :gitlab_import_enabled?, :bitbucket_import_enabled?
rescue_from Encoding::CompatibilityError do |exception|
log_exception(exception)
@@ -46,6 +50,17 @@ class ApplicationController < ActionController::Base
end
end
+ def authenticate_user!(*args)
+ # If user is not signed-in and tries to access root_path - redirect him to landing page
+ if current_application_settings.home_page_url.present?
+ if current_user.nil? && controller_name == 'dashboard' && action_name == 'show'
+ redirect_to current_application_settings.home_page_url and return
+ end
+ end
+
+ super(*args)
+ end
+
def log_exception(exception)
application_trace = ActionDispatch::ExceptionWrapper.new(env, exception).application_trace
application_trace.map!{ |t| " #{t}\n" }
@@ -80,6 +95,7 @@ class ApplicationController < ActionController::Base
def project
unless @project
+ namespace = params[:namespace_id]
id = params[:project_id] || params[:id]
# Redirect from
@@ -91,7 +107,7 @@ class ApplicationController < ActionController::Base
redirect_to request.original_url.gsub(/\.git\Z/, '') and return
end
- @project = Project.find_with_namespace(id)
+ @project = Project.find_with_namespace("#{namespace}/#{id}")
if @project and can?(current_user, :read_project, @project)
@project
@@ -108,7 +124,8 @@ class ApplicationController < ActionController::Base
def repository
@repository ||= project.repository
- rescue Grit::NoSuchPathError
+ rescue Grit::NoSuchPathError(e)
+ log_exception(e)
nil
end
@@ -168,7 +185,7 @@ class ApplicationController < ActionController::Base
end
def add_gon_variables
- gon.default_issues_tracker = Project.issues_tracker.default_value
+ gon.default_issues_tracker = Project.new.default_issue_tracker.to_param
gon.api_version = API::API.version
gon.relative_url_root = Gitlab.config.gitlab.relative_url_root
gon.default_avatar_url = URI::join(Gitlab.config.gitlab.url, ActionController::Base.helpers.image_path('no_avatar.png')).to_s
@@ -239,4 +256,75 @@ class ApplicationController < ActionController::Base
redirect_to profile_path, notice: 'Please complete your profile with email address' and return
end
end
+
+ def set_filters_params
+ params[:sort] ||= 'created_desc'
+ params[:scope] = 'all' if params[:scope].blank?
+ params[:state] = 'opened' if params[:state].blank?
+
+ @filter_params = params.dup
+
+ if @project
+ @filter_params[:project_id] = @project.id
+ elsif @group
+ @filter_params[:group_id] = @group.id
+ else
+ # TODO: this filter ignore issues/mr created in public or
+ # internal repos where you are not a member. Enable this filter
+ # or improve current implementation to filter only issues you
+ # created or assigned or mentioned
+ #@filter_params[:authorized_only] = true
+ end
+
+ @filter_params
+ end
+
+ def set_filter_values(collection)
+ assignee_id = @filter_params[:assignee_id]
+ author_id = @filter_params[:author_id]
+ milestone_id = @filter_params[:milestone_id]
+
+ @sort = @filter_params[:sort]
+ @assignees = User.where(id: collection.pluck(:assignee_id))
+ @authors = User.where(id: collection.pluck(:author_id))
+ @milestones = Milestone.where(id: collection.pluck(:milestone_id))
+
+ if assignee_id.present? && !assignee_id.to_i.zero?
+ @assignee = @assignees.find_by(id: assignee_id)
+ end
+
+ if author_id.present? && !author_id.to_i.zero?
+ @author = @authors.find_by(id: author_id)
+ end
+
+ if milestone_id.present? && !milestone_id.to_i.zero?
+ @milestone = @milestones.find_by(id: milestone_id)
+ end
+ end
+
+ def get_issues_collection
+ set_filters_params
+ issues = IssuesFinder.new.execute(current_user, @filter_params)
+ set_filter_values(issues)
+ issues
+ end
+
+ def get_merge_requests_collection
+ set_filters_params
+ merge_requests = MergeRequestsFinder.new.execute(current_user, @filter_params)
+ set_filter_values(merge_requests)
+ merge_requests
+ end
+
+ def github_import_enabled?
+ OauthHelper.enabled_oauth_providers.include?(:github)
+ end
+
+ def gitlab_import_enabled?
+ OauthHelper.enabled_oauth_providers.include?(:gitlab)
+ end
+
+ def bitbucket_import_enabled?
+ OauthHelper.enabled_oauth_providers.include?(:bitbucket) && Gitlab::BitbucketImport.public_key.present?
+ end
end
diff --git a/app/controllers/dashboard_controller.rb b/app/controllers/dashboard_controller.rb
index 5aff526d1b5..eca7b39bcdf 100644
--- a/app/controllers/dashboard_controller.rb
+++ b/app/controllers/dashboard_controller.rb
@@ -3,22 +3,16 @@ class DashboardController < ApplicationController
before_filter :load_projects, except: [:projects]
before_filter :event_filter, only: :show
- before_filter :default_filter, only: [:issues, :merge_requests]
-
def show
# Fetch only 30 projects.
# If user needs more - point to Dashboard#projects page
@projects_limit = 30
- @groups = current_user.authorized_groups.sort_by(&:human_name)
+ @groups = current_user.authorized_groups.order_name_asc
@has_authorized_projects = @projects.count > 0
@projects_count = @projects.count
- @projects = @projects.limit(@projects_limit)
-
- @events = Event.in_projects(current_user.authorized_projects.pluck(:id))
- @events = @event_filter.apply_filter(@events)
- @events = @events.limit(20).offset(params[:offset] || 0)
+ @projects = @projects.includes(:namespace).limit(@projects_limit)
@last_push = current_user.recent_push
@@ -26,8 +20,16 @@ class DashboardController < ApplicationController
respond_to do |format|
format.html
- format.json { pager_json("events/_events", @events.count) }
- format.atom { render layout: false }
+
+ format.json do
+ load_events
+ pager_json("events/_events", @events.count)
+ end
+
+ format.atom do
+ load_events
+ render layout: false
+ end
end
end
@@ -55,13 +57,13 @@ class DashboardController < ApplicationController
end
def merge_requests
- @merge_requests = MergeRequestsFinder.new.execute(current_user, params)
+ @merge_requests = get_merge_requests_collection
@merge_requests = @merge_requests.page(params[:page]).per(20)
@merge_requests = @merge_requests.preload(:author, :target_project)
end
def issues
- @issues = IssuesFinder.new.execute(current_user, params)
+ @issues = get_issues_collection
@issues = @issues.page(params[:page]).per(20)
@issues = @issues.preload(:author, :project)
@@ -77,9 +79,9 @@ class DashboardController < ApplicationController
@projects = current_user.authorized_projects.sorted_by_activity.non_archived
end
- def default_filter
- params[:scope] = 'assigned-to-me' if params[:scope].blank?
- params[:state] = 'opened' if params[:state].blank?
- params[:authorized_only] = true
+ def load_events
+ @events = Event.in_projects(current_user.authorized_projects.pluck(:id))
+ @events = @event_filter.apply_filter(@events).with_associations
+ @events = @events.limit(20).offset(params[:offset] || 0)
end
end
diff --git a/app/controllers/explore/projects_controller.rb b/app/controllers/explore/projects_controller.rb
index d75fd8e72fa..0e5891ae807 100644
--- a/app/controllers/explore/projects_controller.rb
+++ b/app/controllers/explore/projects_controller.rb
@@ -18,7 +18,7 @@ class Explore::ProjectsController < ApplicationController
def starred
@starred_projects = ProjectsFinder.new.execute(current_user)
- @starred_projects = @starred_projects.order('star_count DESC')
+ @starred_projects = @starred_projects.reorder('star_count DESC')
@starred_projects = @starred_projects.page(params[:page]).per(10)
end
end
diff --git a/app/controllers/files_controller.rb b/app/controllers/files_controller.rb
deleted file mode 100644
index 7937454810d..00000000000
--- a/app/controllers/files_controller.rb
+++ /dev/null
@@ -1,16 +0,0 @@
-class FilesController < ApplicationController
- def download
- note = Note.find(params[:id])
- uploader = note.attachment
-
- if uploader.file_storage?
- if can?(current_user, :read_project, note.project)
- send_file uploader.file.path, disposition: 'attachment'
- else
- not_found!
- end
- else
- redirect_to uploader.url
- end
- end
-end
diff --git a/app/controllers/groups_controller.rb b/app/controllers/groups_controller.rb
index 36222758eb2..d011523c94f 100644
--- a/app/controllers/groups_controller.rb
+++ b/app/controllers/groups_controller.rb
@@ -10,20 +10,18 @@ class GroupsController < ApplicationController
# Load group projects
before_filter :load_projects, except: [:new, :create, :projects, :edit, :update]
-
- before_filter :default_filter, only: [:issues, :merge_requests]
+ before_filter :event_filter, only: :show
+ before_filter :set_title, only: [:new, :create]
layout :determine_layout
- before_filter :set_title, only: [:new, :create]
-
def new
@group = Group.new
end
def create
@group = Group.new(group_params)
- @group.path = @group.name.dup.parameterize if @group.name
+ @group.name = @group.path.dup unless @group.name
if @group.save
@group.add_owner(current_user)
@@ -34,26 +32,32 @@ class GroupsController < ApplicationController
end
def show
- @events = Event.in_projects(project_ids)
- @events = event_filter.apply_filter(@events)
- @events = @events.limit(20).offset(params[:offset] || 0)
@last_push = current_user.recent_push if current_user
+ @projects = @projects.includes(:namespace)
respond_to do |format|
format.html
- format.json { pager_json("events/_events", @events.count) }
- format.atom { render layout: false }
+
+ format.json do
+ load_events
+ pager_json("events/_events", @events.count)
+ end
+
+ format.atom do
+ load_events
+ render layout: false
+ end
end
end
def merge_requests
- @merge_requests = MergeRequestsFinder.new.execute(current_user, params)
+ @merge_requests = get_merge_requests_collection
@merge_requests = @merge_requests.page(params[:page]).per(20)
@merge_requests = @merge_requests.preload(:author, :target_project)
end
def issues
- @issues = IssuesFinder.new.execute(current_user, params)
+ @issues = get_issues_collection
@issues = @issues.page(params[:page]).per(20)
@issues = @issues.preload(:author, :project)
@@ -148,19 +152,13 @@ class GroupsController < ApplicationController
end
end
- def default_filter
- if params[:scope].blank?
- if current_user
- params[:scope] = 'assigned-to-me'
- else
- params[:scope] = 'all'
- end
- end
- params[:state] = 'opened' if params[:state].blank?
- params[:group_id] = @group.id
- end
-
def group_params
params.require(:group).permit(:name, :description, :path, :avatar)
end
+
+ def load_events
+ @events = Event.in_projects(project_ids)
+ @events = event_filter.apply_filter(@events).with_associations
+ @events = @events.limit(20).offset(params[:offset] || 0)
+ end
end
diff --git a/app/controllers/import/base_controller.rb b/app/controllers/import/base_controller.rb
new file mode 100644
index 00000000000..4df171dbcfe
--- /dev/null
+++ b/app/controllers/import/base_controller.rb
@@ -0,0 +1,21 @@
+class Import::BaseController < ApplicationController
+
+ private
+
+ def get_or_create_namespace
+ existing_namespace = Namespace.find_by("path = ? OR name = ?", @target_namespace, @target_namespace)
+
+ if existing_namespace
+ if existing_namespace.owner == current_user
+ namespace = existing_namespace
+ else
+ @already_been_taken = true
+ return false
+ end
+ else
+ namespace = Group.create(name: @target_namespace, path: @target_namespace, owner: current_user)
+ namespace.add_owner(current_user)
+ namespace
+ end
+ end
+end
diff --git a/app/controllers/import/bitbucket_controller.rb b/app/controllers/import/bitbucket_controller.rb
new file mode 100644
index 00000000000..83ebc5fddca
--- /dev/null
+++ b/app/controllers/import/bitbucket_controller.rb
@@ -0,0 +1,79 @@
+class Import::BitbucketController < Import::BaseController
+ before_filter :verify_bitbucket_import_enabled
+ before_filter :bitbucket_auth, except: :callback
+
+ rescue_from OAuth::Error, with: :bitbucket_unauthorized
+
+ def callback
+ request_token = session.delete(:oauth_request_token)
+ raise "Session expired!" if request_token.nil?
+
+ request_token.symbolize_keys!
+
+ access_token = client.get_token(request_token, params[:oauth_verifier], callback_import_bitbucket_url)
+
+ current_user.bitbucket_access_token = access_token.token
+ current_user.bitbucket_access_token_secret = access_token.secret
+
+ current_user.save
+ redirect_to status_import_bitbucket_url
+ end
+
+ def status
+ @repos = client.projects
+
+ @already_added_projects = current_user.created_projects.where(import_type: "bitbucket")
+ already_added_projects_names = @already_added_projects.pluck(:import_source)
+
+ @repos.to_a.reject!{ |repo| already_added_projects_names.include? "#{repo["owner"]}/#{repo["slug"]}" }
+ end
+
+ def jobs
+ jobs = current_user.created_projects.where(import_type: "bitbucket").to_json(only: [:id, :import_status])
+ render json: jobs
+ end
+
+ def create
+ @repo_id = params[:repo_id] || ""
+ repo = client.project(@repo_id.gsub("___", "/"))
+ @target_namespace = params[:new_namespace].presence || repo["owner"]
+ @project_name = repo["slug"]
+
+ namespace = get_or_create_namespace || (render and return)
+
+ unless Gitlab::BitbucketImport::KeyAdder.new(repo, current_user).execute
+ @access_denied = true
+ render
+ return
+ end
+
+ @project = Gitlab::BitbucketImport::ProjectCreator.new(repo, namespace, current_user).execute
+ end
+
+ private
+
+ def client
+ @client ||= Gitlab::BitbucketImport::Client.new(current_user.bitbucket_access_token, current_user.bitbucket_access_token_secret)
+ end
+
+ def verify_bitbucket_import_enabled
+ not_found! unless bitbucket_import_enabled?
+ end
+
+ def bitbucket_auth
+ if current_user.bitbucket_access_token.blank?
+ go_to_bitbucket_for_permissions
+ end
+ end
+
+ def go_to_bitbucket_for_permissions
+ request_token = client.request_token(callback_import_bitbucket_url)
+ session[:oauth_request_token] = request_token
+
+ redirect_to client.authorize_url(request_token, callback_import_bitbucket_url)
+ end
+
+ def bitbucket_unauthorized
+ go_to_bitbucket_for_permissions
+ end
+end
diff --git a/app/controllers/import/github_controller.rb b/app/controllers/import/github_controller.rb
new file mode 100644
index 00000000000..dc7668ee6fd
--- /dev/null
+++ b/app/controllers/import/github_controller.rb
@@ -0,0 +1,65 @@
+class Import::GithubController < Import::BaseController
+ before_filter :verify_github_import_enabled
+ before_filter :github_auth, except: :callback
+
+ rescue_from Octokit::Unauthorized, with: :github_unauthorized
+
+ def callback
+ token = client.get_token(params[:code])
+ current_user.github_access_token = token
+ current_user.save
+ redirect_to status_import_github_url
+ end
+
+ def status
+ @repos = client.repos
+ client.orgs.each do |org|
+ @repos += client.repos(org.login)
+ end
+
+ @already_added_projects = current_user.created_projects.where(import_type: "github")
+ already_added_projects_names = @already_added_projects.pluck(:import_source)
+
+ @repos.reject!{ |repo| already_added_projects_names.include? repo.full_name }
+ end
+
+ def jobs
+ jobs = current_user.created_projects.where(import_type: "github").to_json(only: [:id, :import_status])
+ render json: jobs
+ end
+
+ def create
+ @repo_id = params[:repo_id].to_i
+ repo = client.repo(@repo_id)
+ @target_namespace = params[:new_namespace].presence || repo.owner.login
+ @project_name = repo.name
+
+ namespace = get_or_create_namespace || (render and return)
+
+ @project = Gitlab::GithubImport::ProjectCreator.new(repo, namespace, current_user).execute
+ end
+
+ private
+
+ def client
+ @client ||= Gitlab::GithubImport::Client.new(current_user.github_access_token)
+ end
+
+ def verify_github_import_enabled
+ not_found! unless github_import_enabled?
+ end
+
+ def github_auth
+ if current_user.github_access_token.blank?
+ go_to_github_for_permissions
+ end
+ end
+
+ def go_to_github_for_permissions
+ redirect_to client.authorize_url(callback_import_github_url)
+ end
+
+ def github_unauthorized
+ go_to_github_for_permissions
+ end
+end
diff --git a/app/controllers/import/gitlab_controller.rb b/app/controllers/import/gitlab_controller.rb
new file mode 100644
index 00000000000..e979dad4b11
--- /dev/null
+++ b/app/controllers/import/gitlab_controller.rb
@@ -0,0 +1,62 @@
+class Import::GitlabController < Import::BaseController
+ before_filter :verify_gitlab_import_enabled
+ before_filter :gitlab_auth, except: :callback
+
+ rescue_from OAuth2::Error, with: :gitlab_unauthorized
+
+ def callback
+ token = client.get_token(params[:code], callback_import_gitlab_url)
+ current_user.gitlab_access_token = token
+ current_user.save
+ redirect_to status_import_gitlab_url
+ end
+
+ def status
+ @repos = client.projects
+
+ @already_added_projects = current_user.created_projects.where(import_type: "gitlab")
+ already_added_projects_names = @already_added_projects.pluck(:import_source)
+
+ @repos = @repos.to_a.reject{ |repo| already_added_projects_names.include? repo["path_with_namespace"] }
+ end
+
+ def jobs
+ jobs = current_user.created_projects.where(import_type: "gitlab").to_json(only: [:id, :import_status])
+ render json: jobs
+ end
+
+ def create
+ @repo_id = params[:repo_id].to_i
+ repo = client.project(@repo_id)
+ @target_namespace = params[:new_namespace].presence || repo["namespace"]["path"]
+ @project_name = repo["name"]
+
+ namespace = get_or_create_namespace || (render and return)
+
+ @project = Gitlab::GitlabImport::ProjectCreator.new(repo, namespace, current_user).execute
+ end
+
+ private
+
+ def client
+ @client ||= Gitlab::GitlabImport::Client.new(current_user.gitlab_access_token)
+ end
+
+ def verify_gitlab_import_enabled
+ not_found! unless gitlab_import_enabled?
+ end
+
+ def gitlab_auth
+ if current_user.gitlab_access_token.blank?
+ go_to_gitlab_for_permissions
+ end
+ end
+
+ def go_to_gitlab_for_permissions
+ redirect_to client.authorize_url(callback_import_gitlab_url)
+ end
+
+ def gitlab_unauthorized
+ go_to_gitlab_for_permissions
+ end
+end
diff --git a/app/controllers/import/gitorious_controller.rb b/app/controllers/import/gitorious_controller.rb
new file mode 100644
index 00000000000..6067a87ee04
--- /dev/null
+++ b/app/controllers/import/gitorious_controller.rb
@@ -0,0 +1,43 @@
+class Import::GitoriousController < Import::BaseController
+
+ def new
+ redirect_to client.authorize_url(callback_import_gitorious_url)
+ end
+
+ def callback
+ session[:gitorious_repos] = params[:repos]
+ redirect_to status_import_gitorious_url
+ end
+
+ def status
+ @repos = client.repos
+
+ @already_added_projects = current_user.created_projects.where(import_type: "gitorious")
+ already_added_projects_names = @already_added_projects.pluck(:import_source)
+
+ @repos.reject! { |repo| already_added_projects_names.include? repo.full_name }
+ end
+
+ def jobs
+ jobs = current_user.created_projects.where(import_type: "gitorious").to_json(only: [:id, :import_status])
+ render json: jobs
+ end
+
+ def create
+ @repo_id = params[:repo_id]
+ repo = client.repo(@repo_id)
+ @target_namespace = params[:new_namespace].presence || repo.namespace
+ @project_name = repo.name
+
+ namespace = get_or_create_namespace || (render and return)
+
+ @project = Gitlab::GitoriousImport::ProjectCreator.new(repo, namespace, current_user).execute
+ end
+
+ private
+
+ def client
+ @client ||= Gitlab::GitoriousImport::Client.new(session[:gitorious_repos])
+ end
+
+end
diff --git a/app/controllers/namespaces_controller.rb b/app/controllers/namespaces_controller.rb
index c59a2401cef..b7a9d8c1291 100644
--- a/app/controllers/namespaces_controller.rb
+++ b/app/controllers/namespaces_controller.rb
@@ -15,4 +15,3 @@ class NamespacesController < ApplicationController
end
end
end
-
diff --git a/app/controllers/oauth/applications_controller.rb b/app/controllers/oauth/applications_controller.rb
new file mode 100644
index 00000000000..efa291d9397
--- /dev/null
+++ b/app/controllers/oauth/applications_controller.rb
@@ -0,0 +1,39 @@
+class Oauth::ApplicationsController < Doorkeeper::ApplicationsController
+ before_filter :authenticate_user!
+ layout "profile"
+
+ def index
+ head :forbidden and return
+ end
+
+ def create
+ @application = Doorkeeper::Application.new(application_params)
+
+ @application.owner = current_user
+
+ if @application.save
+ flash[:notice] = I18n.t(:notice, scope: [:doorkeeper, :flash, :applications, :create])
+ redirect_to oauth_application_url(@application)
+ else
+ render :new
+ end
+ end
+
+ def destroy
+ if @application.destroy
+ flash[:notice] = I18n.t(:notice, scope: [:doorkeeper, :flash, :applications, :destroy])
+ end
+
+ redirect_to applications_profile_url
+ end
+
+ private
+
+ def set_application
+ @application = current_user.oauth_applications.find(params[:id])
+ end
+
+ rescue_from ActiveRecord::RecordNotFound do |exception|
+ render "errors/not_found", layout: "errors", status: 404
+ end
+end
diff --git a/app/controllers/oauth/authorizations_controller.rb b/app/controllers/oauth/authorizations_controller.rb
new file mode 100644
index 00000000000..a57b4a60c24
--- /dev/null
+++ b/app/controllers/oauth/authorizations_controller.rb
@@ -0,0 +1,57 @@
+class Oauth::AuthorizationsController < Doorkeeper::AuthorizationsController
+ before_filter :authenticate_resource_owner!
+ layout "profile"
+
+ def new
+ if pre_auth.authorizable?
+ if skip_authorization? || matching_token?
+ auth = authorization.authorize
+ redirect_to auth.redirect_uri
+ else
+ render "doorkeeper/authorizations/new"
+ end
+ else
+ render "doorkeeper/authorizations/error"
+ end
+ end
+
+ # TODO: Handle raise invalid authorization
+ def create
+ redirect_or_render authorization.authorize
+ end
+
+ def destroy
+ redirect_or_render authorization.deny
+ end
+
+ private
+
+ def matching_token?
+ Doorkeeper::AccessToken.matching_token_for(pre_auth.client,
+ current_resource_owner.id,
+ pre_auth.scopes)
+ end
+
+ def redirect_or_render(auth)
+ if auth.redirectable?
+ redirect_to auth.redirect_uri
+ else
+ render json: auth.body, status: auth.status
+ end
+ end
+
+ def pre_auth
+ @pre_auth ||=
+ Doorkeeper::OAuth::PreAuthorization.new(Doorkeeper.configuration,
+ server.client_via_uid,
+ params)
+ end
+
+ def authorization
+ @authorization ||= strategy.request
+ end
+
+ def strategy
+ @strategy ||= server.authorization_request(pre_auth.response_type)
+ end
+end
diff --git a/app/controllers/oauth/authorized_applications_controller.rb b/app/controllers/oauth/authorized_applications_controller.rb
new file mode 100644
index 00000000000..0b27ce7da72
--- /dev/null
+++ b/app/controllers/oauth/authorized_applications_controller.rb
@@ -0,0 +1,8 @@
+class Oauth::AuthorizedApplicationsController < Doorkeeper::AuthorizedApplicationsController
+ layout "profile"
+
+ def destroy
+ Doorkeeper::AccessToken.revoke_all_for(params[:id], current_resource_owner)
+ redirect_to applications_profile_url, notice: I18n.t(:notice, scope: [:doorkeeper, :flash, :authorized_applications, :destroy])
+ end
+end
diff --git a/app/controllers/omniauth_callbacks_controller.rb b/app/controllers/omniauth_callbacks_controller.rb
index bd4b310fcbf..bb9d65c9ed6 100644
--- a/app/controllers/omniauth_callbacks_controller.rb
+++ b/app/controllers/omniauth_callbacks_controller.rb
@@ -42,11 +42,9 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController
def handle_omniauth
if current_user
- # Change a logged-in user's authentication method:
- current_user.extern_uid = oauth['uid']
- current_user.provider = oauth['provider']
- current_user.save
- redirect_to profile_path
+ # Add new authentication method
+ current_user.identities.find_or_create_by(extern_uid: oauth['uid'], provider: oauth['provider'])
+ redirect_to profile_account_path, notice: 'Authentication method updated'
else
@user = Gitlab::OAuth::User.new(oauth)
@user.save
@@ -67,8 +65,8 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController
redirect_to omniauth_error_path(oauth['provider'], error: error_message) and return
end
end
- rescue StandardError
- flash[:notice] = "There's no such user!"
+ rescue Gitlab::OAuth::ForbiddenAction => e
+ flash[:notice] = e.message
redirect_to new_user_session_path
end
diff --git a/app/controllers/passwords_controller.rb b/app/controllers/passwords_controller.rb
index 988ede3007b..dcbbe5baa4b 100644
--- a/app/controllers/passwords_controller.rb
+++ b/app/controllers/passwords_controller.rb
@@ -5,12 +5,12 @@ class PasswordsController < Devise::PasswordsController
resource_found = resource_class.find_by_email(email)
if resource_found && resource_found.ldap_user?
flash[:alert] = "Cannot reset password for LDAP user."
- respond_with({}, :location => after_sending_reset_password_instructions_path_for(resource_name)) and return
+ respond_with({}, location: after_sending_reset_password_instructions_path_for(resource_name)) and return
end
self.resource = resource_class.send_reset_password_instructions(resource_params)
if successfully_sent?(resource)
- respond_with({}, :location => after_sending_reset_password_instructions_path_for(resource_name))
+ respond_with({}, location: after_sending_reset_password_instructions_path_for(resource_name))
else
respond_with(resource)
end
diff --git a/app/controllers/profiles/emails_controller.rb b/app/controllers/profiles/emails_controller.rb
index f3f0e69b83a..4a65c978e5c 100644
--- a/app/controllers/profiles/emails_controller.rb
+++ b/app/controllers/profiles/emails_controller.rb
@@ -18,6 +18,9 @@ class Profiles::EmailsController < ApplicationController
@email = current_user.emails.find(params[:id])
@email.destroy
+ current_user.set_notification_email
+ current_user.save if current_user.notification_email_changed?
+
respond_to do |format|
format.html { redirect_to profile_emails_url }
format.js { render nothing: true }
diff --git a/app/controllers/profiles/keys_controller.rb b/app/controllers/profiles/keys_controller.rb
index 88414b13564..4e2bd0a9b4b 100644
--- a/app/controllers/profiles/keys_controller.rb
+++ b/app/controllers/profiles/keys_controller.rb
@@ -3,7 +3,7 @@ class Profiles::KeysController < ApplicationController
skip_before_filter :authenticate_user!, only: [:get_keys]
def index
- @keys = current_user.keys.order('id DESC')
+ @keys = current_user.keys
end
def show
diff --git a/app/controllers/profiles/notifications_controller.rb b/app/controllers/profiles/notifications_controller.rb
index 638d1f9789b..433c19189af 100644
--- a/app/controllers/profiles/notifications_controller.rb
+++ b/app/controllers/profiles/notifications_controller.rb
@@ -2,6 +2,7 @@ class Profiles::NotificationsController < ApplicationController
layout 'profile'
def show
+ @user = current_user
@notification = current_user.notification
@project_members = current_user.project_members
@group_members = current_user.group_members
@@ -11,8 +12,7 @@ class Profiles::NotificationsController < ApplicationController
type = params[:notification_type]
@saved = if type == 'global'
- current_user.notification_level = params[:notification_level]
- current_user.save
+ current_user.update_attributes(user_params)
elsif type == 'group'
users_group = current_user.group_members.find(params[:notification_id])
users_group.notification_level = params[:notification_level]
@@ -22,5 +22,23 @@ class Profiles::NotificationsController < ApplicationController
project_member.notification_level = params[:notification_level]
project_member.save
end
+
+ respond_to do |format|
+ format.html do
+ if @saved
+ flash[:notice] = "Notification settings saved"
+ else
+ flash[:alert] = "Failed to save new settings"
+ end
+
+ redirect_to :back
+ end
+
+ format.js
+ end
+ end
+
+ def user_params
+ params.require(:user).permit(:notification_email, :notification_level)
end
end
diff --git a/app/controllers/profiles/passwords_controller.rb b/app/controllers/profiles/passwords_controller.rb
index 1191ce47eba..0c614969a3f 100644
--- a/app/controllers/profiles/passwords_controller.rb
+++ b/app/controllers/profiles/passwords_controller.rb
@@ -11,7 +11,7 @@ class Profiles::PasswordsController < ApplicationController
end
def create
- unless @user.valid_password?(user_params[:current_password])
+ unless @user.password_automatically_set || @user.valid_password?(user_params[:current_password])
redirect_to new_profile_password_path, alert: 'You must provide a valid current password'
return
end
@@ -21,7 +21,8 @@ class Profiles::PasswordsController < ApplicationController
result = @user.update_attributes(
password: new_password,
- password_confirmation: new_password_confirmation
+ password_confirmation: new_password_confirmation,
+ password_automatically_set: false
)
if result
@@ -39,8 +40,9 @@ class Profiles::PasswordsController < ApplicationController
password_attributes = user_params.select do |key, value|
%w(password password_confirmation).include?(key.to_s)
end
+ password_attributes[:password_automatically_set] = false
- unless @user.valid_password?(user_params[:current_password])
+ unless @user.password_automatically_set || @user.valid_password?(user_params[:current_password])
redirect_to edit_profile_password_path, alert: 'You must provide a valid current password'
return
end
diff --git a/app/controllers/profiles_controller.rb b/app/controllers/profiles_controller.rb
index e877f9b9049..a7863aba756 100644
--- a/app/controllers/profiles_controller.rb
+++ b/app/controllers/profiles_controller.rb
@@ -13,6 +13,12 @@ class ProfilesController < ApplicationController
def design
end
+ def applications
+ @applications = current_user.oauth_applications
+ @authorized_tokens = current_user.oauth_authorized_tokens
+ @authorized_apps = @authorized_tokens.map(&:application).uniq
+ end
+
def update
user_params.except!(:email) if @user.ldap_user?
@@ -62,7 +68,7 @@ class ProfilesController < ApplicationController
params.require(:user).permit(
:email, :password, :password_confirmation, :bio, :name, :username,
:skype, :linkedin, :twitter, :website_url, :color_scheme_id, :theme_id,
- :avatar, :hide_no_ssh_key,
+ :avatar, :hide_no_ssh_key, :hide_no_password
)
end
end
diff --git a/app/controllers/projects/application_controller.rb b/app/controllers/projects/application_controller.rb
index 7e4580017dd..4719933394f 100644
--- a/app/controllers/projects/application_controller.rb
+++ b/app/controllers/projects/application_controller.rb
@@ -8,7 +8,8 @@ class Projects::ApplicationController < ApplicationController
# for non-signed users
if !current_user
id = params[:project_id] || params[:id]
- @project = Project.find_with_namespace(id)
+ project_with_namespace = "#{params[:namespace_id]}/#{id}"
+ @project = Project.find_with_namespace(project_with_namespace)
return if @project && @project.public?
end
@@ -26,7 +27,10 @@ class Projects::ApplicationController < ApplicationController
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"
+ redirect_to(
+ namespace_project_tree_path(@project.namespace, @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/avatars_controller.rb b/app/controllers/projects/avatars_controller.rb
new file mode 100644
index 00000000000..a482b90880d
--- /dev/null
+++ b/app/controllers/projects/avatars_controller.rb
@@ -0,0 +1,29 @@
+class Projects::AvatarsController < Projects::ApplicationController
+ layout 'project'
+
+ before_filter :project
+
+ def show
+ @blob = @project.repository.blob_at_branch('master', @project.avatar_in_git)
+ if @blob
+ headers['X-Content-Type-Options'] = 'nosniff'
+ send_data(
+ @blob.data,
+ type: @blob.mime_type,
+ disposition: 'inline',
+ filename: @blob.name
+ )
+ else
+ not_found!
+ end
+ end
+
+ def destroy
+ @project.remove_avatar!
+
+ @project.save
+ @project.reset_events_cache
+
+ redirect_to edit_project_path(@project)
+ end
+end
diff --git a/app/controllers/projects/base_tree_controller.rb b/app/controllers/projects/base_tree_controller.rb
deleted file mode 100644
index 56c306063c8..00000000000
--- a/app/controllers/projects/base_tree_controller.rb
+++ /dev/null
@@ -1,8 +0,0 @@
-class Projects::BaseTreeController < Projects::ApplicationController
- include ExtractsPath
-
- before_filter :authorize_read_project!
- before_filter :authorize_download_code!
- before_filter :require_non_empty_project
-end
-
diff --git a/app/controllers/projects/blame_controller.rb b/app/controllers/projects/blame_controller.rb
index bad06e7aa2d..489a6ae5666 100644
--- a/app/controllers/projects/blame_controller.rb
+++ b/app/controllers/projects/blame_controller.rb
@@ -2,10 +2,9 @@
class Projects::BlameController < Projects::ApplicationController
include ExtractsPath
- # Authorize
- before_filter :authorize_read_project!
- before_filter :authorize_download_code!
before_filter :require_non_empty_project
+ before_filter :assign_ref_vars
+ before_filter :authorize_download_code!
def show
@blob = @repository.blob_at(@commit.id, @path)
diff --git a/app/controllers/projects/blob_controller.rb b/app/controllers/projects/blob_controller.rb
index 04aa044001e..4b7eb4df298 100644
--- a/app/controllers/projects/blob_controller.rb
+++ b/app/controllers/projects/blob_controller.rb
@@ -1,24 +1,91 @@
# Controller for viewing a file's blame
class Projects::BlobController < Projects::ApplicationController
include ExtractsPath
+ include ActionView::Helpers::SanitizeHelper
- # Authorize
- before_filter :authorize_read_project!
+ # Raised when given an invalid file path
+ class InvalidPathError < StandardError; end
+
+ before_filter :require_non_empty_project, except: [:new, :create]
before_filter :authorize_download_code!
- before_filter :require_non_empty_project
before_filter :authorize_push_code!, only: [:destroy]
+ before_filter :assign_blob_vars
+ before_filter :commit, except: [:new, :create]
+ before_filter :blob, except: [:new, :create]
+ before_filter :from_merge_request, only: [:edit, :update]
+ before_filter :after_edit_path, only: [:edit, :update]
+ before_filter :require_branch_head, only: [:edit, :update]
+
+ def new
+ commit unless @repository.empty?
+ end
- before_filter :blob
+ def create
+ file_path = File.join(@path, File.basename(params[:file_name]))
+ result = Files::CreateService.new(
+ @project,
+ current_user,
+ params.merge(new_branch: sanitized_new_branch_name),
+ @ref,
+ file_path
+ ).execute
+
+ if result[:status] == :success
+ flash[:notice] = "Your changes have been successfully committed"
+ ref = sanitized_new_branch_name.presence || @ref
+ redirect_to namespace_project_blob_path(@project.namespace, @project, File.join(ref, file_path))
+ else
+ flash[:alert] = result[:message]
+ render :new
+ end
+ end
def show
end
+ def edit
+ @last_commit = Gitlab::Git::Commit.last_for_path(@repository, @ref, @path).sha
+ end
+
+ def update
+ result = Files::UpdateService.
+ new(
+ @project,
+ current_user,
+ params.merge(new_branch: sanitized_new_branch_name),
+ @ref,
+ @path
+ ).execute
+
+ if result[:status] == :success
+ flash[:notice] = "Your changes have been successfully committed"
+
+ if from_merge_request
+ from_merge_request.reload_code
+ end
+
+ redirect_to after_edit_path
+ else
+ flash[:alert] = result[:message]
+ render :edit
+ end
+ end
+
+ def preview
+ @content = params[:content]
+ diffy = Diffy::Diff.new(@blob.data, @content, diff: '-U 3', include_diff_info: true)
+ @diff_lines = Gitlab::Diff::Parser.new.parse(diffy.diff.scan(/.*\n/))
+
+ render layout: false
+ end
+
def destroy
result = Files::DeleteService.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)
+ redirect_to namespace_project_tree_path(@project.namespace, @project,
+ @ref)
else
flash[:alert] = result[:message]
render :show
@@ -47,10 +114,50 @@ class Projects::BlobController < Projects::ApplicationController
if @blob
@blob
- elsif tree.entries.any?
- redirect_to project_tree_path(@project, File.join(@ref, @path)) and return
else
+ if tree = @repository.tree(@commit.id, @path)
+ if tree.entries.any?
+ redirect_to namespace_project_tree_path(@project.namespace, @project, File.join(@ref, @path)) and return
+ end
+ end
+
return not_found!
end
end
+
+ def commit
+ @commit = @repository.commit(@ref)
+
+ return not_found! unless @commit
+ end
+
+ def assign_blob_vars
+ @id = params[:id]
+ @ref, @path = extract_ref(@id)
+
+
+ rescue InvalidPathError
+ not_found!
+ end
+
+ def after_edit_path
+ @after_edit_path ||=
+ if from_merge_request
+ diffs_namespace_project_merge_request_path(from_merge_request.target_project.namespace, from_merge_request.target_project, from_merge_request) +
+ "#file-path-#{hexdigest(@path)}"
+ elsif sanitized_new_branch_name.present?
+ namespace_project_blob_path(@project.namespace, @project, File.join(sanitized_new_branch_name, @path))
+ else
+ namespace_project_blob_path(@project.namespace, @project, @id)
+ end
+ end
+
+ def from_merge_request
+ # If blob edit was initiated from merge request page
+ @from_merge_request ||= MergeRequest.find_by(id: params[:from_merge_request_id])
+ end
+
+ def sanitized_new_branch_name
+ @new_branch ||= sanitize(strip_tags(params[:new_branch]))
+ end
end
diff --git a/app/controllers/projects/branches_controller.rb b/app/controllers/projects/branches_controller.rb
index 9f50660a5ad..690501f3060 100644
--- a/app/controllers/projects/branches_controller.rb
+++ b/app/controllers/projects/branches_controller.rb
@@ -1,8 +1,7 @@
class Projects::BranchesController < Projects::ApplicationController
+ include ActionView::Helpers::SanitizeHelper
# Authorize
- before_filter :authorize_read_project!
before_filter :require_non_empty_project
-
before_filter :authorize_download_code!
before_filter :authorize_push_code!, only: [:create, :destroy]
@@ -17,12 +16,15 @@ class Projects::BranchesController < Projects::ApplicationController
end
def create
+ branch_name = sanitize(strip_tags(params[:branch_name]))
+ ref = sanitize(strip_tags(params[:ref]))
result = CreateBranchService.new(project, current_user).
- execute(params[:branch_name], params[:ref])
+ execute(branch_name, ref)
if result[:status] == :success
@branch = result[:branch]
- redirect_to project_tree_path(@project, @branch.name)
+ redirect_to namespace_project_tree_path(@project.namespace, @project,
+ @branch.name)
else
@error = result[:message]
render action: 'new'
@@ -34,7 +36,10 @@ class Projects::BranchesController < Projects::ApplicationController
@branch_name = params[:id]
respond_to do |format|
- format.html { redirect_to project_branches_path(@project) }
+ format.html do
+ redirect_to namespace_project_branches_path(@project.namespace,
+ @project)
+ end
format.js
end
end
diff --git a/app/controllers/projects/commit_controller.rb b/app/controllers/projects/commit_controller.rb
index cf05e6ea220..87e39f1363a 100644
--- a/app/controllers/projects/commit_controller.rb
+++ b/app/controllers/projects/commit_controller.rb
@@ -3,16 +3,14 @@
# Not to be confused with CommitsController, plural.
class Projects::CommitController < Projects::ApplicationController
# Authorize
- before_filter :authorize_read_project!
- before_filter :authorize_download_code!
before_filter :require_non_empty_project
+ before_filter :authorize_download_code!
before_filter :commit
def show
return git_not_found! unless @commit
@line_notes = @project.notes.for_commit_id(commit.id).inline
- @branches = @project.repository.branch_names_contains(commit.id)
@diffs = @commit.diffs
@note = @project.build_commit_note(commit)
@notes_count = @project.notes.for_commit_id(commit.id).count
@@ -31,6 +29,12 @@ class Projects::CommitController < Projects::ApplicationController
end
end
+ def branches
+ @branches = @project.repository.branch_names_contains(commit.id)
+ @tags = @project.repository.tag_names_contains(commit.id)
+ render layout: false
+ end
+
def commit
@commit ||= @project.repository.commit(params[:id])
end
diff --git a/app/controllers/projects/commits_controller.rb b/app/controllers/projects/commits_controller.rb
index 53a0d063d8e..4b6ab437476 100644
--- a/app/controllers/projects/commits_controller.rb
+++ b/app/controllers/projects/commits_controller.rb
@@ -3,10 +3,9 @@ require "base64"
class Projects::CommitsController < Projects::ApplicationController
include ExtractsPath
- # Authorize
- before_filter :authorize_read_project!
- before_filter :authorize_download_code!
before_filter :require_non_empty_project
+ before_filter :assign_ref_vars
+ before_filter :authorize_download_code!
def show
@repo = @project.repository
@@ -14,7 +13,7 @@ class Projects::CommitsController < Projects::ApplicationController
@commits = @repo.commits(@ref, @path, @limit, @offset)
@note_counts = Note.where(commit_id: @commits.map(&:id)).
- group(:commit_id).count
+ group(:commit_id).count
respond_to do |format|
format.html
diff --git a/app/controllers/projects/compare_controller.rb b/app/controllers/projects/compare_controller.rb
index 6d944025598..146808fa562 100644
--- a/app/controllers/projects/compare_controller.rb
+++ b/app/controllers/projects/compare_controller.rb
@@ -1,8 +1,7 @@
class Projects::CompareController < Projects::ApplicationController
# Authorize
- before_filter :authorize_read_project!
- before_filter :authorize_download_code!
before_filter :require_non_empty_project
+ before_filter :authorize_download_code!
def index
end
@@ -26,6 +25,7 @@ class Projects::CompareController < Projects::ApplicationController
end
def create
- redirect_to project_compare_path(@project, params[:from], params[:to])
+ redirect_to namespace_project_compare_path(@project.namespace, @project,
+ params[:from], params[:to])
end
end
diff --git a/app/controllers/projects/deploy_keys_controller.rb b/app/controllers/projects/deploy_keys_controller.rb
index 024b9520d30..b7cc305899c 100644
--- a/app/controllers/projects/deploy_keys_controller.rb
+++ b/app/controllers/projects/deploy_keys_controller.rb
@@ -25,7 +25,8 @@ class Projects::DeployKeysController < Projects::ApplicationController
@key = DeployKey.new(deploy_key_params)
if @key.valid? && @project.deploy_keys << @key
- redirect_to project_deploy_keys_path(@project)
+ redirect_to namespace_project_deploy_keys_path(@project.namespace,
+ @project)
else
render "new"
end
@@ -44,13 +45,15 @@ class Projects::DeployKeysController < Projects::ApplicationController
def enable
@project.deploy_keys << available_keys.find(params[:id])
- redirect_to project_deploy_keys_path(@project)
+ redirect_to namespace_project_deploy_keys_path(@project.namespace,
+ @project)
end
def disable
@project.deploy_keys_projects.where(deploy_key_id: params[:id]).last.destroy
- redirect_to project_deploy_keys_path(@project)
+ redirect_to namespace_project_deploy_keys_path(@project.namespace,
+ @project)
end
protected
diff --git a/app/controllers/projects/edit_tree_controller.rb b/app/controllers/projects/edit_tree_controller.rb
deleted file mode 100644
index 65661c80410..00000000000
--- a/app/controllers/projects/edit_tree_controller.rb
+++ /dev/null
@@ -1,60 +0,0 @@
-class Projects::EditTreeController < Projects::BaseTreeController
- before_filter :require_branch_head
- before_filter :blob
- before_filter :authorize_push_code!
- before_filter :from_merge_request
- before_filter :after_edit_path
-
- def show
- @last_commit = Gitlab::Git::Commit.last_for_path(@repository, @ref, @path).sha
- end
-
- def update
- result = Files::UpdateService.
- new(@project, current_user, params, @ref, @path).execute
-
- if result[:status] == :success
- flash[:notice] = "Your changes have been successfully committed"
-
- if from_merge_request
- from_merge_request.reload_code
- end
-
- redirect_to after_edit_path
- else
- flash[:alert] = result[:message]
- render :show
- end
- end
-
- def preview
- @content = params[:content]
-
- diffy = Diffy::Diff.new(@blob.data, @content, diff: '-U 3',
- include_diff_info: true)
- @diff_lines = Gitlab::Diff::Parser.new.parse(diffy.diff.scan(/.*\n/))
-
- render layout: false
- end
-
- private
-
- def blob
- @blob ||= @repository.blob_at(@commit.id, @path)
- end
-
- def after_edit_path
- @after_edit_path ||=
- if from_merge_request
- diffs_project_merge_request_path(from_merge_request.target_project, from_merge_request) +
- "#file-path-#{hexdigest(@path)}"
- else
- project_blob_path(@project, @id)
- end
- end
-
- def from_merge_request
- # If blob edit was initiated from merge request page
- @from_merge_request ||= MergeRequest.find_by(id: params[:from_merge_request_id])
- end
-end
diff --git a/app/controllers/projects/forks_controller.rb b/app/controllers/projects/forks_controller.rb
new file mode 100644
index 00000000000..21a151a426e
--- /dev/null
+++ b/app/controllers/projects/forks_controller.rb
@@ -0,0 +1,25 @@
+class Projects::ForksController < Projects::ApplicationController
+ # Authorize
+ before_filter :require_non_empty_project
+ before_filter :authorize_download_code!
+
+ def new
+ @namespaces = current_user.manageable_namespaces
+ @namespaces.delete(@project.namespace)
+ end
+
+ def create
+ namespace = Namespace.find(params[:namespace_key])
+ @forked_project = ::Projects::ForkService.new(project, current_user, namespace: namespace).execute
+
+ if @forked_project.saved? && @forked_project.forked?
+ redirect_to(
+ namespace_project_path(@forked_project.namespace, @forked_project),
+ notice: 'Project was successfully forked.'
+ )
+ else
+ @title = 'Fork project'
+ render :error
+ end
+ end
+end
diff --git a/app/controllers/projects/graphs_controller.rb b/app/controllers/projects/graphs_controller.rb
index 21d3970d65a..752474b4a4c 100644
--- a/app/controllers/projects/graphs_controller.rb
+++ b/app/controllers/projects/graphs_controller.rb
@@ -1,8 +1,7 @@
class Projects::GraphsController < Projects::ApplicationController
# Authorize
- before_filter :authorize_read_project!
- before_filter :authorize_download_code!
before_filter :require_non_empty_project
+ before_filter :authorize_download_code!
def show
respond_to do |format|
diff --git a/app/controllers/projects/hooks_controller.rb b/app/controllers/projects/hooks_controller.rb
index cab8fd76e6c..ba95bb13e1f 100644
--- a/app/controllers/projects/hooks_controller.rb
+++ b/app/controllers/projects/hooks_controller.rb
@@ -16,7 +16,7 @@ class Projects::HooksController < Projects::ApplicationController
@hook.save
if @hook.valid?
- redirect_to project_hooks_path(@project)
+ redirect_to namespace_project_hooks_path(@project.namespace, @project)
else
@hooks = @project.hooks.select(&:persisted?)
render :index
@@ -26,6 +26,7 @@ class Projects::HooksController < Projects::ApplicationController
def test
if !@project.empty_repo?
status = TestHookService.new.execute(hook, current_user)
+
if status
flash[:notice] = 'Hook successfully executed.'
else
@@ -42,7 +43,7 @@ class Projects::HooksController < Projects::ApplicationController
def destroy
hook.destroy
- redirect_to project_hooks_path(@project)
+ redirect_to namespace_project_hooks_path(@project.namespace, @project)
end
private
diff --git a/app/controllers/projects/imports_controller.rb b/app/controllers/projects/imports_controller.rb
new file mode 100644
index 00000000000..e2f957a640c
--- /dev/null
+++ b/app/controllers/projects/imports_controller.rb
@@ -0,0 +1,51 @@
+class Projects::ImportsController < Projects::ApplicationController
+ # Authorize
+ before_filter :authorize_admin_project!
+ before_filter :require_no_repo
+ before_filter :redirect_if_progress, except: :show
+
+ def new
+ end
+
+ def create
+ @project.import_url = params[:project][:import_url]
+
+ if @project.save
+ @project.reload
+
+ if @project.import_failed?
+ @project.import_retry
+ else
+ @project.import_start
+ end
+ end
+
+ redirect_to namespace_project_import_path(@project.namespace, @project)
+ end
+
+ def show
+ unless @project.import_in_progress?
+ if @project.import_finished?
+ redirect_to(@project) and return
+ else
+ redirect_to new_namespace_project_import_path(@project.namespace,
+ @project) && return
+ end
+ end
+ end
+
+ private
+
+ def require_no_repo
+ if @project.repository_exists?
+ redirect_to(namespace_project_path(@project.namespace, @project)) and return
+ end
+ end
+
+ def redirect_if_progress
+ if @project.import_in_progress?
+ redirect_to namespace_project_import_path(@project.namespace, @project) &&
+ return
+ end
+ end
+end
diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb
index c6d526f05c5..6a2af08a199 100644
--- a/app/controllers/projects/issues_controller.rb
+++ b/app/controllers/projects/issues_controller.rb
@@ -18,18 +18,10 @@ class Projects::IssuesController < Projects::ApplicationController
def index
terms = params['issue_search']
-
- @issues = issues_filtered
+ @issues = get_issues_collection
@issues = @issues.full_search(terms) if terms.present?
@issues = @issues.page(params[:page]).per(20)
- 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?
- @assignees = User.where(id: @project.issues.pluck(:assignee_id)).active
-
respond_to do |format|
format.html
format.atom { render layout: false }
@@ -68,7 +60,7 @@ class Projects::IssuesController < Projects::ApplicationController
respond_to do |format|
format.html do
if @issue.valid?
- redirect_to project_issue_path(@project, @issue)
+ redirect_to issue_path(@issue)
else
render :new
end
@@ -86,7 +78,7 @@ class Projects::IssuesController < Projects::ApplicationController
format.js
format.html do
if @issue.valid?
- redirect_to [@project, @issue]
+ redirect_to issue_path(@issue)
else
render :edit
end
@@ -127,12 +119,6 @@ class Projects::IssuesController < Projects::ApplicationController
return render_404 unless @project.issues_enabled
end
- def issues_filtered
- params[:scope] = 'all' if params[:scope].blank?
- params[:state] = 'opened' if params[:state].blank?
- @issues = IssuesFinder.new.execute(current_user, params.merge(project_id: @project.id))
- end
-
# Since iids are implemented only in 6.1
# user may navigate to issue page using old global ids.
#
@@ -142,7 +128,7 @@ class Projects::IssuesController < Projects::ApplicationController
issue = @project.issues.find_by(id: params[:id])
if issue
- redirect_to project_issue_path(@project, issue)
+ redirect_to issue_path(issue)
return
else
raise ActiveRecord::RecordNotFound.new
diff --git a/app/controllers/projects/labels_controller.rb b/app/controllers/projects/labels_controller.rb
index 6c7bde9c5d5..5e31fce4b0e 100644
--- a/app/controllers/projects/labels_controller.rb
+++ b/app/controllers/projects/labels_controller.rb
@@ -7,7 +7,7 @@ class Projects::LabelsController < Projects::ApplicationController
respond_to :js, :html
def index
- @labels = @project.labels.order_by_name.page(params[:page]).per(20)
+ @labels = @project.labels.page(params[:page]).per(20)
end
def new
@@ -18,7 +18,7 @@ class Projects::LabelsController < Projects::ApplicationController
@label = @project.labels.create(label_params)
if @label.valid?
- redirect_to project_labels_path(@project)
+ redirect_to namespace_project_labels_path(@project.namespace, @project)
else
render 'new'
end
@@ -29,7 +29,7 @@ class Projects::LabelsController < Projects::ApplicationController
def update
if @label.update_attributes(label_params)
- redirect_to project_labels_path(@project)
+ redirect_to namespace_project_labels_path(@project.namespace, @project)
else
render 'edit'
end
@@ -39,11 +39,12 @@ class Projects::LabelsController < Projects::ApplicationController
Gitlab::IssuesLabels.generate(@project)
if params[:redirect] == 'issues'
- redirect_to project_issues_path(@project)
+ redirect_to namespace_project_issues_path(@project.namespace, @project)
elsif params[:redirect] == 'merge_requests'
- redirect_to project_merge_requests_path(@project)
+ redirect_to namespace_project_merge_requests_path(@project.namespace,
+ @project)
else
- redirect_to project_labels_path(@project)
+ redirect_to namespace_project_labels_path(@project.namespace, @project)
end
end
@@ -51,7 +52,10 @@ class Projects::LabelsController < Projects::ApplicationController
@label.destroy
respond_to do |format|
- format.html { redirect_to project_labels_path(@project), notice: 'Label was removed' }
+ format.html do
+ redirect_to(namespace_project_labels_path(@project.namespace, @project),
+ notice: 'Label was removed')
+ end
format.js
end
end
diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb
index 20a733b10e1..26d4c51773f 100644
--- a/app/controllers/projects/merge_requests_controller.rb
+++ b/app/controllers/projects/merge_requests_controller.rb
@@ -17,26 +17,17 @@ class Projects::MergeRequestsController < Projects::ApplicationController
before_filter :authorize_modify_merge_request!, only: [:close, :edit, :update, :sort]
def index
- params[:sort] ||= 'newest'
- params[:scope] = 'all' if params[:scope].blank?
- params[:state] = 'opened' if params[:state].blank?
-
- @merge_requests = MergeRequestsFinder.new.execute(current_user, params.merge(project_id: @project.id))
+ @merge_requests = get_merge_requests_collection
@merge_requests = @merge_requests.page(params[:page]).per(20)
-
- @sort = params[:sort].humanize
- 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?
- @assignees = User.where(id: @project.merge_requests.pluck(:assignee_id))
end
def show
@note_counts = Note.where(commit_id: @merge_request.commits.map(&:id)).
- group(:commit_id).count
+ group(:commit_id).count
respond_to do |format|
format.html
+ format.json { render json: @merge_request }
format.diff { render text: @merge_request.to_diff(current_user) }
format.patch { render text: @merge_request.to_patch(current_user) }
end
@@ -87,7 +78,10 @@ class Projects::MergeRequestsController < Projects::ApplicationController
@merge_request = MergeRequests::CreateService.new(project, current_user, merge_request_params).execute
if @merge_request.valid?
- redirect_to project_merge_request_path(@merge_request.target_project, @merge_request), notice: 'Merge request was successfully created.'
+ redirect_to(
+ merge_request_path(@merge_request),
+ notice: 'Merge request was successfully created.'
+ )
else
@source_project = @merge_request.source_project
@target_project = @merge_request.target_project
@@ -102,7 +96,9 @@ class Projects::MergeRequestsController < Projects::ApplicationController
respond_to do |format|
format.js
format.html do
- redirect_to [@merge_request.target_project, @merge_request], notice: 'Merge request was successfully updated.'
+ redirect_to([@merge_request.target_project.namespace.becomes(Namespace),
+ @merge_request.target_project, @merge_request],
+ notice: 'Merge request was successfully updated.')
end
end
else
@@ -114,15 +110,15 @@ class Projects::MergeRequestsController < Projects::ApplicationController
if @merge_request.unchecked?
@merge_request.check_if_can_be_merged
end
- render json: {merge_status: @merge_request.merge_status_name}
+
+ render json: { merge_status: @merge_request.merge_status_name }
end
def automerge
return access_denied! unless allowed_to_merge?
if @merge_request.open? && @merge_request.can_be_merged?
- @merge_request.should_remove_source_branch = params[:should_remove_source_branch]
- @merge_request.automerge!(current_user, params[:commit_message])
+ AutoMergeWorker.perform_async(@merge_request.id, current_user.id, params)
@status = true
else
@status = false
@@ -225,6 +221,11 @@ class Projects::MergeRequestsController < Projects::ApplicationController
@allowed_to_merge = allowed_to_merge?
@show_merge_controls = @merge_request.open? && @commits.any? && @allowed_to_merge
@source_branch = @merge_request.source_project.repository.find_branch(@merge_request.source_branch).try(:name)
+
+ if @merge_request.locked_long_ago?
+ @merge_request.unlock_mr
+ @merge_request.close
+ end
end
def allowed_to_merge?
@@ -237,13 +238,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
end
def allowed_to_push_code?(project, branch)
- action = if project.protected_branch?(branch)
- :push_code_to_protected_branches
- else
- :push_code
- end
-
- can?(current_user, action, project)
+ ::Gitlab::GitAccess.can_push_to_branch?(current_user, project, branch)
end
def merge_request_params
diff --git a/app/controllers/projects/milestones_controller.rb b/app/controllers/projects/milestones_controller.rb
index d338cdedfaf..97eaabb15c3 100644
--- a/app/controllers/projects/milestones_controller.rb
+++ b/app/controllers/projects/milestones_controller.rb
@@ -11,7 +11,7 @@ class Projects::MilestonesController < Projects::ApplicationController
respond_to :html
def index
- @milestones = case params[:f]
+ @milestones = case params[:state]
when 'all'; @project.milestones.order("state, due_date DESC")
when 'closed'; @project.milestones.closed.order("due_date DESC")
else @project.milestones.active.order("due_date ASC")
@@ -40,7 +40,8 @@ class Projects::MilestonesController < Projects::ApplicationController
@milestone = Milestones::CreateService.new(project, current_user, milestone_params).execute
if @milestone.save
- redirect_to project_milestone_path(@project, @milestone)
+ redirect_to namespace_project_milestone_path(@project.namespace,
+ @project, @milestone)
else
render "new"
end
@@ -67,7 +68,7 @@ class Projects::MilestonesController < Projects::ApplicationController
@milestone.destroy
respond_to do |format|
- format.html { redirect_to project_milestones_path }
+ format.html { redirect_to namespace_project_milestones_path }
format.js { render nothing: true }
end
end
@@ -103,7 +104,9 @@ class Projects::MilestonesController < Projects::ApplicationController
end
def module_enabled
- return render_404 unless @project.issues_enabled
+ unless @project.issues_enabled || @project.merge_requests_enabled
+ return render_404
+ end
end
def milestone_params
diff --git a/app/controllers/projects/network_controller.rb b/app/controllers/projects/network_controller.rb
index 009089ee639..83d1c1dacae 100644
--- a/app/controllers/projects/network_controller.rb
+++ b/app/controllers/projects/network_controller.rb
@@ -2,10 +2,9 @@ class Projects::NetworkController < Projects::ApplicationController
include ExtractsPath
include ApplicationHelper
- # Authorize
- before_filter :authorize_read_project!
- before_filter :authorize_download_code!
before_filter :require_non_empty_project
+ before_filter :assign_ref_vars
+ before_filter :authorize_download_code!
def show
respond_to do |format|
diff --git a/app/controllers/projects/new_tree_controller.rb b/app/controllers/projects/new_tree_controller.rb
deleted file mode 100644
index ffba706b2f6..00000000000
--- a/app/controllers/projects/new_tree_controller.rb
+++ /dev/null
@@ -1,20 +0,0 @@
-class Projects::NewTreeController < Projects::BaseTreeController
- before_filter :require_branch_head
- before_filter :authorize_push_code!
-
- def show
- end
-
- def update
- file_path = File.join(@path, File.basename(params[:file_name]))
- result = Files::CreateService.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[:message]
- render :show
- end
- end
-end
diff --git a/app/controllers/projects/notes_controller.rb b/app/controllers/projects/notes_controller.rb
index 7b08b79d236..2f1d631c14a 100644
--- a/app/controllers/projects/notes_controller.rb
+++ b/app/controllers/projects/notes_controller.rb
@@ -61,10 +61,6 @@ class Projects::NotesController < Projects::ApplicationController
end
end
- def preview
- render text: view_context.markdown(params[:note])
- end
-
private
def note
diff --git a/app/controllers/projects/protected_branches_controller.rb b/app/controllers/projects/protected_branches_controller.rb
index bd31b1d3c54..ac36ac6fcd3 100644
--- a/app/controllers/projects/protected_branches_controller.rb
+++ b/app/controllers/projects/protected_branches_controller.rb
@@ -12,14 +12,33 @@ class Projects::ProtectedBranchesController < Projects::ApplicationController
def create
@project.protected_branches.create(protected_branch_params)
- redirect_to project_protected_branches_path(@project)
+ redirect_to namespace_project_protected_branches_path(@project.namespace,
+ @project)
+ end
+
+ def update
+ protected_branch = @project.protected_branches.find(params[:id])
+
+ if protected_branch &&
+ protected_branch.update_attributes(
+ developers_can_push: params[:developers_can_push]
+ )
+
+ respond_to do |format|
+ format.json { render json: protected_branch, status: :ok }
+ end
+ else
+ respond_to do |format|
+ format.json { render json: protected_branch.errors, status: :unprocessable_entity }
+ end
+ end
end
def destroy
@project.protected_branches.find(params[:id]).destroy
respond_to do |format|
- format.html { redirect_to project_protected_branches_path }
+ format.html { redirect_to namespace_project_protected_branches_path }
format.js { render nothing: true }
end
end
@@ -27,6 +46,6 @@ class Projects::ProtectedBranchesController < Projects::ApplicationController
private
def protected_branch_params
- params.require(:protected_branch).permit(:name)
+ params.require(:protected_branch).permit(:name, :developers_can_push)
end
end
diff --git a/app/controllers/projects/raw_controller.rb b/app/controllers/projects/raw_controller.rb
index f4fdd616c50..b1a029ce696 100644
--- a/app/controllers/projects/raw_controller.rb
+++ b/app/controllers/projects/raw_controller.rb
@@ -2,10 +2,9 @@
class Projects::RawController < Projects::ApplicationController
include ExtractsPath
- # Authorize
- before_filter :authorize_read_project!
- before_filter :authorize_download_code!
before_filter :require_non_empty_project
+ before_filter :assign_ref_vars
+ before_filter :authorize_download_code!
def show
@blob = @repository.blob_at(@commit.id, @path)
@@ -36,4 +35,3 @@ class Projects::RawController < Projects::ApplicationController
end
end
end
-
diff --git a/app/controllers/projects/refs_controller.rb b/app/controllers/projects/refs_controller.rb
index 9ac189a78b3..67acf45ab7f 100644
--- a/app/controllers/projects/refs_controller.rb
+++ b/app/controllers/projects/refs_controller.rb
@@ -1,22 +1,23 @@
class Projects::RefsController < Projects::ApplicationController
include ExtractsPath
- # Authorize
- before_filter :authorize_read_project!
- before_filter :authorize_download_code!
before_filter :require_non_empty_project
+ before_filter :assign_ref_vars
+ before_filter :authorize_download_code!
def switch
respond_to do |format|
format.html do
new_path = if params[:destination] == "tree"
- project_tree_path(@project, (@id))
+ namespace_project_tree_path(@project.namespace, @project,
+ (@id))
elsif params[:destination] == "blob"
- project_blob_path(@project, (@id))
+ namespace_project_blob_path(@project.namespace, @project,
+ (@id))
elsif params[:destination] == "graph"
- project_network_path(@project, @id, @options)
+ namespace_project_network_path(@project.namespace, @project, @id, @options)
else
- project_commits_path(@project, @id)
+ namespace_project_commits_path(@project.namespace, @project, @id)
end
redirect_to new_path
@@ -32,19 +33,19 @@ class Projects::RefsController < Projects::ApplicationController
def logs_tree
@offset = if params[:offset].present?
- params[:offset].to_i
- else
- 0
- end
+ params[:offset].to_i
+ else
+ 0
+ end
@limit = 25
@path = params[:path]
contents = []
- contents += tree.trees
- contents += tree.blobs
- contents += tree.submodules
+ contents.push(*tree.trees)
+ contents.push(*tree.blobs)
+ contents.push(*tree.submodules)
@logs = contents[@offset, @limit].to_a.map do |content|
file = @path ? File.join(@path, content.name) : content.name
diff --git a/app/controllers/projects/repositories_controller.rb b/app/controllers/projects/repositories_controller.rb
index 6d8ef0f1ac8..cbb888b25e8 100644
--- a/app/controllers/projects/repositories_controller.rb
+++ b/app/controllers/projects/repositories_controller.rb
@@ -1,8 +1,14 @@
class Projects::RepositoriesController < Projects::ApplicationController
# Authorize
- before_filter :authorize_read_project!
+ before_filter :require_non_empty_project, except: :create
before_filter :authorize_download_code!
- before_filter :require_non_empty_project
+ before_filter :authorize_admin_project!, only: :create
+
+ def create
+ @project.create_repository
+
+ redirect_to project_path(@project)
+ end
def archive
unless can?(current_user, :download_code, @project)
diff --git a/app/controllers/projects/services_controller.rb b/app/controllers/projects/services_controller.rb
index a5f30dcfd9d..5c29a6550f5 100644
--- a/app/controllers/projects/services_controller.rb
+++ b/app/controllers/projects/services_controller.rb
@@ -9,7 +9,7 @@ class Projects::ServicesController < Projects::ApplicationController
def index
@project.build_missing_services
- @services = @project.services.reload
+ @services = @project.services.visible.reload
end
def edit
@@ -17,18 +17,25 @@ class Projects::ServicesController < Projects::ApplicationController
def update
if @service.update_attributes(service_params)
- redirect_to edit_project_service_path(@project, @service.to_param)
+ redirect_to(
+ edit_namespace_project_service_path(@project.namespace, @project,
+ @service.to_param, notice:
+ 'Successfully updated.')
+ )
else
render 'edit'
end
end
def test
- data = GitPushService.new.sample_data(project, current_user)
-
- @service.execute(data)
+ data = Gitlab::PushDataBuilder.build_sample(project, current_user)
+ if @service.execute(data)
+ message = { notice: 'We sent a request to the provided URL' }
+ else
+ message = { alert: 'We tried to send a request to the provided URL but an error occured' }
+ end
- redirect_to :back
+ redirect_to :back, message
end
private
@@ -42,7 +49,8 @@ class Projects::ServicesController < Projects::ApplicationController
:title, :token, :type, :active, :api_key, :subdomain,
:room, :recipients, :project_url, :webhook,
:user_key, :device, :priority, :sound, :bamboo_url, :username, :password,
- :build_key
+ :build_key, :server, :teamcity_url, :build_type,
+ :description, :issues_url, :new_issue_url, :restrict_to_branch
)
end
end
diff --git a/app/controllers/projects/snippets_controller.rb b/app/controllers/projects/snippets_controller.rb
index 9d5dd8a95cc..6c250e4ffed 100644
--- a/app/controllers/projects/snippets_controller.rb
+++ b/app/controllers/projects/snippets_controller.rb
@@ -32,7 +32,8 @@ class Projects::SnippetsController < Projects::ApplicationController
@snippet.author = current_user
if @snippet.save
- redirect_to project_snippet_path(@project, @snippet)
+ redirect_to namespace_project_snippet_path(@project.namespace, @project,
+ @snippet)
else
respond_with(@snippet)
end
@@ -43,7 +44,7 @@ class Projects::SnippetsController < Projects::ApplicationController
def update
if @snippet.update_attributes(snippet_params)
- redirect_to project_snippet_path(@project, @snippet)
+ redirect_to namespace_project_snippet_path(@project.namespace, @project, @snippet)
else
respond_with(@snippet)
end
@@ -60,7 +61,7 @@ class Projects::SnippetsController < Projects::ApplicationController
@snippet.destroy
- redirect_to project_snippets_path(@project)
+ redirect_to namespace_project_snippets_path(@project.namespace, @project)
end
def raw
@@ -68,7 +69,7 @@ class Projects::SnippetsController < Projects::ApplicationController
@snippet.content,
type: 'text/plain; charset=utf-8',
disposition: 'inline',
- filename: @snippet.file_name
+ filename: @snippet.sanitized_file_name
)
end
diff --git a/app/controllers/projects/tags_controller.rb b/app/controllers/projects/tags_controller.rb
index 94794fb5dd0..08c7ce3f37d 100644
--- a/app/controllers/projects/tags_controller.rb
+++ b/app/controllers/projects/tags_controller.rb
@@ -1,8 +1,6 @@
class Projects::TagsController < Projects::ApplicationController
# Authorize
- before_filter :authorize_read_project!
before_filter :require_non_empty_project
-
before_filter :authorize_download_code!
before_filter :authorize_push_code!, only: [:create]
before_filter :authorize_admin_project!, only: [:destroy]
@@ -15,9 +13,10 @@ class Projects::TagsController < Projects::ApplicationController
def create
result = CreateTagService.new(@project, current_user).
execute(params[:tag_name], params[:ref], params[:message])
+
if result[:status] == :success
@tag = result[:tag]
- redirect_to project_tags_path(@project)
+ redirect_to namespace_project_tags_path(@project.namespace, @project)
else
@error = result[:message]
render action: 'new'
@@ -28,11 +27,11 @@ class Projects::TagsController < Projects::ApplicationController
tag = @repository.find_tag(params[:id])
if tag && @repository.rm_tag(tag.name)
- Event.create_ref_event(@project, current_user, tag, 'rm', 'refs/tags')
+ EventCreateService.new.push_ref(@project, current_user, tag, 'rm', 'refs/tags')
end
respond_to do |format|
- format.html { redirect_to project_tags_path }
+ format.html { redirect_to namespace_project_tags_path }
format.js
end
end
diff --git a/app/controllers/projects/team_members_controller.rb b/app/controllers/projects/team_members_controller.rb
index 0791e6080fb..71b0ab7ee82 100644
--- a/app/controllers/projects/team_members_controller.rb
+++ b/app/controllers/projects/team_members_controller.rb
@@ -21,7 +21,8 @@ class Projects::TeamMembersController < Projects::ApplicationController
if params[:redirect_to]
redirect_to params[:redirect_to]
else
- redirect_to project_team_index_path(@project)
+ redirect_to namespace_project_team_index_path(@project.namespace,
+ @project)
end
end
@@ -32,7 +33,7 @@ class Projects::TeamMembersController < Projects::ApplicationController
unless @user_project_relation.valid?
flash[:alert] = "User should have at least one role"
end
- redirect_to project_team_index_path(@project)
+ redirect_to namespace_project_team_index_path(@project.namespace, @project)
end
def destroy
@@ -40,7 +41,10 @@ class Projects::TeamMembersController < Projects::ApplicationController
@user_project_relation.destroy
respond_to do |format|
- format.html { redirect_to project_team_index_path(@project) }
+ format.html do
+ redirect_to namespace_project_team_index_path(@project.namespace,
+ @project)
+ end
format.js { render nothing: true }
end
end
@@ -59,7 +63,8 @@ class Projects::TeamMembersController < Projects::ApplicationController
status = @project.team.import(giver)
notice = status ? "Successfully imported" : "Import failed"
- redirect_to project_team_index_path(project), notice: notice
+ redirect_to(namespace_project_team_index_path(project.namespace, project),
+ notice: notice)
end
protected
diff --git a/app/controllers/projects/tree_controller.rb b/app/controllers/projects/tree_controller.rb
index 4d033b36848..b23010bf595 100644
--- a/app/controllers/projects/tree_controller.rb
+++ b/app/controllers/projects/tree_controller.rb
@@ -1,10 +1,18 @@
# Controller for viewing a repository's file structure
-class Projects::TreeController < Projects::BaseTreeController
- def show
+class Projects::TreeController < Projects::ApplicationController
+ include ExtractsPath
+
+ before_filter :require_non_empty_project, except: [:new, :create]
+ before_filter :assign_ref_vars
+ before_filter :authorize_download_code!
+ def show
if tree.entries.empty?
if @repository.blob_at(@commit.id, @path)
- redirect_to project_blob_path(@project, File.join(@ref, @path)) and return
+ redirect_to(
+ namespace_project_blob_path(@project.namespace, @project,
+ File.join(@ref, @path))
+ ) and return
else
return not_found!
end
diff --git a/app/controllers/projects/uploads_controller.rb b/app/controllers/projects/uploads_controller.rb
new file mode 100644
index 00000000000..9020e86c44e
--- /dev/null
+++ b/app/controllers/projects/uploads_controller.rb
@@ -0,0 +1,35 @@
+class Projects::UploadsController < Projects::ApplicationController
+ layout 'project'
+
+ before_filter :project
+
+ def create
+ link_to_file = ::Projects::UploadService.new(project, params[:file]).
+ execute
+
+ respond_to do |format|
+ if link_to_file
+ format.json do
+ render json: { link: link_to_file }
+ end
+ else
+ format.json do
+ render json: 'Invalid file.', status: :unprocessable_entity
+ end
+ end
+ end
+ end
+
+ def show
+ uploader = FileUploader.new(project, params[:secret])
+
+ return redirect_to uploader.url unless uploader.file_storage?
+
+ uploader.retrieve_from_store!(params[:filename])
+
+ return not_found! unless uploader.file.exists?
+
+ disposition = uploader.image? ? 'inline' : 'attachment'
+ send_file uploader.file.path, disposition: disposition
+ end
+end
diff --git a/app/controllers/projects/wikis_controller.rb b/app/controllers/projects/wikis_controller.rb
index 0e03956e738..69824dca944 100644
--- a/app/controllers/projects/wikis_controller.rb
+++ b/app/controllers/projects/wikis_controller.rb
@@ -16,16 +16,16 @@ class Projects::WikisController < Projects::ApplicationController
if @page
render 'show'
elsif file = @project_wiki.find_file(params[:id], params[:version_id])
- if file.on_disk?
- send_file file.on_disk_path, disposition: 'inline'
- else
- send_data(
- file.raw_data,
- type: file.mime_type,
- disposition: 'inline',
- filename: file.name
- )
- end
+ if file.on_disk?
+ send_file file.on_disk_path, disposition: 'inline'
+ else
+ send_data(
+ file.raw_data,
+ type: file.mime_type,
+ disposition: 'inline',
+ filename: file.name
+ )
+ end
else
return render('empty') unless can?(current_user, :write_wiki, @project)
@page = WikiPage.new(@project_wiki)
@@ -45,7 +45,7 @@ class Projects::WikisController < Projects::ApplicationController
return render('empty') unless can?(current_user, :write_wiki, @project)
if @page.update(content, format, message)
- redirect_to [@project, @page], notice: 'Wiki was successfully updated.'
+ redirect_to [@project.namespace.becomes(Namespace), @project, @page], notice: 'Wiki was successfully updated.'
else
render 'edit'
end
@@ -55,7 +55,10 @@ class Projects::WikisController < Projects::ApplicationController
@page = WikiPage.new(@project_wiki)
if @page.create(wiki_params)
- redirect_to project_wiki_path(@project, @page), notice: 'Wiki was successfully updated.'
+ redirect_to(
+ namespace_project_wiki_path(@project.namespace, @project, @page),
+ notice: 'Wiki was successfully updated.'
+ )
else
render action: "edit"
end
@@ -65,7 +68,10 @@ class Projects::WikisController < Projects::ApplicationController
@page = @project_wiki.find_page(params[:id])
unless @page
- redirect_to(project_wiki_path(@project, :home), notice: "Page not found")
+ redirect_to(
+ namespace_project_wiki_path(@project.namespace, @project, :home),
+ notice: "Page not found"
+ )
end
end
@@ -73,7 +79,10 @@ class Projects::WikisController < Projects::ApplicationController
@page = @project_wiki.find_page(params[:id])
@page.delete if @page
- redirect_to project_wiki_path(@project, :home), notice: "Page was successfully deleted"
+ redirect_to(
+ namespace_project_wiki_path(@project.namespace, @project, :home),
+ notice: "Page was successfully deleted"
+ )
end
def git_access
diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb
index f81fc29677b..5486a97e51d 100644
--- a/app/controllers/projects_controller.rb
+++ b/app/controllers/projects_controller.rb
@@ -4,26 +4,30 @@ class ProjectsController < ApplicationController
before_filter :repository, except: [:new, :create]
# Authorize
- before_filter :authorize_read_project!, except: [:index, :new, :create]
- before_filter :authorize_admin_project!, only: [:edit, :update, :destroy, :transfer, :archive, :unarchive, :retry_import]
+ before_filter :authorize_admin_project!, only: [:edit, :update, :destroy, :transfer, :archive, :unarchive]
+ before_filter :set_title, only: [:new, :create]
+ before_filter :event_filter, only: :show
layout 'navless', only: [:new, :create, :fork]
- before_filter :set_title, only: [:new, :create]
def new
@project = Project.new
end
def edit
- render 'edit', layout: "project_settings"
+ render 'edit', layout: 'project_settings'
end
def create
@project = ::Projects::CreateService.new(current_user, project_params).execute
- flash[:notice] = 'Project was successfully created.' if @project.saved?
- respond_to do |format|
- format.js
+ if @project.saved?
+ redirect_to(
+ project_path(@project),
+ notice: 'Project was successfully created.'
+ )
+ else
+ render 'new'
end
end
@@ -33,67 +37,59 @@ class ProjectsController < ApplicationController
respond_to do |format|
if status
flash[:notice] = 'Project was successfully updated.'
- format.html { redirect_to edit_project_path(@project), notice: 'Project was successfully updated.' }
+ format.html do
+ redirect_to(
+ edit_project_path(@project),
+ notice: 'Project was successfully updated.'
+ )
+ end
format.js
else
- format.html { render "edit", layout: "project_settings" }
+ format.html { render 'edit', layout: 'project_settings' }
format.js
end
end
end
def transfer
- ::Projects::TransferService.new(project, current_user, project_params).execute
+ transfer_params = params.permit(:new_namespace_id)
+ ::Projects::TransferService.new(project, current_user, transfer_params).execute
+ if @project.errors[:namespace_id].present?
+ flash[:alert] = @project.errors[:namespace_id].first
+ end
end
def show
if @project.import_in_progress?
- redirect_to import_project_path(@project)
+ redirect_to namespace_project_import_path(@project.namespace, @project)
return
end
- 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)
@show_star = !(current_user && current_user.starred?(@project))
respond_to do |format|
format.html do
- if @project.empty_repo?
- render "projects/empty", layout: user_layout
+ if @project.repository_exists?
+ if @project.empty_repo?
+ render 'projects/empty', layout: user_layout
+ else
+ @last_push = current_user.recent_push(@project.id) if current_user
+ render :show, layout: user_layout
+ end
else
- @last_push = current_user.recent_push(@project.id) if current_user
- render :show, layout: user_layout
+ render 'projects/no_repo', layout: user_layout
end
end
- format.json { pager_json("events/_events", @events.count) }
- end
- end
- def import
- if @project.import_finished?
- redirect_to @project
- return
- end
- end
-
- def retry_import
- unless @project.import_failed?
- redirect_to import_project_path(@project)
- end
-
- @project.import_url = project_params[:import_url]
-
- if @project.save
- @project.reload
- @project.import_retry
+ format.json do
+ @events = @project.events.recent
+ @events = event_filter.apply_filter(@events).with_associations
+ @events = @events.limit(limit).offset(params[:offset] || 0)
+ pager_json('events/_events', @events.count)
+ end
end
-
- redirect_to import_project_path(@project)
end
def destroy
@@ -103,10 +99,10 @@ class ProjectsController < ApplicationController
respond_to do |format|
format.html do
- flash[:alert] = "Project deleted."
+ flash[:alert] = 'Project deleted.'
- if request.referer.include?("/admin")
- redirect_to admin_projects_path
+ if request.referer.include?('/admin')
+ redirect_to admin_namespaces_projects_path
else
redirect_to projects_dashboard_path
end
@@ -114,35 +110,21 @@ class ProjectsController < ApplicationController
end
end
- def fork
- @forked_project = ::Projects::ForkService.new(project, current_user).execute
-
- respond_to do |format|
- format.html do
- if @forked_project.saved? && @forked_project.forked?
- redirect_to(@forked_project, notice: 'Project was successfully forked.')
- else
- @title = 'Fork project'
- render "fork"
- end
- end
- format.js
- end
- end
-
def autocomplete_sources
note_type = params['type']
note_id = params['type_id']
- participants = ::Projects::ParticipantsService.new(@project).execute(note_type, note_id)
+ autocomplete = ::Projects::AutocompleteService.new(@project)
+ participants = ::Projects::ParticipantsService.new(@project, current_user).execute(note_type, note_id)
+
@suggestions = {
- emojis: Emoji.names.map { |e| { name: e, path: view_context.image_url("emoji/#{e}.png") } },
- issues: @project.issues.select([:iid, :title, :description]),
- mergerequests: @project.merge_requests.select([:iid, :title, :description]),
+ emojis: autocomplete_emojis,
+ issues: autocomplete.issues,
+ mergerequests: autocomplete.merge_requests,
members: participants
}
respond_to do |format|
- format.json { render :json => @suggestions }
+ format.json { render json: @suggestions }
end
end
@@ -151,7 +133,7 @@ class ProjectsController < ApplicationController
@project.archive!
respond_to do |format|
- format.html { redirect_to @project }
+ format.html { redirect_to project_path(@project) }
end
end
@@ -160,19 +142,7 @@ class ProjectsController < ApplicationController
@project.unarchive!
respond_to do |format|
- format.html { redirect_to @project }
- end
- end
-
- def upload_image
- link_to_image = ::Projects::ImageService.new(repository, params, root_url).execute
-
- respond_to do |format|
- if link_to_image
- format.json { render json: { link: link_to_image } }
- else
- format.json { render json: "Invalid file.", status: :unprocessable_entity }
- end
+ format.html { redirect_to project_path(@project) }
end
end
@@ -182,30 +152,36 @@ class ProjectsController < ApplicationController
render json: { star_count: @project.star_count }
end
- private
-
- def upload_path
- base_dir = FileUploader.generate_dir
- File.join(repository.path_with_namespace, base_dir)
+ def markdown_preview
+ render text: view_context.markdown(params[:md_text])
end
- def accepted_images
- %w(png jpg jpeg gif)
- end
+ private
def set_title
@title = 'New Project'
end
def user_layout
- current_user ? "projects" : "public_projects"
+ current_user ? 'projects' : 'public_projects'
end
def project_params
params.require(:project).permit(
:name, :path, :description, :issues_tracker, :tag_list,
:issues_enabled, :merge_requests_enabled, :snippets_enabled, :issues_tracker_id, :default_branch,
- :wiki_enabled, :visibility_level, :import_url, :last_activity_at, :namespace_id
+ :wiki_enabled, :visibility_level, :import_url, :last_activity_at, :namespace_id, :avatar
)
end
+
+ def autocomplete_emojis
+ Rails.cache.fetch("autocomplete-emoji-#{Emoji::VERSION}") do
+ Emoji.names.map do |e|
+ {
+ name: e,
+ path: view_context.image_url("emoji/#{e}.png")
+ }
+ end
+ end
+ end
end
diff --git a/app/controllers/registrations_controller.rb b/app/controllers/registrations_controller.rb
index 6d3214b70a8..38d116a4ee3 100644
--- a/app/controllers/registrations_controller.rb
+++ b/app/controllers/registrations_controller.rb
@@ -1,6 +1,10 @@
class RegistrationsController < Devise::RegistrationsController
before_filter :signup_enabled?
+ def new
+ redirect_to(new_user_session_path)
+ end
+
def destroy
current_user.destroy
@@ -15,18 +19,20 @@ class RegistrationsController < Devise::RegistrationsController
super
end
- def after_sign_up_path_for(resource)
+ def after_sign_up_path_for(_resource)
new_user_session_path
end
- def after_inactive_sign_up_path_for(resource)
+ def after_inactive_sign_up_path_for(_resource)
new_user_session_path
end
private
def signup_enabled?
- redirect_to new_user_session_path unless Gitlab.config.gitlab.signup_enabled
+ unless current_application_settings.signup_enabled?
+ redirect_to(new_user_session_path)
+ end
end
def sign_up_params
diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb
index 5ced98152a5..7b6982c5074 100644
--- a/app/controllers/sessions_controller.rb
+++ b/app/controllers/sessions_controller.rb
@@ -1,16 +1,16 @@
class SessionsController < Devise::SessionsController
-
def new
- redirect_path = if request.referer.present? && (params['redirect_to_referer'] == 'yes')
- referer_uri = URI(request.referer)
- if referer_uri.host == Gitlab.config.gitlab.host
- referer_uri.path
- else
- request.fullpath
- end
- else
- request.fullpath
- end
+ redirect_path =
+ if request.referer.present? && (params['redirect_to_referer'] == 'yes')
+ referer_uri = URI(request.referer)
+ if referer_uri.host == Gitlab.config.gitlab.host
+ referer_uri.path
+ else
+ request.fullpath
+ end
+ else
+ request.fullpath
+ end
# Prevent a 'you are already signed in' message directly after signing:
# we should never redirect to '/users/sign_in' after signing in successfully.
diff --git a/app/controllers/snippets_controller.rb b/app/controllers/snippets_controller.rb
index bf3312fedc8..6ac048e4b83 100644
--- a/app/controllers/snippets_controller.rb
+++ b/app/controllers/snippets_controller.rb
@@ -27,7 +27,7 @@ class SnippetsController < ApplicationController
@snippets = SnippetsFinder.new.execute(current_user, {
filter: :by_user,
user: @user,
- scope: params[:scope]}).
+ scope: params[:scope] }).
page(params[:page]).per(20)
if @user == current_user
@@ -79,7 +79,7 @@ class SnippetsController < ApplicationController
@snippet.content,
type: 'text/plain; charset=utf-8',
disposition: 'inline',
- filename: @snippet.file_name
+ filename: @snippet.sanitized_file_name
)
end
@@ -106,6 +106,7 @@ class SnippetsController < ApplicationController
def set_title
@title = 'Snippets'
+ @title_url = snippets_path
end
def snippet_params
diff --git a/app/controllers/uploads_controller.rb b/app/controllers/uploads_controller.rb
new file mode 100644
index 00000000000..b096c3913e1
--- /dev/null
+++ b/app/controllers/uploads_controller.rb
@@ -0,0 +1,24 @@
+class UploadsController < ApplicationController
+ skip_before_filter :authenticate_user!, :reject_blocked!
+ before_filter :authorize_access
+
+ def show
+ model = params[:model].camelize.constantize.find(params[:id])
+ uploader = model.send(params[:mounted_as])
+
+ return not_found! if model.respond_to?(:project) && !can?(current_user, :read_project, model.project)
+
+ return redirect_to uploader.url unless uploader.file_storage?
+
+ return not_found! unless uploader.file.exists?
+
+ disposition = uploader.image? ? 'inline' : 'attachment'
+ send_file uploader.file.path, disposition: disposition
+ end
+
+ def authorize_access
+ unless params[:mounted_as] == 'avatar'
+ authenticate_user! && reject_blocked!
+ end
+ end
+end
diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb
index 0b442f5383a..8a13394dbac 100644
--- a/app/controllers/users_controller.rb
+++ b/app/controllers/users_controller.rb
@@ -1,28 +1,43 @@
class UsersController < ApplicationController
- skip_before_filter :authenticate_user!, only: [:show]
+ skip_before_filter :authenticate_user!
+ before_filter :set_user
layout :determine_layout
def show
- @user = User.find_by_username!(params[:username])
-
- unless current_user || @user.public_profile?
- return authenticate_user!
- end
-
- # Projects user can view
- authorized_projects_ids = ProjectsFinder.new.execute(current_user).pluck(:id)
+ @contributed_projects = Project.
+ where(id: authorized_projects_ids & @user.contributed_projects_ids).
+ in_group_namespace.
+ includes(:namespace).
+ reject(&:forked?)
@projects = @user.personal_projects.
- where(id: authorized_projects_ids)
+ where(id: authorized_projects_ids).includes(:namespace)
# Collect only groups common for both users
@groups = @user.groups & GroupsFinder.new.execute(current_user)
# Get user activity feed for projects common for both users
@events = @user.recent_events.
- where(project_id: authorized_projects_ids).limit(20)
+ where(project_id: authorized_projects_ids).
+ with_associations.limit(30)
@title = @user.name
+ @title_url = user_path(@user)
+
+ respond_to do |format|
+ format.html
+ format.atom { render layout: false }
+ end
+ end
+
+ def calendar
+ projects = Project.where(id: authorized_projects_ids & @user.contributed_projects_ids)
+ calendar = Gitlab::CommitsCalendar.new(projects, @user)
+ @timestamps = calendar.timestamps
+ @starting_year = calendar.starting_year
+ @starting_month = calendar.starting_month
+
+ render 'calendar', layout: false
end
def determine_layout
@@ -32,4 +47,20 @@ class UsersController < ApplicationController
'public_users'
end
end
+
+ private
+
+ def set_user
+ @user = User.find_by_username!(params[:username])
+
+ unless current_user || @user.public_profile?
+ return authenticate_user!
+ end
+ end
+
+ def authorized_projects_ids
+ # Projects user can view
+ @authorized_projects_ids ||=
+ ProjectsFinder.new.execute(current_user).pluck(:id)
+ end
end
diff --git a/app/finders/issuable_finder.rb b/app/finders/issuable_finder.rb
index d0574240511..e1477510065 100644
--- a/app/finders/issuable_finder.rb
+++ b/app/finders/issuable_finder.rb
@@ -33,6 +33,7 @@ class IssuableFinder
items = by_search(items)
items = by_milestone(items)
items = by_assignee(items)
+ items = by_author(items)
items = by_label(items)
items = sort(items)
end
@@ -125,6 +126,14 @@ class IssuableFinder
items
end
+ def by_author(items)
+ if params[:author_id].present?
+ items = items.where(author_id: (params[:author_id] == '0' ? nil : params[:author_id]))
+ end
+
+ items
+ end
+
def by_label(items)
if params[:label_name].present?
label_names = params[:label_name].split(",")
diff --git a/app/finders/notes_finder.rb b/app/finders/notes_finder.rb
index bef82d7f0fd..ab252821b52 100644
--- a/app/finders/notes_finder.rb
+++ b/app/finders/notes_finder.rb
@@ -7,20 +7,21 @@ class NotesFinder
# Default to 0 to remain compatible with old clients
last_fetched_at = Time.at(params.fetch(:last_fetched_at, 0).to_i)
- notes = case target_type
- when "commit"
- project.notes.for_commit_id(target_id).not_inline.fresh
- when "issue"
- project.issues.find(target_id).notes.inc_author.fresh
- when "merge_request"
- project.merge_requests.find(target_id).mr_and_commit_notes.inc_author.fresh
- when "snippet", "project_snippet"
- project.snippets.find(target_id).notes.fresh
- else
- raise 'invalid target_type'
- end
+ notes =
+ case target_type
+ when "commit"
+ project.notes.for_commit_id(target_id).not_inline
+ when "issue"
+ project.issues.find(target_id).notes.inc_author
+ when "merge_request"
+ project.merge_requests.find(target_id).mr_and_commit_notes.inc_author
+ when "snippet", "project_snippet"
+ project.snippets.find(target_id).notes
+ else
+ raise 'invalid target_type'
+ end
# Use overlapping intervals to avoid worrying about race conditions
- notes.where('updated_at > ?', last_fetched_at - FETCH_OVERLAP)
+ notes.where('updated_at > ?', last_fetched_at - FETCH_OVERLAP).fresh
end
end
diff --git a/app/finders/snippets_finder.rb b/app/finders/snippets_finder.rb
index 4b0c69f2d2f..07b5759443b 100644
--- a/app/finders/snippets_finder.rb
+++ b/app/finders/snippets_finder.rb
@@ -40,7 +40,7 @@ class SnippetsFinder
when 'are_public' then
snippets.are_public
else
- snippets
+ snippets
end
else
snippets.public_and_internal
diff --git a/app/finders/trending_projects_finder.rb b/app/finders/trending_projects_finder.rb
index 32d7968924a..a79bd47d986 100644
--- a/app/finders/trending_projects_finder.rb
+++ b/app/finders/trending_projects_finder.rb
@@ -8,7 +8,7 @@ class TrendingProjectsFinder
# for period of time - ex. month
projects.joins(:notes).where('notes.created_at > ?', start_date).
select("projects.*, count(notes.id) as ncount").
- group("projects.id").order("ncount DESC")
+ group("projects.id").reorder("ncount DESC")
end
private
diff --git a/app/helpers/appearances_helper.rb b/app/helpers/appearances_helper.rb
index 96e5d43a369..bb8d5683807 100644
--- a/app/helpers/appearances_helper.rb
+++ b/app/helpers/appearances_helper.rb
@@ -14,4 +14,8 @@ module AppearancesHelper
def brand_text
nil
end
+
+ def brand_header_logo
+ image_tag 'logo-white.png'
+ end
end
diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb
index 021bd0a494c..365de3595cd 100644
--- a/app/helpers/application_helper.rb
+++ b/app/helpers/application_helper.rb
@@ -5,8 +5,9 @@ module ApplicationHelper
COLOR_SCHEMES = {
1 => 'white',
2 => 'dark',
- 3 => 'solarized-dark',
- 4 => 'monokai',
+ 3 => 'solarized-light',
+ 4 => 'solarized-dark',
+ 5 => 'monokai',
}
COLOR_SCHEMES.default = 'white'
@@ -49,6 +50,44 @@ module ApplicationHelper
args.any? { |v| v.to_s.downcase == action_name }
end
+ def project_icon(project_id, options = {})
+ project =
+ if project_id.is_a?(Project)
+ project = project_id
+ else
+ Project.find_with_namespace(project_id)
+ end
+
+ if project.avatar.present?
+ image_tag project.avatar.url, options
+ elsif project.avatar_in_git
+ image_tag namespace_project_avatar_path(project.namespace, project), options
+ else # generated icon
+ project_identicon(project, options)
+ end
+ end
+
+ def project_identicon(project, options = {})
+ allowed_colors = {
+ red: 'FFEBEE',
+ purple: 'F3E5F5',
+ indigo: 'E8EAF6',
+ blue: 'E3F2FD',
+ teal: 'E0F2F1',
+ orange: 'FBE9E7',
+ gray: 'EEEEEE'
+ }
+
+ options[:class] ||= ''
+ options[:class] << ' identicon'
+ bg_key = project.id % 7
+ style = "background-color: ##{ allowed_colors.values[bg_key] }; color: #555"
+
+ content_tag(:div, class: options[:class], style: style) do
+ project.name[0, 1].upcase
+ end
+ end
+
def group_icon(group_path)
group = Group.find_by(path: group_path)
if group && group.avatar.present?
@@ -81,24 +120,24 @@ module ApplicationHelper
if project.repo_exists?
time_ago_with_tooltip(project.repository.commit.committed_date)
else
- "Never"
+ 'Never'
end
rescue
- "Never"
+ 'Never'
end
def grouped_options_refs
repository = @project.repository
options = [
- ["Branches", repository.branch_names],
- ["Tags", VersionSorter.rsort(repository.tag_names)]
+ ['Branches', repository.branch_names],
+ ['Tags', VersionSorter.rsort(repository.tag_names)]
]
# If reference is commit id - we should add it to branch/tag selectbox
if(@ref && !options.flatten.include?(@ref) &&
@ref =~ /^[0-9a-zA-Z]{6,52}$/)
- options << ["Commit", [@ref]]
+ options << ['Commit', [@ref]]
end
grouped_options_for_select(options, @ref || @project.default_branch)
@@ -114,6 +153,10 @@ module ApplicationHelper
Gitlab::Theme.css_class_by_id(current_user.try(:theme_id))
end
+ def theme_type
+ Gitlab::Theme.type_css_class_by_id(current_user.try(:theme_id))
+ end
+
def user_color_scheme_class
COLOR_SCHEMES[current_user.try(:color_scheme_id)] if defined?(current_user)
end
@@ -156,7 +199,7 @@ module ApplicationHelper
path = controller.controller_path.split('/')
namespace = path.first if path.second
- [namespace, controller.controller_name, controller.action_name].compact.join(":")
+ [namespace, controller.controller_name, controller.action_name].compact.join(':')
end
# shortcut for gitlab config
@@ -171,13 +214,13 @@ module ApplicationHelper
def search_placeholder
if @project && @project.persisted?
- "Search in this project"
+ 'Search in this project'
elsif @snippet || @snippets || @show_snippets
'Search snippets'
elsif @group && @group.persisted?
- "Search in this group"
+ 'Search in this group'
else
- "Search"
+ 'Search'
end
end
@@ -185,24 +228,10 @@ module ApplicationHelper
BroadcastMessage.current
end
- def highlight_js(&block)
- string = capture(&block)
-
- content_tag :div, class: "highlighted-data #{user_color_scheme_class}" do
- content_tag :div, class: 'highlight' do
- content_tag :pre do
- content_tag :code do
- string.html_safe
- end
- end
- end
- 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"),
+ 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()"
@@ -224,15 +253,6 @@ module ApplicationHelper
Gitlab::MarkdownHelper.gitlab_markdown?(filename)
end
- def spinner(text = nil, visible = false)
- css_class = "loading"
- css_class << " hide" unless visible
-
- content_tag :div, class: css_class do
- content_tag(:i, nil, class: 'fa fa-spinner fa-spin') + text
- end
- end
-
def link_to(name = nil, options = nil, html_options = nil, &block)
begin
uri = URI(options)
@@ -243,17 +263,17 @@ module ApplicationHelper
absolute_uri = nil
end
- # Add "nofollow" only to external links
+ # Add 'nofollow' only to external links
if host && host != Gitlab.config.gitlab.host && absolute_uri
if html_options
if html_options[:rel]
- html_options[:rel] << " nofollow"
+ html_options[:rel] << ' nofollow'
else
- html_options.merge!(rel: "nofollow")
+ html_options.merge!(rel: 'nofollow')
end
else
html_options = Hash.new
- html_options[:rel] = "nofollow"
+ html_options[:rel] = 'nofollow'
end
end
@@ -271,4 +291,42 @@ module ApplicationHelper
def promo_url
'https://' + promo_host
end
+
+ def page_filter_path(options={})
+ exist_opts = {
+ state: params[:state],
+ scope: params[:scope],
+ label_name: params[:label_name],
+ milestone_id: params[:milestone_id],
+ assignee_id: params[:assignee_id],
+ author_id: params[:author_id],
+ sort: params[:sort],
+ }
+
+ options = exist_opts.merge(options)
+
+ path = request.path
+ path << "?#{options.to_param}"
+ path
+ end
+
+ def outdated_browser?
+ browser.ie? && browser.version.to_i < 10
+ end
+
+ def path_to_key(key, admin = false)
+ if admin
+ admin_user_key_path(@user, key)
+ else
+ profile_key_path(key)
+ end
+ end
+
+ def nav_sidebar_class
+ if nav_menu_collapsed?
+ "page-sidebar-collapsed"
+ else
+ "page-sidebar-expanded"
+ end
+ end
end
diff --git a/app/helpers/application_settings_helper.rb b/app/helpers/application_settings_helper.rb
new file mode 100644
index 00000000000..1ee086da997
--- /dev/null
+++ b/app/helpers/application_settings_helper.rb
@@ -0,0 +1,21 @@
+module ApplicationSettingsHelper
+ def gravatar_enabled?
+ current_application_settings.gravatar_enabled?
+ end
+
+ def twitter_sharing_enabled?
+ current_application_settings.twitter_sharing_enabled?
+ end
+
+ def signup_enabled?
+ current_application_settings.signup_enabled?
+ end
+
+ def signin_enabled?
+ current_application_settings.signin_enabled?
+ end
+
+ def extra_sign_in_text
+ current_application_settings.sign_in_text
+ end
+end
diff --git a/app/helpers/blob_helper.rb b/app/helpers/blob_helper.rb
index 11fbf1baae7..798d62b3a09 100644
--- a/app/helpers/blob_helper.rb
+++ b/app/helpers/blob_helper.rb
@@ -1,18 +1,64 @@
module BlobHelper
- def highlightjs_class(blob_name)
- if blob_name.include?('.')
- ext = blob_name.split('.').last
- return 'language-' + ext
- else
- if no_highlight_files.include?(blob_name.downcase)
- 'no-highlight'
- else
- blob_name.downcase
- end
+ def highlight(blob_name, blob_content, nowrap = false)
+ formatter = Rugments::Formatters::HTML.new(
+ nowrap: nowrap,
+ cssclass: 'code highlight',
+ lineanchors: true,
+ lineanchorsid: 'LC'
+ )
+
+ begin
+ lexer = Rugments::Lexer.guess(filename: blob_name, source: blob_content)
+ rescue Rugments::Lexer::AmbiguousGuess
+ lexer = Rugments::Lexers::PlainText
end
+
+ formatter.format(lexer.lex(blob_content)).html_safe
end
def no_highlight_files
- %w(credits changelog copying copyright license authors)
+ %w(credits changelog news copying copyright license authors)
+ end
+
+ def edit_blob_link(project, ref, path, options = {})
+ blob =
+ begin
+ project.repository.blob_at(ref, path)
+ rescue
+ nil
+ end
+
+ if blob && blob.text?
+ text = 'Edit'
+ after = options[:after] || ''
+ from_mr = options[:from_merge_request_id]
+ link_opts = {}
+ link_opts[:from_merge_request_id] = from_mr if from_mr
+ cls = 'btn btn-small'
+ if allowed_tree_edit?(project, ref)
+ link_to(text,
+ namespace_project_edit_blob_path(project.namespace, project,
+ tree_join(ref, path),
+ link_opts),
+ class: cls
+ )
+ else
+ content_tag :span, text, class: cls + ' disabled'
+ end + after.html_safe
+ else
+ ''
+ end
+ end
+
+ def leave_edit_message
+ "Leave edit mode?\nAll unsaved changes will be lost."
+ end
+
+ def editing_preview_title(filename)
+ if Gitlab::MarkdownHelper.previewable?(filename)
+ 'Preview'
+ else
+ 'Preview changes'
+ end
end
end
diff --git a/app/helpers/branches_helper.rb b/app/helpers/branches_helper.rb
index 2ec2cc96157..4a5edf6d101 100644
--- a/app/helpers/branches_helper.rb
+++ b/app/helpers/branches_helper.rb
@@ -11,12 +11,7 @@ module BranchesHelper
def can_push_branch?(project, branch_name)
return false unless project.repository.branch_names.include?(branch_name)
- action = if project.protected_branch?(branch_name)
- :push_code_to_protected_branches
- else
- :push_code
- end
-
- current_user.can?(action, project)
+
+ ::Gitlab::GitAccess.can_push_to_branch?(current_user, project, branch_name)
end
end
diff --git a/app/helpers/commits_helper.rb b/app/helpers/commits_helper.rb
index 0e0532b65b2..5aae697e2f0 100644
--- a/app/helpers/commits_helper.rb
+++ b/app/helpers/commits_helper.rb
@@ -37,16 +37,26 @@ module CommitsHelper
# Add the root project link and the arrow icon
crumbs = content_tag(:li) do
- link_to(@project.path, project_commits_path(@project, @ref))
+ link_to(
+ @project.path,
+ namespace_project_commits_path(@project.namespace, @project, @ref)
+ )
end
if @path
parts = @path.split('/')
parts.each_with_index do |part, i|
- crumbs += content_tag(:li) do
+ crumbs << content_tag(:li) do
# The text is just the individual part, but the link needs all the parts before it
- link_to part, project_commits_path(@project, tree_join(@ref, parts[0..i].join('/')))
+ link_to(
+ part,
+ namespace_project_commits_path(
+ @project.namespace,
+ @project,
+ tree_join(@ref, parts[0..i].join('/'))
+ )
+ )
end
end
end
@@ -62,18 +72,55 @@ module CommitsHelper
# Returns the sorted alphabetically links to branches, separated by a comma
def commit_branches_links(project, branches)
- branches.sort.map { |branch| link_to(branch, project_tree_path(project, branch)) }.join(", ").html_safe
+ branches.sort.map do |branch|
+ link_to(
+ namespace_project_tree_path(project.namespace, project, branch)
+ ) do
+ content_tag :span, class: 'label label-gray' do
+ icon('code-fork') + ' ' + branch
+ end
+ end
+ end.join(" ").html_safe
+ end
+
+ # Returns the sorted links to tags, separated by a comma
+ def commit_tags_links(project, tags)
+ sorted = VersionSorter.rsort(tags)
+ sorted.map do |tag|
+ link_to(
+ namespace_project_commits_path(project.namespace, project,
+ project.repository.find_tag(tag).name)
+ ) do
+ content_tag :span, class: 'label label-gray' do
+ icon('tag') + ' ' + tag
+ end
+ end
+ end.join(" ").html_safe
end
def link_to_browse_code(project, commit)
if current_controller?(:projects, :commits)
if @repo.blob_at(commit.id, @path)
- return link_to "Browse File »", project_blob_path(project, tree_join(commit.id, @path)), class: "pull-right"
+ return link_to(
+ "Browse File »",
+ namespace_project_blob_path(project.namespace, project,
+ tree_join(commit.id, @path)),
+ class: "pull-right"
+ )
elsif @path.present?
- return link_to "Browse Dir »", project_tree_path(project, tree_join(commit.id, @path)), class: "pull-right"
+ return link_to(
+ "Browse Dir »",
+ namespace_project_tree_path(project.namespace, project,
+ tree_join(commit.id, @path)),
+ class: "pull-right"
+ )
end
end
- link_to "Browse Code »", project_tree_path(project, commit), class: "pull-right"
+ link_to(
+ "Browse Code »",
+ namespace_project_tree_path(project.namespace, project, commit),
+ class: "pull-right"
+ )
end
protected
@@ -87,19 +134,20 @@ module CommitsHelper
# avatar: true will prepend the avatar image
# size: size of the avatar image in px
def commit_person_link(commit, options = {})
- source_name = commit.send "#{options[:source]}_name".to_sym
- source_email = commit.send "#{options[:source]}_email".to_sym
+ source_name = clean(commit.send "#{options[:source]}_name".to_sym)
+ source_email = clean(commit.send "#{options[:source]}_email".to_sym)
user = User.find_for_commit(source_email, source_name)
person_name = user.nil? ? source_name : user.name
person_email = user.nil? ? source_email : user.email
- text = if options[:avatar]
- avatar = image_tag(avatar_icon(person_email, options[:size]), class: "avatar #{"s#{options[:size]}" if options[:size]}", width: options[:size], alt: "")
- %Q{#{avatar} <span class="commit-#{options[:source]}-name">#{person_name}</span>}
- else
- person_name
- end
+ text =
+ if options[:avatar]
+ avatar = image_tag(avatar_icon(person_email, options[:size]), class: "avatar #{"s#{options[:size]}" if options[:size]}", width: options[:size], alt: "")
+ %Q{#{avatar} <span class="commit-#{options[:source]}-name">#{person_name}</span>}
+ else
+ person_name
+ end
options = {
class: "commit-#{options[:source]}-link has_tooltip",
@@ -114,8 +162,11 @@ module CommitsHelper
end
def view_file_btn(commit_sha, diff, project)
- link_to project_blob_path(project, tree_join(commit_sha, diff.new_path)),
- class: 'btn btn-small view-file js-view-file' do
+ link_to(
+ namespace_project_blob_path(project.namespace, project,
+ tree_join(commit_sha, diff.new_path)),
+ class: 'btn btn-small view-file js-view-file'
+ ) do
raw('View file @') + content_tag(:span, commit_sha[0..6],
class: 'commit-short-id')
end
@@ -124,4 +175,8 @@ module CommitsHelper
def truncate_sha(sha)
Commit.truncate_sha(sha)
end
+
+ def clean(string)
+ Sanitize.clean(string, remove_contents: true)
+ end
end
diff --git a/app/helpers/compare_helper.rb b/app/helpers/compare_helper.rb
index 5ff19b88293..01847c6b807 100644
--- a/app/helpers/compare_helper.rb
+++ b/app/helpers/compare_helper.rb
@@ -1,7 +1,7 @@
module CompareHelper
def compare_to_mr_button?
@project.merge_requests_enabled &&
- params[:from].present? &&
+ params[:from].present? &&
params[:to].present? &&
@repository.branch_names.include?(params[:from]) &&
@repository.branch_names.include?(params[:to]) &&
@@ -10,6 +10,13 @@ module CompareHelper
end
def compare_mr_path
- new_project_merge_request_path(@project, merge_request: {source_branch: params[:to], target_branch: params[:from]})
+ new_namespace_project_merge_request_path(
+ @project.namespace,
+ @project,
+ merge_request: {
+ source_branch: params[:to],
+ target_branch: params[:from]
+ }
+ )
end
end
diff --git a/app/helpers/dashboard_helper.rb b/app/helpers/dashboard_helper.rb
index acc0eeb76b3..4dae96644c8 100644
--- a/app/helpers/dashboard_helper.rb
+++ b/app/helpers/dashboard_helper.rb
@@ -1,32 +1,11 @@
module DashboardHelper
- def filter_path(entity, options={})
- exist_opts = {
- state: params[:state],
- scope: params[:scope],
- project_id: params[:project_id],
- }
-
- options = exist_opts.merge(options)
-
- path = request.path
- path << "?#{options.to_param}"
- path
- end
-
- 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],
+ tag: params[:tag],
+ visibility_level: params[:visibility_level],
}
options = exist_opts.merge(options)
@@ -36,32 +15,11 @@ module DashboardHelper
path
end
- def assigned_entities_count(current_user, entity, scope = nil)
- items = current_user.send('assigned_' + entity.pluralize)
- get_count(items, scope)
+ def assigned_issues_dashboard_path
+ issues_dashboard_path(assignee_id: current_user.id)
end
- def authored_entities_count(current_user, entity, scope = nil)
- items = current_user.send(entity.pluralize)
- get_count(items, scope)
- end
-
- def authorized_entities_count(current_user, entity, scope = nil)
- items = entity.classify.constantize
- get_count(items, scope, true, current_user)
- end
-
- protected
-
- def get_count(items, scope, get_authorized = false, current_user = nil)
- items = items.opened
- if scope.kind_of?(Group)
- items = items.of_group(scope)
- elsif scope.kind_of?(Project)
- items = items.of_projects(scope)
- elsif get_authorized
- items = items.of_projects(current_user.authorized_projects)
- end
- items.count
+ def assigned_mrs_dashboard_path
+ merge_requests_dashboard_path(assignee_id: current_user.id)
end
end
diff --git a/app/helpers/diff_helper.rb b/app/helpers/diff_helper.rb
index cb50d89cba8..8c921cba543 100644
--- a/app/helpers/diff_helper.rb
+++ b/app/helpers/diff_helper.rb
@@ -117,4 +117,37 @@ module DiffHelper
[comments_left, comments_right]
end
+
+ def inline_diff_btn
+ params_copy = params.dup
+ params_copy[:view] = 'inline'
+
+ link_to url_for(params_copy), id: "commit-diff-viewtype", class: (params[:view] != 'parallel' ? 'btn active' : 'btn') do
+ 'Inline'
+ end
+ end
+
+ def parallel_diff_btn
+ params_copy = params.dup
+ params_copy[:view] = 'parallel'
+
+ link_to url_for(params_copy), id: "commit-diff-viewtype", class: (params[:view] == 'parallel' ? 'btn active' : 'btn') do
+ 'Side-by-side'
+ end
+ end
+
+ def submodule_link(blob, ref)
+ tree, commit = submodule_links(blob, ref)
+ commit_id = if commit.nil?
+ blob.id[0..10]
+ else
+ link_to "#{blob.id[0..10]}", commit
+ end
+
+ [
+ content_tag(:span, link_to(truncate(blob.name, length: 40), tree)),
+ '@',
+ content_tag(:span, commit_id, class: 'monospace'),
+ ].join(' ').html_safe
+ end
end
diff --git a/app/helpers/emails_helper.rb b/app/helpers/emails_helper.rb
new file mode 100644
index 00000000000..92cc9c426b8
--- /dev/null
+++ b/app/helpers/emails_helper.rb
@@ -0,0 +1,42 @@
+module EmailsHelper
+
+ # Google Actions
+ # https://developers.google.com/gmail/markup/reference/go-to-action
+ def email_action(url)
+ name = action_title(url)
+ if name
+ data = {
+ "@context" => "http://schema.org",
+ "@type" => "EmailMessage",
+ "action" => {
+ "@type" => "ViewAction",
+ "name" => name,
+ "url" => url,
+ }
+ }
+
+ content_tag :script, type: 'application/ld+json' do
+ data.to_json.html_safe
+ end
+ end
+ end
+
+ def action_title(url)
+ return unless url
+ ["merge_requests", "issues", "commit"].each do |action|
+ if url.split("/").include?(action)
+ return "View #{action.humanize.singularize}"
+ end
+ end
+ end
+
+ def add_email_highlight_css
+ Rugments::Themes::Github.render(scope: '.highlight')
+ end
+
+ def color_email_diff(diffcontent)
+ formatter = Rugments::Formatters::HTML.new(cssclass: 'highlight')
+ lexer = Rugments::Lexers::Diff.new
+ raw formatter.format(lexer.lex(diffcontent))
+ end
+end
diff --git a/app/helpers/events_helper.rb b/app/helpers/events_helper.rb
index 71f97fbb8c8..d38b546e1b2 100644
--- a/app/helpers/events_helper.rb
+++ b/app/helpers/events_helper.rb
@@ -10,70 +10,87 @@ module EventsHelper
end
def event_action_name(event)
- target = if event.target_type
- event.target_type.titleize.downcase
- else
- 'project'
- end
+ target = if event.target_type
+ if event.note?
+ event.note_target_type
+ else
+ event.target_type.titleize.downcase
+ end
+ else
+ 'project'
+ end
[event.action_name, target].join(" ")
end
def event_filter_link(key, tooltip)
key = key.to_s
- inactive = if @event_filter.active? key
- nil
- else
- 'inactive'
- end
-
- content_tag :div, class: "filter_icon #{inactive}" do
- link_to request.path, class: 'has_tooltip event_filter_link', id: "#{key}_event_filter", 'data-original-title' => tooltip do
- content_tag :i, nil, class: icon_for_event[key]
+ active = if @event_filter.active? key
+ 'active'
+ end
+
+ content_tag :li, class: "filter_icon #{active}" do
+ link_to request.path, class: 'has_tooltip event_filter_link', id: "#{key}_event_filter", 'data-original-title' => 'Filter by ' + tooltip.downcase do
+ icon(icon_for_event[key]) + content_tag(:span, ' ' + tooltip)
end
end
end
def icon_for_event
{
- EventFilter.push => 'fa fa-upload',
- EventFilter.merged => 'fa fa-check-square-o',
- EventFilter.comments => 'fa fa-comments',
- EventFilter.team => 'fa fa-user',
+ EventFilter.push => 'upload',
+ EventFilter.merged => 'check-square-o',
+ EventFilter.comments => 'comments',
+ EventFilter.team => 'user',
}
end
def event_feed_title(event)
- if event.issue?
- "#{event.author_name} #{event.action_name} issue ##{event.target_iid}: #{event.issue_title} at #{event.project_name}"
- elsif event.merge_request?
- "#{event.author_name} #{event.action_name} MR ##{event.target_iid}: #{event.merge_request_title} at #{event.project_name}"
- elsif event.push?
- "#{event.author_name} #{event.push_action_name} #{event.ref_type} #{event.ref_name} at #{event.project_name}"
- elsif event.membership_changed?
- "#{event.author_name} #{event.action_name} #{event.project_name}"
- elsif event.note? && event.note_commit?
- "#{event.author_name} commented on #{event.note_target_type} #{event.note_short_commit_id} at #{event.project_name}"
- elsif event.note?
- "#{event.author_name} commented on #{event.note_target_type} ##{truncate event.note_target_iid} at #{event.project_name}"
- else
- ""
+ words = []
+ words << event.author_name
+ words << event_action_name(event)
+
+ if event.push?
+ words << event.ref_type
+ words << event.ref_name
+ words << "at"
+ elsif event.commented?
+ if event.note_commit?
+ words << event.note_short_commit_id
+ else
+ words << "##{truncate event.note_target_iid}"
+ end
+ words << "at"
+ elsif event.target
+ words << "##{event.target_iid}:"
+ words << event.target.title if event.target.respond_to?(:title)
+ words << "at"
end
+
+ words << event.project_name
+
+ words.join(" ")
end
def event_feed_url(event)
if event.issue?
- project_issue_url(event.project, event.issue)
+ namespace_project_issue_url(event.project.namespace, event.project,
+ event.issue)
elsif event.merge_request?
- project_merge_request_url(event.project, event.merge_request)
+ namespace_project_merge_request_url(event.project.namespace,
+ event.project, event.merge_request)
elsif event.note? && event.note_commit?
- project_commit_url(event.project, event.note_target)
+ namespace_project_commit_url(event.project.namespace, event.project,
+ event.note_target)
elsif event.note?
if event.note_target
if event.note_commit?
- project_commit_path(event.project, event.note_commit_id, anchor: dom_id(event.target))
+ namespace_project_commit_path(event.project.namespace, event.project,
+ event.note_commit_id,
+ anchor: dom_id(event.target))
elsif event.note_project_snippet?
- project_snippet_path(event.project, event.note_target)
+ namespace_project_snippet_path(event.project.namespace,
+ event.project, event.note_target)
else
event_note_target_path(event)
end
@@ -81,12 +98,16 @@ module EventsHelper
elsif event.push?
if event.push_with_commits?
if event.commits_count > 1
- project_compare_url(event.project, from: event.commit_from, to: event.commit_to)
+ namespace_project_compare_url(event.project.namespace, event.project,
+ from: event.commit_from, to:
+ event.commit_to)
else
- project_commit_url(event.project, id: event.commit_to)
+ namespace_project_commit_url(event.project.namespace, event.project,
+ id: event.commit_to)
end
else
- project_commits_url(event.project, event.ref_name)
+ namespace_project_commits_url(event.project.namespace, event.project,
+ event.ref_name)
end
end
end
@@ -98,8 +119,6 @@ module EventsHelper
render "events/event_push", event: event
elsif event.merge_request?
render "events/event_merge_request", merge_request: event.merge_request
- elsif event.push?
- render "events/event_push", event: event
elsif event.note?
render "events/event_note", note: event.note
end
@@ -107,20 +126,30 @@ module EventsHelper
def event_note_target_path(event)
if event.note? && event.note_commit?
- project_commit_path(event.project, event.note_target)
+ namespace_project_commit_path(event.project.namespace, event.project,
+ event.note_target)
else
- polymorphic_path([event.project, event.note_target], anchor: dom_id(event.target))
+ polymorphic_path([event.project.namespace.becomes(Namespace),
+ 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, anchor: dom_id(event.target)), class: "commit_short_id" do
+ link_to(
+ namespace_project_commit_path(event.project.namespace, 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
+ link_to(namespace_project_snippet_path(event.project.namespace,
+ event.project,
+ event.note_target)) do
"#{event.note_target_type} ##{truncate event.note_target_id}"
end
else
@@ -145,4 +174,26 @@ module EventsHelper
rescue
"--broken encoding"
end
+
+ def event_to_atom(xml, event)
+ if event.proper?
+ xml.entry do
+ event_link = event_feed_url(event)
+ event_title = event_feed_title(event)
+ event_summary = event_feed_summary(event)
+
+ xml.id "tag:#{request.host},#{event.created_at.strftime("%Y-%m-%d")}:#{event.id}"
+ xml.link href: event_link
+ xml.title truncate(event_title, length: 80)
+ xml.updated event.created_at.strftime("%Y-%m-%dT%H:%M:%SZ")
+ xml.media :thumbnail, width: "40", height: "40", url: avatar_icon(event.author_email)
+ xml.author do |author|
+ xml.name event.author_name
+ xml.email event.author_email
+ end
+
+ xml.summary(type: "xhtml") { |x| x << event_summary unless event_summary.nil? }
+ end
+ end
+ end
end
diff --git a/app/helpers/git_helper.rb b/app/helpers/git_helper.rb
new file mode 100644
index 00000000000..09684955233
--- /dev/null
+++ b/app/helpers/git_helper.rb
@@ -0,0 +1,5 @@
+module GitHelper
+ def strip_gpg_signature(text)
+ text.gsub(/-----BEGIN PGP SIGNATURE-----(.*)-----END PGP SIGNATURE-----/m, "")
+ end
+end
diff --git a/app/helpers/gitlab_markdown_helper.rb b/app/helpers/gitlab_markdown_helper.rb
index 7d3cb749829..ab30f498c01 100644
--- a/app/helpers/gitlab_markdown_helper.rb
+++ b/app/helpers/gitlab_markdown_helper.rb
@@ -110,7 +110,7 @@ module GitlabMarkdownHelper
end
def link_to_ignore?(link)
- if link =~ /\#\w+/
+ if link =~ /\A\#\w+/
# ignore anchors like <a href="#my-header">
true
else
@@ -122,10 +122,11 @@ module GitlabMarkdownHelper
["http://","https://", "ftp://", "mailto:"]
end
- def rebuild_path(path)
- path.gsub!(/(#.*)/, "")
+ def rebuild_path(file_path)
+ file_path = file_path.dup
+ file_path.gsub!(/(#.*)/, "")
id = $1 || ""
- file_path = relative_file_path(path)
+ file_path = relative_file_path(file_path)
file_path = sanitize_slashes(file_path)
[
@@ -254,4 +255,16 @@ module GitlabMarkdownHelper
truncated
end
end
+
+ def cross_project_reference(project, entity)
+ path = project.path_with_namespace
+
+ if entity.kind_of?(Issue)
+ [path, entity.iid].join('#')
+ elsif entity.kind_of?(MergeRequest)
+ [path, entity.iid].join('!')
+ else
+ raise 'Not supported type'
+ end
+ end
end
diff --git a/app/helpers/gitlab_routing_helper.rb b/app/helpers/gitlab_routing_helper.rb
new file mode 100644
index 00000000000..f0eb50a0e17
--- /dev/null
+++ b/app/helpers/gitlab_routing_helper.rb
@@ -0,0 +1,31 @@
+# Shorter routing method for project and project items
+# Since update to rails 4.1.9 we are now allowed to use `/` in project routing
+# so we use nested routing for project resources which include project and
+# project namespace. To avoid writing long methods every time we define shortcuts for
+# some of routing.
+#
+# For example instead of this:
+#
+# namespace_project_merge_request_path(merge_request.project.namespace, merge_request.projects, merge_request)
+#
+# We can simply use shortcut:
+#
+# merge_request_path(merge_request)
+#
+module GitlabRoutingHelper
+ def project_path(project, *args)
+ namespace_project_path(project.namespace, project, *args)
+ end
+
+ def edit_project_path(project, *args)
+ edit_namespace_project_path(project.namespace, project, *args)
+ end
+
+ def issue_path(entity, *args)
+ namespace_project_issue_path(entity.project.namespace, entity.project, entity, *args)
+ end
+
+ def merge_request_path(entity, *args)
+ namespace_project_merge_request_path(entity.project.namespace, entity.project, entity, *args)
+ end
+end
diff --git a/app/helpers/graph_helper.rb b/app/helpers/graph_helper.rb
index 7cb1b6f8d1a..e1dda20de85 100644
--- a/app/helpers/graph_helper.rb
+++ b/app/helpers/graph_helper.rb
@@ -1,10 +1,10 @@
module GraphHelper
def get_refs(repo, commit)
refs = ""
- refs += commit.ref_names(repo).join(" ")
+ refs << commit.ref_names(repo).join(' ')
# append note count
- refs += "[#{@graph.notes[commit.id]}]" if @graph.notes[commit.id] > 0
+ refs << "[#{@graph.notes[commit.id]}]" if @graph.notes[commit.id] > 0
refs
end
diff --git a/app/helpers/groups_helper.rb b/app/helpers/groups_helper.rb
index 0dc53dedeb7..03fd461a462 100644
--- a/app/helpers/groups_helper.rb
+++ b/app/helpers/groups_helper.rb
@@ -6,7 +6,7 @@ module GroupsHelper
def leave_group_message(group)
"Are you sure you want to leave \"#{group}\" group?"
end
-
+
def should_user_see_group_roles?(user, group)
if user
user.is_admin? || group.members.exists?(user_id: user.id)
@@ -33,15 +33,11 @@ module GroupsHelper
title
end
- def group_filter_path(entity, options={})
- exist_opts = {
- status: params[:status]
- }
-
- options = exist_opts.merge(options)
-
- path = request.path
- path << "?#{options.to_param}"
- path
+ def group_settings_page?
+ if current_controller?('groups')
+ current_action?('edit') || current_action?('projects')
+ else
+ false
+ end
end
end
diff --git a/app/helpers/icons_helper.rb b/app/helpers/icons_helper.rb
index aaa8f8d0077..18260f0ed4d 100644
--- a/app/helpers/icons_helper.rb
+++ b/app/helpers/icons_helper.rb
@@ -1,21 +1,39 @@
module IconsHelper
+ # Creates an icon tag given icon name(s) and possible icon modifiers.
+ #
+ # Right now this method simply delegates directly to `fa_icon` from the
+ # font-awesome-rails gem, but should we ever use a different icon pack in the
+ # future we won't have to change hundreds of method calls.
+ def icon(names, options = {})
+ fa_icon(names, options)
+ end
+
+ def spinner(text = nil, visible = false)
+ css_class = 'loading'
+ css_class << ' hide' unless visible
+
+ content_tag :div, class: css_class do
+ icon('spinner spin') + text
+ end
+ end
+
def boolean_to_icon(value)
if value.to_s == "true"
- content_tag :i, nil, class: 'fa fa-circle cgreen'
+ icon('circle', class: 'cgreen')
else
- content_tag :i, nil, class: 'fa fa-power-off clgray'
+ icon('power-off', class: 'clgray')
end
end
def public_icon
- content_tag :i, nil, class: 'fa fa-globe'
+ icon('globe')
end
def internal_icon
- content_tag :i, nil, class: 'fa fa-shield'
+ icon('shield')
end
def private_icon
- content_tag :i, nil, class: 'fa fa-lock'
+ icon('lock')
end
end
diff --git a/app/helpers/issues_helper.rb b/app/helpers/issues_helper.rb
index 7671033b539..15c5dcb6a25 100644
--- a/app/helpers/issues_helper.rb
+++ b/app/helpers/issues_helper.rb
@@ -16,45 +16,25 @@ module IssuesHelper
def url_for_project_issues(project = @project)
return '' if project.nil?
- if project.used_default_issues_tracker? || !external_issues_tracker_enabled?
- project_issues_path(project)
- else
- url = Gitlab.config.issues_tracker[project.issues_tracker]['project_url']
- url.gsub(':project_id', project.id.to_s).
- gsub(':issues_tracker_id', project.issues_tracker_id.to_s)
- end
+ project.issues_tracker.project_url
end
def url_for_new_issue(project = @project)
return '' if project.nil?
- if project.used_default_issues_tracker? || !external_issues_tracker_enabled?
- url = new_project_issue_path project_id: project
- else
- issues_tracker = Gitlab.config.issues_tracker[project.issues_tracker]
- url = issues_tracker['new_issue_url']
- url.gsub(':project_id', project.id.to_s).
- gsub(':issues_tracker_id', project.issues_tracker_id.to_s)
- end
+ project.issues_tracker.new_issue_url
end
def url_for_issue(issue_iid, project = @project)
return '' if project.nil?
- if project.used_default_issues_tracker? || !external_issues_tracker_enabled?
- url = project_issue_url project_id: project, id: issue_iid
- else
- url = Gitlab.config.issues_tracker[project.issues_tracker]['issues_url']
- url.gsub(':id', issue_iid.to_s).
- gsub(':project_id', project.id.to_s).
- gsub(':issues_tracker_id', project.issues_tracker_id.to_s)
- end
+ project.issues_tracker.issue_url(issue_iid)
end
def title_for_issue(issue_iid, project = @project)
return '' if project.nil?
- if project.used_default_issues_tracker?
+ if project.default_issues_tracker?
issue = project.issues.where(iid: issue_iid).first
return issue.title if issue
end
@@ -62,9 +42,19 @@ module IssuesHelper
''
end
- # Checks if issues_tracker setting exists in gitlab.yml
- def external_issues_tracker_enabled?
- Gitlab.config.issues_tracker && Gitlab.config.issues_tracker.values.any?
+ def issue_timestamp(issue)
+ # Shows the created at time and the updated at time if different
+ ts = "#{time_ago_with_tooltip(issue.created_at, 'bottom', 'note_created_ago')}"
+ if issue.updated_at != issue.created_at
+ ts << capture_haml do
+ haml_tag :span do
+ haml_concat '&middot;'
+ haml_concat icon('edit', title: 'edited')
+ haml_concat time_ago_with_tooltip(issue.updated_at, 'bottom', 'issue_edited_ago')
+ end
+ end
+ end
+ ts.html_safe
end
def bulk_update_milestone_options
@@ -100,4 +90,21 @@ module IssuesHelper
'issue-box-open'
end
end
+
+ def issue_to_atom(xml, issue)
+ xml.entry do
+ xml.id namespace_project_issue_url(issue.project.namespace,
+ issue.project, issue)
+ xml.link href: namespace_project_issue_url(issue.project.namespace,
+ issue.project, issue)
+ xml.title truncate(issue.title, length: 80)
+ xml.updated issue.created_at.strftime("%Y-%m-%dT%H:%M:%SZ")
+ xml.media :thumbnail, width: "40", height: "40", url: avatar_icon(issue.author_email)
+ xml.author do |author|
+ xml.name issue.author_name
+ xml.email issue.author_email
+ end
+ xml.summary issue.title
+ end
+ end
end
diff --git a/app/helpers/labels_helper.rb b/app/helpers/labels_helper.rb
index 19d688c4bb8..49063491abf 100644
--- a/app/helpers/labels_helper.rb
+++ b/app/helpers/labels_helper.rb
@@ -7,21 +7,34 @@ module LabelsHelper
label_color = label.color || Label::DEFAULT_COLOR
text_color = text_color_for_bg(label_color)
- content_tag :span, class: 'label color-label', style: "background:#{label_color};color:#{text_color}" do
+ content_tag :span, class: 'label color-label', style: "background-color:#{label_color};color:#{text_color}" do
label.name
end
end
def suggested_colors
[
- '#D9534F',
- '#F0AD4E',
+ '#0033CC',
'#428BCA',
+ '#44AD8E',
+ '#A8D695',
'#5CB85C',
+ '#69D100',
+ '#004E00',
'#34495E',
'#7F8C8D',
+ '#A295D6',
+ '#5843AD',
'#8E44AD',
- '#FFECDB'
+ '#FFECDB',
+ '#AD4363',
+ '#D10069',
+ '#CC0033',
+ '#FF0000',
+ '#D9534F',
+ '#D1D100',
+ '#F0AD4E',
+ '#AD8D43'
]
end
diff --git a/app/helpers/merge_requests_helper.rb b/app/helpers/merge_requests_helper.rb
index fe6fd5832fc..3b1589da57f 100644
--- a/app/helpers/merge_requests_helper.rb
+++ b/app/helpers/merge_requests_helper.rb
@@ -1,25 +1,29 @@
module MergeRequestsHelper
def new_mr_path_from_push_event(event)
target_project = event.project.forked_from_project || event.project
- new_project_merge_request_path(
+ new_namespace_project_merge_request_path(
+ event.project.namespace,
event.project,
new_mr_from_push_event(event, target_project)
)
end
def new_mr_path_for_fork_from_push_event(event)
- new_project_merge_request_path(
+ new_namespace_project_merge_request_path(
+ event.project.namespace,
event.project,
new_mr_from_push_event(event, event.project.forked_from_project)
)
end
def new_mr_from_push_event(event, target_project)
- return :merge_request => {
- source_project_id: event.project.id,
- target_project_id: target_project.id,
- source_branch: event.branch_name,
- target_branch: target_project.repository.root_ref
+ return {
+ merge_request: {
+ source_project_id: event.project.id,
+ target_project_id: target_project.id,
+ source_branch: event.branch_name,
+ target_branch: target_project.repository.root_ref
+ }
}
end
diff --git a/app/helpers/milestones_helper.rb b/app/helpers/milestones_helper.rb
new file mode 100644
index 00000000000..47fa147dccf
--- /dev/null
+++ b/app/helpers/milestones_helper.rb
@@ -0,0 +1,9 @@
+module MilestonesHelper
+ def milestones_filter_path(opts = {})
+ if @project
+ namespace_project_milestones_path(@project.namespace, @project, opts)
+ elsif @group
+ group_milestones_path(@group, opts)
+ end
+ end
+end
diff --git a/app/helpers/namespaces_helper.rb b/app/helpers/namespaces_helper.rb
index bf25dce2301..2bcfde62830 100644
--- a/app/helpers/namespaces_helper.rb
+++ b/app/helpers/namespaces_helper.rb
@@ -25,4 +25,12 @@ module NamespacesHelper
hidden_field_tag(id, value, class: css_class)
end
+
+ def namespace_icon(namespace, size = 40)
+ if namespace.kind_of?(Group)
+ group_icon(namespace.path)
+ else
+ avatar_icon(namespace.owner.email, size)
+ end
+ end
end
diff --git a/app/helpers/nav_helper.rb b/app/helpers/nav_helper.rb
new file mode 100644
index 00000000000..2b03269800e
--- /dev/null
+++ b/app/helpers/nav_helper.rb
@@ -0,0 +1,5 @@
+module NavHelper
+ def nav_menu_collapsed?
+ cookies[:collapsed_nav] == 'true'
+ end
+end
diff --git a/app/helpers/notes_helper.rb b/app/helpers/notes_helper.rb
index 901052edec6..92ecb2abe4d 100644
--- a/app/helpers/notes_helper.rb
+++ b/app/helpers/notes_helper.rb
@@ -1,5 +1,5 @@
module NotesHelper
- # Helps to distinguish e.g. commit notes in mr notes list
+ # Helps to distinguish e.g. commit notes in mr notes list
def note_for_main_target?(note)
(@noteable.class.name == note.noteable_type && !note.for_diff_line?)
end
@@ -11,7 +11,11 @@ module NotesHelper
def link_to_commit_diff_line_note(note)
if note.for_commit_diff_line?
- link_to "#{note.diff_file_name}:L#{note.diff_new_line}", project_commit_path(@project, note.noteable, anchor: note.line_code)
+ link_to(
+ "#{note.diff_file_name}:L#{note.diff_new_line}",
+ namespace_project_commit_path(@project.namespace, @project,
+ note.noteable, anchor: note.line_code)
+ )
end
end
@@ -20,8 +24,10 @@ module NotesHelper
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')})"
+ haml_tag :span do
+ haml_concat '&middot;'
+ haml_concat icon('edit', title: 'edited')
+ haml_concat time_ago_with_tooltip(note.updated_at, 'bottom', 'note_edited_ago')
end
end
end
@@ -52,8 +58,11 @@ module NotesHelper
discussion_id: discussion_id
}
- button_tag '', class: 'btn add-diff-note js-add-diff-note-button',
- data: data, title: 'Add a comment to this line'
+ button_tag(class: 'btn add-diff-note js-add-diff-note-button',
+ data: data,
+ title: 'Add a comment to this line') do
+ icon('comment-o')
+ end
end
def link_to_reply_diff(note)
@@ -69,7 +78,7 @@ module NotesHelper
button_tag class: 'btn reply-btn js-discussion-reply-button',
data: data, title: 'Add a reply' do
- link_text = content_tag(:i, nil, class: 'fa fa-comment')
+ link_text = icon('comment')
link_text << ' Reply'
end
end
diff --git a/app/helpers/notifications_helper.rb b/app/helpers/notifications_helper.rb
index bad380e98a8..f771fe761ef 100644
--- a/app/helpers/notifications_helper.rb
+++ b/app/helpers/notifications_helper.rb
@@ -1,13 +1,13 @@
module NotificationsHelper
def notification_icon(notification)
if notification.disabled?
- content_tag :i, nil, class: 'fa fa-volume-off ns-mute'
+ icon('volume-off', class: 'ns-mute')
elsif notification.participating?
- content_tag :i, nil, class: 'fa fa-volume-down ns-part'
+ icon('volume-down', class: 'ns-part')
elsif notification.watch?
- content_tag :i, nil, class: 'fa fa-volume-up ns-watch'
+ icon('volume-up', class: 'ns-watch')
else
- content_tag :i, nil, class: 'fa fa-circle-o ns-default'
+ icon('circle-o', class: 'ns-default')
end
end
end
diff --git a/app/helpers/oauth_helper.rb b/app/helpers/oauth_helper.rb
index 7024483b8b3..1a0ad17b607 100644
--- a/app/helpers/oauth_helper.rb
+++ b/app/helpers/oauth_helper.rb
@@ -4,7 +4,7 @@ module OauthHelper
end
def default_providers
- [:twitter, :github, :google_oauth2, :ldap]
+ [:twitter, :github, :gitlab, :bitbucket, :google_oauth2, :ldap]
end
def enabled_oauth_providers
@@ -13,7 +13,13 @@ module OauthHelper
def enabled_social_providers
enabled_oauth_providers.select do |name|
- [:twitter, :github, :google_oauth2].include?(name.to_sym)
+ [:twitter, :gitlab, :github, :bitbucket, :google_oauth2].include?(name.to_sym)
end
end
+
+ def additional_providers
+ enabled_oauth_providers.reject{|provider| provider.to_s.starts_with?('ldap')}
+ end
+
+ extend self
end
diff --git a/app/helpers/profile_helper.rb b/app/helpers/profile_helper.rb
index 0b375558305..9e37e44732a 100644
--- a/app/helpers/profile_helper.rb
+++ b/app/helpers/profile_helper.rb
@@ -1,6 +1,6 @@
module ProfileHelper
def oauth_active_class(provider)
- if current_user.provider == provider.to_s
+ if current_user.identities.exists?(provider: provider.to_s)
'active'
end
end
@@ -10,10 +10,10 @@ module ProfileHelper
end
def show_profile_social_tab?
- enabled_social_providers.any? && !current_user.ldap_user?
+ enabled_social_providers.any?
end
def show_profile_remove_tab?
- gitlab_config.signup_enabled && !current_user.ldap_user?
+ signup_enabled?
end
end
diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb
index 883c1f63af6..a5d7372bbe5 100644
--- a/app/helpers/projects_helper.rb
+++ b/app/helpers/projects_helper.rb
@@ -4,7 +4,7 @@ module ProjectsHelper
end
def link_to_project(project)
- link_to project do
+ link_to [project.namespace.becomes(Namespace), project] do
title = content_tag(:span, project.name, class: 'project-name')
if project.namespace
@@ -42,12 +42,20 @@ module ProjectsHelper
def project_title(project)
if project.group
content_tag :span do
- link_to(simple_sanitize(project.group.name), group_path(project.group)) + " / " + project.name
+ link_to(
+ simple_sanitize(project.group.name), group_path(project.group)
+ ) + ' / ' +
+ link_to(simple_sanitize(project.name),
+ project_path(project))
end
else
owner = project.namespace.owner
content_tag :span do
- link_to(simple_sanitize(owner.name), user_path(owner)) + " / " + project.name
+ link_to(
+ simple_sanitize(owner.name), user_path(owner)
+ ) + ' / ' +
+ link_to(simple_sanitize(project.name),
+ project_path(project))
end
end
end
@@ -68,67 +76,13 @@ module ProjectsHelper
project_nav_tabs.include? name
end
- def selected_label?(label_name)
- params[:label_name].to_s.split(',').include?(label_name)
- end
-
- def labels_filter_path(label_name)
- label_name =
- if selected_label?(label_name)
- params[:label_name].split(',').reject { |l| l == label_name }.join(',')
- elsif params[:label_name].present?
- "#{params[:label_name]},#{label_name}"
- else
- label_name
- end
-
- project_filter_path(label_name: label_name)
- end
-
- def label_filter_class(label_name)
- if selected_label?(label_name)
- 'label-filter-item active'
- else
- 'label-filter-item light'
- end
- end
-
- def project_filter_path(options={})
- exist_opts = {
- state: params[:state],
- 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)
-
- path = request.path
- path << "?#{options.to_param}"
- path
- end
-
def project_active_milestones
@project.milestones.active.order("due_date, title ASC")
end
- 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]
- else
- [Gitlab.config.issues_tracker[tracker_key]['title'] || tracker_key, tracker_key]
- end
- end
-
- options_for_select(values, current_tracker)
- end
-
def link_to_toggle_star(title, starred, signed_in)
cls = 'star-btn'
- cls += ' disabled' unless signed_in
+ cls << ' disabled' unless signed_in
toggle_html = content_tag('span', class: 'toggle') do
toggle_text = if starred
@@ -137,7 +91,7 @@ module ProjectsHelper
' Star'
end
- content_tag('i', ' ', class: 'fa fa-star') + toggle_text
+ icon('star') + toggle_text
end
count_html = content_tag('span', class: 'count') do
@@ -149,19 +103,22 @@ module ProjectsHelper
class: cls,
method: :post,
remote: true,
- data: {type: 'json'}
+ data: { type: 'json' }
}
content_tag 'span', class: starred ? 'turn-on' : 'turn-off' do
- link_to toggle_star_project_path(@project), link_opts do
+ link_to(
+ toggle_star_namespace_project_path(@project.namespace, @project),
+ link_opts
+ ) do
toggle_html + ' ' + count_html
end
end
end
def link_to_toggle_fork
- out = content_tag(:i, '', class: 'fa fa-code-fork')
+ out = icon('code-fork')
out << ' Fork'
out << content_tag(:span, class: 'count') do
@project.forks_count.to_s
@@ -229,7 +186,13 @@ module ProjectsHelper
"Issues - " + title
end
elsif current_controller?(:blob)
- "#{@project.path}\/#{@blob.path} at #{@ref} - " + title
+ if current_action?(:new) || current_action?(:create)
+ "New file at #{@ref}"
+ elsif current_action?(:show)
+ "#{@blob.path} at #{@ref}"
+ elsif @blob
+ "Edit file #{@blob.path} at #{@ref}"
+ end
elsif current_controller?(:commits)
"Commits at #{@ref} - " + title
elsif current_controller?(:merge_requests)
@@ -270,13 +233,36 @@ module ProjectsHelper
def contribution_guide_url(project)
if project && project.repository.contribution_guide
- project_blob_path(project, tree_join(project.default_branch, project.repository.contribution_guide.name))
+ namespace_project_blob_path(
+ project.namespace,
+ project,
+ tree_join(project.default_branch,
+ project.repository.contribution_guide.name)
+ )
end
end
def hidden_pass_url(original_url)
result = URI(original_url)
- result.password = '*****' if result.password.present?
+ result.password = '*****' unless result.password.nil?
result
+ rescue
+ original_url
+ end
+
+ def project_wiki_path_with_version(proj, page, version, is_newest)
+ url_params = is_newest ? {} : { version_id: version }
+ namespace_project_wiki_path(proj.namespace, proj, page, url_params)
+ end
+
+ def project_status_css_class(status)
+ case status
+ when "started"
+ "active"
+ when "failed"
+ "danger"
+ when "finished"
+ "success"
+ end
end
end
diff --git a/app/helpers/search_helper.rb b/app/helpers/search_helper.rb
index 65b9408cfa1..cb829037697 100644
--- a/app/helpers/search_helper.rb
+++ b/app/helpers/search_helper.rb
@@ -52,16 +52,16 @@ module SearchHelper
ref = @ref || @project.repository.root_ref
[
- { label: "#{prefix} - Files", url: project_tree_path(@project, ref) },
- { label: "#{prefix} - Commits", url: project_commits_path(@project, ref) },
- { label: "#{prefix} - Network", url: project_network_path(@project, ref) },
- { label: "#{prefix} - Graph", url: project_graph_path(@project, ref) },
- { label: "#{prefix} - Issues", url: project_issues_path(@project) },
- { label: "#{prefix} - Merge Requests", url: project_merge_requests_path(@project) },
- { label: "#{prefix} - Milestones", url: project_milestones_path(@project) },
- { label: "#{prefix} - Snippets", url: project_snippets_path(@project) },
- { label: "#{prefix} - Team", url: project_team_index_path(@project) },
- { label: "#{prefix} - Wiki", url: project_wikis_path(@project) },
+ { label: "#{prefix} - Files", url: namespace_project_tree_path(@project.namespace, @project, ref) },
+ { label: "#{prefix} - Commits", url: namespace_project_commits_path(@project.namespace, @project, ref) },
+ { label: "#{prefix} - Network", url: namespace_project_network_path(@project.namespace, @project, ref) },
+ { label: "#{prefix} - Graph", url: namespace_project_graph_path(@project.namespace, @project, ref) },
+ { label: "#{prefix} - Issues", url: namespace_project_issues_path(@project.namespace, @project) },
+ { label: "#{prefix} - Merge Requests", url: namespace_project_merge_requests_path(@project.namespace, @project) },
+ { label: "#{prefix} - Milestones", url: namespace_project_milestones_path(@project.namespace, @project) },
+ { label: "#{prefix} - Snippets", url: namespace_project_snippets_path(@project.namespace, @project) },
+ { label: "#{prefix} - Team", url: namespace_project_team_index_path(@project.namespace, @project) },
+ { label: "#{prefix} - Wiki", url: namespace_project_wikis_path(@project.namespace, @project) },
]
else
[]
@@ -84,7 +84,7 @@ module SearchHelper
sorted_by_stars.non_archived.limit(limit).map do |p|
{
label: "project: #{search_result_sanitize(p.name_with_namespace)}",
- url: project_path(p)
+ url: namespace_project_path(p.namespace, p)
}
end
end
diff --git a/app/helpers/selects_helper.rb b/app/helpers/selects_helper.rb
index ab24367c455..796d805f219 100644
--- a/app/helpers/selects_helper.rb
+++ b/app/helpers/selects_helper.rb
@@ -17,4 +17,13 @@ module SelectsHelper
project_id = opts[:project_id] || @project.id
hidden_field_tag(id, value, class: css_class, 'data-placeholder' => placeholder, 'data-project-id' => project_id)
end
+
+ def groups_select_tag(id, opts = {})
+ css_class = "ajax-groups-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/snippets_helper.rb b/app/helpers/snippets_helper.rb
index b0abc2cae33..906cb12cd48 100644
--- a/app/helpers/snippets_helper.rb
+++ b/app/helpers/snippets_helper.rb
@@ -11,7 +11,8 @@ module SnippetsHelper
def reliable_snippet_path(snippet)
if snippet.project_id?
- project_snippet_path(snippet.project, snippet)
+ namespace_project_snippet_path(snippet.project.namespace,
+ snippet.project, snippet)
else
snippet_path(snippet)
end
diff --git a/app/helpers/sorting_helper.rb b/app/helpers/sorting_helper.rb
new file mode 100644
index 00000000000..bb12d43f397
--- /dev/null
+++ b/app/helpers/sorting_helper.rb
@@ -0,0 +1,96 @@
+module SortingHelper
+ def sort_options_hash
+ {
+ sort_value_name => sort_title_name,
+ sort_value_recently_updated => sort_title_recently_updated,
+ sort_value_oldest_updated => sort_title_oldest_updated,
+ sort_value_recently_created => sort_title_recently_created,
+ sort_value_oldest_created => sort_title_oldest_created,
+ sort_value_milestone_soon => sort_title_milestone_soon,
+ sort_value_milestone_later => sort_title_milestone_later,
+ sort_value_largest_repo => sort_title_largest_repo,
+ sort_value_recently_signin => sort_title_recently_signin,
+ sort_value_oldest_signin => sort_title_oldest_signin,
+ }
+ end
+
+ def sort_title_oldest_updated
+ 'Oldest updated'
+ end
+
+ def sort_title_recently_updated
+ 'Recently updated'
+ end
+
+ def sort_title_oldest_created
+ 'Oldest created'
+ end
+
+ def sort_title_recently_created
+ 'Recently created'
+ end
+
+ def sort_title_milestone_soon
+ 'Milestone due soon'
+ end
+
+ def sort_title_milestone_later
+ 'Milestone due later'
+ end
+
+ def sort_title_name
+ 'Name'
+ end
+
+ def sort_title_largest_repo
+ 'Largest repository'
+ end
+
+ def sort_title_recently_signin
+ 'Recent sign in'
+ end
+
+ def sort_title_oldest_signin
+ 'Oldest sign in'
+ end
+
+ def sort_value_oldest_updated
+ 'updated_asc'
+ end
+
+ def sort_value_recently_updated
+ 'updated_desc'
+ end
+
+ def sort_value_oldest_created
+ 'created_asc'
+ end
+
+ def sort_value_recently_created
+ 'created_desc'
+ end
+
+ def sort_value_milestone_soon
+ 'milestone_due_asc'
+ end
+
+ def sort_value_milestone_later
+ 'milestone_due_desc'
+ end
+
+ def sort_value_name
+ 'name_asc'
+ end
+
+ def sort_value_largest_repo
+ 'repository_size_desc'
+ end
+
+ def sort_value_recently_signin
+ 'recent_sign_in'
+ end
+
+ def sort_value_oldest_signin
+ 'oldest_sign_in'
+ end
+end
diff --git a/app/helpers/submodule_helper.rb b/app/helpers/submodule_helper.rb
index 09e5c08e621..525266fb3b5 100644
--- a/app/helpers/submodule_helper.rb
+++ b/app/helpers/submodule_helper.rb
@@ -2,22 +2,25 @@ module SubmoduleHelper
include Gitlab::ShellAdapter
# links to files listing for submodule if submodule is a project on this server
- def submodule_links(submodule_item)
- url = @repository.submodule_url_for(@ref, submodule_item.path)
+ def submodule_links(submodule_item, ref = nil)
+ url = @repository.submodule_url_for(ref, submodule_item.path)
- return url, nil unless url =~ /([^\/:]+\/[^\/]+\.git)\Z/
+ return url, nil unless url =~ /([^\/:]+)\/([^\/]+\.git)\Z/
- project = $1
+ namespace = $1
+ project = $2
project.chomp!('.git')
- if self_url?(url, project)
- return project_path(project), project_tree_path(project, submodule_item.id)
+ if self_url?(url, namespace, project)
+ return namespace_project_path(namespace, project),
+ namespace_project_tree_path(namespace, project,
+ submodule_item.id)
elsif relative_self_url?(url)
relative_self_links(url, submodule_item.id)
elsif github_dot_com_url?(url)
- standard_links('github.com', project, submodule_item.id)
+ standard_links('github.com', namespace, project, submodule_item.id)
elsif gitlab_dot_com_url?(url)
- standard_links('gitlab.com', project, submodule_item.id)
+ standard_links('gitlab.com', namespace, project, submodule_item.id)
else
return url, nil
end
@@ -33,9 +36,10 @@ module SubmoduleHelper
url =~ /gitlab\.com[\/:][^\/]+\/[^\/]+\Z/
end
- def self_url?(url, project)
- return true if url == [ Gitlab.config.gitlab.url, '/', project, '.git' ].join('')
- url == gitlab_shell.url_to_repo(project)
+ def self_url?(url, namespace, project)
+ return true if url == [ Gitlab.config.gitlab.url, '/', namespace, '/',
+ project, '.git' ].join('')
+ url == gitlab_shell.url_to_repo([namespace, '/', project].join(''))
end
def relative_self_url?(url)
@@ -43,8 +47,8 @@ module SubmoduleHelper
url =~ /^((\.\/)?(\.\.\/))(?!(\.\.)|(.*\/)).*\.git\Z/ || url =~ /^((\.\/)?(\.\.\/){2})(?!(\.\.))([^\/]*)\/(?!(\.\.)|(.*\/)).*\.git\Z/
end
- def standard_links(host, project, commit)
- base = [ 'https://', host, '/', project ].join('')
+ def standard_links(host, namespace, project, commit)
+ base = [ 'https://', host, '/', namespace, '/', project ].join('')
return base, [ base, '/tree/', commit ].join('')
end
@@ -54,6 +58,7 @@ module SubmoduleHelper
else
base = [ @project.group.path, '/', url[/([^\/]*)\.git/, 1] ].join('')
end
- return project_path(base), project_tree_path(base, commit)
+ return namespace_project_path(base.namespace, base),
+ namespace_project_tree_path(base.namespace, base, commit)
end
end
diff --git a/app/helpers/tab_helper.rb b/app/helpers/tab_helper.rb
index bc43e078568..7a401a274d3 100644
--- a/app/helpers/tab_helper.rb
+++ b/app/helpers/tab_helper.rb
@@ -28,6 +28,10 @@ module TabHelper
# nav_link(controller: [:tree, :refs]) { "Hello" }
# # => '<li class="active">Hello</li>'
#
+ # # Several paths
+ # nav_link(path: ['tree#show', 'profile#show']) { "Hello" }
+ # # => '<li class="active">Hello</li>'
+ #
# # Shorthand path
# nav_link(path: 'tree#show') { "Hello" }
# # => '<li class="active">Hello</li>'
@@ -38,25 +42,7 @@ module TabHelper
#
# Returns a list item element String
def nav_link(options = {}, &block)
- if path = options.delete(:path)
- if path.respond_to?(:each)
- c = path.map { |p| p.split('#').first }
- a = path.map { |p| p.split('#').last }
- else
- c, a, _ = path.split('#')
- end
- else
- c = options.delete(:controller)
- a = options.delete(:action)
- end
-
- if c && a
- # When given both options, make sure BOTH are active
- klass = current_controller?(*c) && current_action?(*a) ? 'active' : ''
- else
- # Otherwise check EITHER option
- klass = current_controller?(*c) || current_action?(*a) ? 'active' : ''
- end
+ klass = active_nav_link?(options) ? 'active' : ''
# Add our custom class into the html_options, which may or may not exist
# and which may or may not already have a :class key
@@ -72,18 +58,47 @@ module TabHelper
end
end
+ def active_nav_link?(options)
+ if path = options.delete(:path)
+ unless path.respond_to?(:each)
+ path = [path]
+ end
+
+ path.any? do |single_path|
+ current_path?(single_path)
+ end
+ else
+ c = options.delete(:controller)
+ a = options.delete(:action)
+
+ if c && a
+ # When given both options, make sure BOTH are true
+ current_controller?(*c) && current_action?(*a)
+ else
+ # Otherwise check EITHER option
+ current_controller?(*c) || current_action?(*a)
+ end
+ end
+ end
+
+ def current_path?(path)
+ c, a, _ = path.split('#')
+ current_controller?(c) && current_action?(a)
+ end
+
def project_tab_class
return "active" if current_page?(controller: "/projects", action: :edit, id: @project)
if ['services', 'hooks', 'deploy_keys', 'team_members', 'protected_branches'].include? controller.controller_name
- "active"
+ "active"
end
end
def branches_tab_class
if current_controller?(:protected_branches) ||
current_controller?(:branches) ||
- current_page?(project_repository_path(@project))
+ current_page?(namespace_project_repository_path(@project.namespace,
+ @project))
'active'
end
end
diff --git a/app/helpers/tags_helper.rb b/app/helpers/tags_helper.rb
index ef89bb32c6d..fb85544df2d 100644
--- a/app/helpers/tags_helper.rb
+++ b/app/helpers/tags_helper.rb
@@ -6,7 +6,7 @@ module TagsHelper
def tag_list(project)
html = ''
project.tag_list.each do |tag|
- html += link_to tag, tag_path(tag)
+ html << link_to(tag, tag_path(tag))
end
html.html_safe
diff --git a/app/helpers/tree_helper.rb b/app/helpers/tree_helper.rb
index 9c611a1c147..b6fb7a8aa5a 100644
--- a/app/helpers/tree_helper.rb
+++ b/app/helpers/tree_helper.rb
@@ -10,13 +10,16 @@ module TreeHelper
tree = ""
# Render folders if we have any
- tree += render partial: 'projects/tree/tree_item', collection: folders, locals: {type: 'folder'} if folders.present?
+ tree << render(partial: 'projects/tree/tree_item', collection: folders,
+ locals: { type: 'folder' }) if folders.present?
# Render files if we have any
- tree += render partial: 'projects/tree/blob_item', collection: files, locals: {type: 'file'} if files.present?
+ tree << render(partial: 'projects/tree/blob_item', collection: files,
+ locals: { type: 'file' }) if files.present?
# Render submodules if we have any
- tree += render partial: 'projects/tree/submodule_item', collection: submodules if submodules.present?
+ tree << render(partial: 'projects/tree/submodule_item',
+ collection: submodules) if submodules.present?
tree.html_safe
end
@@ -35,13 +38,8 @@ module TreeHelper
#
# type - String type of the tree item; either 'folder' or 'file'
def tree_icon(type)
- icon_class = if type == 'folder'
- 'fa fa-folder'
- else
- 'fa fa-file-o'
- end
-
- content_tag :i, nil, class: icon_class
+ icon_class = type == 'folder' ? 'folder' : 'file-o'
+ icon(icon_class)
end
def tree_hex_class(content)
@@ -53,20 +51,18 @@ module TreeHelper
File.join(*args)
end
- def allowed_tree_edit?
- return false unless @repository.branch_names.include?(@ref)
+ def allowed_tree_edit?(project = nil, ref = nil)
+ project ||= @project
+ ref ||= @ref
+ return false unless project.repository.branch_names.include?(ref)
- if @project.protected_branch? @ref
- can?(current_user, :push_code_to_protected_branches, @project)
- else
- can?(current_user, :push_code, @project)
- end
+ ::Gitlab::GitAccess.can_push_to_branch?(current_user, project, ref)
end
def tree_breadcrumbs(tree, max_links = 2)
if @path.present?
part_path = ""
- parts = @path.split("\/")
+ parts = @path.split('/')
yield('..', nil) if parts.count > max_links
@@ -80,20 +76,18 @@ module TreeHelper
end
end
- def up_dir_path(tree)
+ def up_dir_path
file = File.join(@path, "..")
tree_join(@ref, file)
end
- def leave_edit_message
- "Leave edit mode?\nAll unsaved changes will be lost."
- end
-
- def editing_preview_title(filename)
- if Gitlab::MarkdownHelper.previewable?(filename)
- 'Preview'
+ # returns the relative path of the first subdir that doesn't have only one directory descendant
+ def flatten_tree(tree)
+ subtree = Gitlab::Git::Tree.where(@repository, @commit.id, tree.path)
+ if subtree.count == 1 && subtree.first.dir?
+ return tree_join(tree.name, flatten_tree(subtree.first))
else
- 'Diff'
+ return tree.name
end
end
end
diff --git a/app/mailers/emails/issues.rb b/app/mailers/emails/issues.rb
index e5346235963..687bac3aa31 100644
--- a/app/mailers/emails/issues.rb
+++ b/app/mailers/emails/issues.rb
@@ -3,7 +3,7 @@ module Emails
def new_issue_email(recipient_id, issue_id)
@issue = Issue.find(issue_id)
@project = @issue.project
- @target_url = project_issue_url(@project, @issue)
+ @target_url = namespace_project_issue_url(@project.namespace, @project, @issue)
mail_new_thread(@issue,
from: sender(@issue.author_id),
to: recipient(recipient_id),
@@ -14,7 +14,7 @@ module Emails
@issue = Issue.find(issue_id)
@previous_assignee = User.find_by(id: previous_assignee_id) if previous_assignee_id
@project = @issue.project
- @target_url = project_issue_url(@project, @issue)
+ @target_url = namespace_project_issue_url(@project.namespace, @project, @issue)
mail_answer_thread(@issue,
from: sender(updated_by_user_id),
to: recipient(recipient_id),
@@ -25,7 +25,7 @@ module Emails
@issue = Issue.find issue_id
@project = @issue.project
@updated_by = User.find updated_by_user_id
- @target_url = project_issue_url(@project, @issue)
+ @target_url = namespace_project_issue_url(@project.namespace, @project, @issue)
mail_answer_thread(@issue,
from: sender(updated_by_user_id),
to: recipient(recipient_id),
@@ -37,7 +37,7 @@ module Emails
@issue_status = status
@project = @issue.project
@updated_by = User.find updated_by_user_id
- @target_url = project_issue_url(@project, @issue)
+ @target_url = namespace_project_issue_url(@project.namespace, @project, @issue)
mail_answer_thread(@issue,
from: sender(updated_by_user_id),
to: recipient(recipient_id),
diff --git a/app/mailers/emails/merge_requests.rb b/app/mailers/emails/merge_requests.rb
index 9ecdac87d72..512a8f7ea6b 100644
--- a/app/mailers/emails/merge_requests.rb
+++ b/app/mailers/emails/merge_requests.rb
@@ -3,7 +3,9 @@ module Emails
def new_merge_request_email(recipient_id, merge_request_id)
@merge_request = MergeRequest.find(merge_request_id)
@project = @merge_request.project
- @target_url = project_merge_request_url(@project, @merge_request)
+ @target_url = namespace_project_merge_request_url(@project.namespace,
+ @project,
+ @merge_request)
mail_new_thread(@merge_request,
from: sender(@merge_request.author_id),
to: recipient(recipient_id),
@@ -14,7 +16,9 @@ module Emails
@merge_request = MergeRequest.find(merge_request_id)
@previous_assignee = User.find_by(id: previous_assignee_id) if previous_assignee_id
@project = @merge_request.project
- @target_url = project_merge_request_url(@project, @merge_request)
+ @target_url = namespace_project_merge_request_url(@project.namespace,
+ @project,
+ @merge_request)
mail_answer_thread(@merge_request,
from: sender(updated_by_user_id),
to: recipient(recipient_id),
@@ -25,7 +29,9 @@ module Emails
@merge_request = MergeRequest.find(merge_request_id)
@updated_by = User.find updated_by_user_id
@project = @merge_request.project
- @target_url = project_merge_request_url(@project, @merge_request)
+ @target_url = namespace_project_merge_request_url(@project.namespace,
+ @project,
+ @merge_request)
mail_answer_thread(@merge_request,
from: sender(updated_by_user_id),
to: recipient(recipient_id),
@@ -35,7 +41,9 @@ module Emails
def merged_merge_request_email(recipient_id, merge_request_id, updated_by_user_id)
@merge_request = MergeRequest.find(merge_request_id)
@project = @merge_request.project
- @target_url = project_merge_request_url(@project, @merge_request)
+ @target_url = namespace_project_merge_request_url(@project.namespace,
+ @project,
+ @merge_request)
mail_answer_thread(@merge_request,
from: sender(updated_by_user_id),
to: recipient(recipient_id),
@@ -47,7 +55,9 @@ module Emails
@mr_status = status
@project = @merge_request.project
@updated_by = User.find updated_by_user_id
- @target_url = project_merge_request_url(@project, @merge_request)
+ @target_url = namespace_project_merge_request_url(@project.namespace,
+ @project,
+ @merge_request)
set_reference("merge_request_#{merge_request_id}")
mail_answer_thread(@merge_request,
from: sender(updated_by_user_id),
@@ -56,7 +66,7 @@ module Emails
end
end
- # Over rides default behavour to show source/target
+ # Over rides default behaviour to show source/target
# Formats arguments into a String suitable for use as an email subject
#
# extra - Extra Strings to be inserted into the subject
diff --git a/app/mailers/emails/notes.rb b/app/mailers/emails/notes.rb
index ef9af726a6c..ff251209e01 100644
--- a/app/mailers/emails/notes.rb
+++ b/app/mailers/emails/notes.rb
@@ -4,7 +4,9 @@ module Emails
@note = Note.find(note_id)
@commit = @note.noteable
@project = @note.project
- @target_url = project_commit_url(@project, @commit, anchor: "note_#{@note.id}")
+ @target_url = namespace_project_commit_url(@project.namespace, @project,
+ @commit, anchor:
+ "note_#{@note.id}")
mail_answer_thread(@commit,
from: sender(@note.author_id),
to: recipient(recipient_id),
@@ -15,7 +17,9 @@ module Emails
@note = Note.find(note_id)
@issue = @note.noteable
@project = @note.project
- @target_url = project_issue_url(@project, @issue, anchor: "note_#{@note.id}")
+ @target_url = namespace_project_issue_url(@project.namespace, @project,
+ @issue, anchor:
+ "note_#{@note.id}")
mail_answer_thread(@issue,
from: sender(@note.author_id),
to: recipient(recipient_id),
@@ -26,7 +30,10 @@ module Emails
@note = Note.find(note_id)
@merge_request = @note.noteable
@project = @note.project
- @target_url = project_merge_request_url(@project, @merge_request, anchor: "note_#{@note.id}")
+ @target_url = namespace_project_merge_request_url(@project.namespace,
+ @project,
+ @merge_request, anchor:
+ "note_#{@note.id}")
mail_answer_thread(@merge_request,
from: sender(@note.author_id),
to: recipient(recipient_id),
diff --git a/app/mailers/emails/profile.rb b/app/mailers/emails/profile.rb
index f8a7d133d1d..ab5b0765352 100644
--- a/app/mailers/emails/profile.rb
+++ b/app/mailers/emails/profile.rb
@@ -1,24 +1,23 @@
module Emails
module Profile
- def new_user_email(user_id, password, token = nil)
+ def new_user_email(user_id, token = nil)
@user = User.find(user_id)
- @password = password
@target_url = user_url(@user)
@token = token
- mail(to: @user.email, subject: subject("Account was created for you"))
+ mail(to: @user.notification_email, subject: subject("Account was created for you"))
end
def new_email_email(email_id)
@email = Email.find(email_id)
@user = @email.user
- mail(to: @user.email, subject: subject("Email was added to your account"))
+ mail(to: @user.notification_email, subject: subject("Email was added to your account"))
end
def new_ssh_key_email(key_id)
@key = Key.find(key_id)
@user = @key.user
@target_url = user_url(@user)
- mail(to: @user.email, subject: subject("SSH key was added to your account"))
+ mail(to: @user.notification_email, subject: subject("SSH key was added to your account"))
end
end
end
diff --git a/app/mailers/emails/projects.rb b/app/mailers/emails/projects.rb
index d6edfd7059f..4bc40b35f2d 100644
--- a/app/mailers/emails/projects.rb
+++ b/app/mailers/emails/projects.rb
@@ -3,7 +3,7 @@ module Emails
def project_access_granted_email(user_project_id)
@project_member = ProjectMember.find user_project_id
@project = @project_member.project
- @target_url = project_url(@project)
+ @target_url = namespace_project_url(@project.namespace, @project)
mail(to: @project_member.user.email,
subject: subject("Access to project was granted"))
end
@@ -11,8 +11,8 @@ module Emails
def project_was_moved_email(project_id, user_id)
@user = User.find user_id
@project = Project.find project_id
- @target_url = project_url(@project)
- mail(to: @user.email,
+ @target_url = namespace_project_url(@project.namespace, @project)
+ mail(to: @user.notification_email,
subject: subject("Project was moved"))
end
@@ -24,10 +24,14 @@ module Emails
@diffs = compare.diffs
@branch = branch
if @commits.length > 1
- @target_url = project_compare_url(@project, from: @commits.first, to: @commits.last)
+ @target_url = namespace_project_compare_url(@project.namespace,
+ @project,
+ from: @commits.first,
+ to: @commits.last)
@subject = "#{@commits.length} new commits pushed to repository"
else
- @target_url = project_commit_url(@project, @commits.first)
+ @target_url = namespace_project_commit_url(@project.namespace,
+ @project, @commits.first)
@subject = @commits.first.title
end
diff --git a/app/mailers/notify.rb b/app/mailers/notify.rb
index bd438bab89a..46ead62f75f 100644
--- a/app/mailers/notify.rb
+++ b/app/mailers/notify.rb
@@ -11,6 +11,7 @@ class Notify < ActionMailer::Base
add_template_helper ApplicationHelper
add_template_helper GitlabMarkdownHelper
add_template_helper MergeRequestsHelper
+ add_template_helper EmailsHelper
default_url_options[:host] = Gitlab.config.gitlab.host
default_url_options[:protocol] = Gitlab.config.gitlab.protocol
@@ -25,6 +26,14 @@ class Notify < ActionMailer::Base
delay_for(2.seconds)
end
+ def test_email(recipient_email, subject, body)
+ mail(to: recipient_email,
+ subject: subject,
+ body: body.html_safe,
+ content_type: 'text/html'
+ )
+ end
+
private
# The default email address to send emails from
@@ -51,7 +60,7 @@ class Notify < ActionMailer::Base
# Returns a String containing the User's email address.
def recipient(recipient_id)
if recipient = User.find(recipient_id)
- recipient.email
+ recipient.notification_email
end
end
@@ -102,6 +111,7 @@ class Notify < ActionMailer::Base
# See: mail_answer_thread
def mail_new_thread(model, headers = {}, &block)
headers['Message-ID'] = message_id(model)
+ headers['X-GitLab-Project'] = "#{@project.name} | " if @project
mail(headers, &block)
end
@@ -116,6 +126,7 @@ class Notify < ActionMailer::Base
def mail_answer_thread(model, headers = {}, &block)
headers['In-Reply-To'] = message_id(model)
headers['References'] = message_id(model)
+ headers['X-GitLab-Project'] = "#{@project.name} | " if @project
if (headers[:subject])
headers[:subject].prepend('Re: ')
diff --git a/app/models/ability.rb b/app/models/ability.rb
index 97a72bf3635..890417e780d 100644
--- a/app/models/ability.rb
+++ b/app/models/ability.rb
@@ -73,28 +73,28 @@ class Ability
# Rules based on role in project
if team.master?(user)
- rules += project_master_rules
+ rules.push(*project_master_rules)
elsif team.developer?(user)
- rules += project_dev_rules
+ rules.push(*project_dev_rules)
elsif team.reporter?(user)
- rules += project_report_rules
+ rules.push(*project_report_rules)
elsif team.guest?(user)
- rules += project_guest_rules
+ rules.push(*project_guest_rules)
end
if project.public? || project.internal?
- rules += public_project_rules
+ rules.push(*public_project_rules)
end
if project.owner == user || user.admin?
- rules += project_admin_rules
+ rules.push(*project_admin_rules)
end
if project.group && project.group.has_owner?(user)
- rules += project_admin_rules
+ rules.push(*project_admin_rules)
end
if project.archived?
@@ -193,17 +193,17 @@ class Ability
# Only group masters and group owners can create new projects in group
if group.has_master?(user) || group.has_owner?(user) || user.admin?
- rules += [
+ rules.push(*[
:create_projects,
- ]
+ ])
end
# Only group owner and administrators can manage group
if group.has_owner?(user) || user.admin?
- rules += [
+ rules.push(*[
:manage_group,
:manage_namespace
- ]
+ ])
end
rules.flatten
@@ -214,10 +214,10 @@ class Ability
# Only namespace owner and administrators can manage it
if namespace.owner == user || user.admin?
- rules += [
+ rules.push(*[
:create_projects,
:manage_namespace
- ]
+ ])
end
rules.flatten
diff --git a/app/models/application_setting.rb b/app/models/application_setting.rb
new file mode 100644
index 00000000000..f1d918e5457
--- /dev/null
+++ b/app/models/application_setting.rb
@@ -0,0 +1,43 @@
+# == Schema Information
+#
+# Table name: application_settings
+#
+# id :integer not null, primary key
+# default_projects_limit :integer
+# default_branch_protection :integer
+# signup_enabled :boolean
+# signin_enabled :boolean
+# gravatar_enabled :boolean
+# twitter_sharing_enabled :boolean
+# sign_in_text :text
+# created_at :datetime
+# updated_at :datetime
+# home_page_url :string(255)
+#
+
+class ApplicationSetting < ActiveRecord::Base
+ validates :home_page_url,
+ allow_blank: true,
+ format: { with: URI::regexp(%w(http https)), message: "should be a valid url" },
+ if: :home_page_url_column_exist
+
+ def self.current
+ ApplicationSetting.last
+ end
+
+ def self.create_from_defaults
+ create(
+ default_projects_limit: Settings.gitlab['default_projects_limit'],
+ default_branch_protection: Settings.gitlab['default_branch_protection'],
+ signup_enabled: Settings.gitlab['signup_enabled'],
+ signin_enabled: Settings.gitlab['signin_enabled'],
+ twitter_sharing_enabled: Settings.gitlab['twitter_sharing_enabled'],
+ gravatar_enabled: Settings.gravatar['enabled'],
+ sign_in_text: Settings.extra['sign_in_text'],
+ )
+ end
+
+ def home_page_url_column_exist
+ ActiveRecord::Base.connection.column_exists?(:application_settings, :home_page_url)
+ end
+end
diff --git a/app/models/broadcast_message.rb b/app/models/broadcast_message.rb
index 4d0c04bcc3d..05f5e979695 100644
--- a/app/models/broadcast_message.rb
+++ b/app/models/broadcast_message.rb
@@ -14,6 +14,8 @@
#
class BroadcastMessage < ActiveRecord::Base
+ include Sortable
+
validates :message, presence: true
validates :starts_at, presence: true
validates :ends_at, presence: true
diff --git a/app/models/commit.rb b/app/models/commit.rb
index 212229649fc..e0461809e10 100644
--- a/app/models/commit.rb
+++ b/app/models/commit.rb
@@ -10,12 +10,12 @@ class Commit
# Used to prevent 500 error on huge commits by suppressing diff
#
# User can force display of diff above this size
- DIFF_SAFE_FILES = 100
- DIFF_SAFE_LINES = 5000
+ DIFF_SAFE_FILES = 100 unless defined?(DIFF_SAFE_FILES)
+ DIFF_SAFE_LINES = 5000 unless defined?(DIFF_SAFE_LINES)
# Commits above this size will not be rendered in HTML
- DIFF_HARD_LIMIT_FILES = 1000
- DIFF_HARD_LIMIT_LINES = 50000
+ DIFF_HARD_LIMIT_FILES = 1000 unless defined?(DIFF_HARD_LIMIT_FILES)
+ DIFF_HARD_LIMIT_LINES = 50000 unless defined?(DIFF_HARD_LIMIT_LINES)
class << self
def decorate(commits)
@@ -75,11 +75,11 @@ class Commit
return no_commit_message if title.blank?
- title_end = title.index(/\n/)
+ title_end = title.index("\n")
if (!title_end && title.length > 100) || (title_end && title_end > 100)
title[0..79] << "&hellip;".html_safe
else
- title.split(/\n/, 2).first
+ title.split("\n", 2).first
end
end
@@ -87,12 +87,13 @@ class Commit
#
# cut off, ellipses (`&hellp;`) are prepended to the commit message.
def description
- title_end = safe_message.index(/\n/)
- @description ||= if (!title_end && safe_message.length > 100) || (title_end && title_end > 100)
- "&hellip;".html_safe << safe_message[80..-1]
- else
- safe_message.split(/\n/, 2)[1].try(:chomp)
- end
+ title_end = safe_message.index("\n")
+ @description ||=
+ if (!title_end && safe_message.length > 100) || (title_end && title_end > 100)
+ "&hellip;".html_safe << safe_message[80..-1]
+ else
+ safe_message.split("\n", 2)[1].try(:chomp)
+ end
end
def description?
diff --git a/app/models/concerns/issuable.rb b/app/models/concerns/issuable.rb
index 553087946d6..f5e23e9dc2d 100644
--- a/app/models/concerns/issuable.rb
+++ b/app/models/concerns/issuable.rb
@@ -29,6 +29,8 @@ module Issuable
scope :only_opened, -> { with_state(:opened) }
scope :only_reopened, -> { with_state(:reopened) }
scope :closed, -> { with_state(:closed) }
+ scope :order_milestone_due_desc, -> { joins(:milestone).reorder('milestones.due_date DESC, milestones.id DESC') }
+ scope :order_milestone_due_asc, -> { joins(:milestone).reorder('milestones.due_date ASC, milestones.id ASC') }
delegate :name,
:email,
@@ -55,13 +57,10 @@ module Issuable
def sort(method)
case method.to_s
- when 'newest' then reorder("#{table_name}.created_at DESC")
- when 'oldest' then reorder("#{table_name}.created_at ASC")
- when 'recently_updated' then reorder("#{table_name}.updated_at DESC")
- when 'last_updated' then reorder("#{table_name}.updated_at ASC")
- when 'milestone_due_soon' then joins(:milestone).reorder("milestones.due_date ASC")
- when 'milestone_due_later' then joins(:milestone).reorder("milestones.due_date DESC")
- else reorder("#{table_name}.created_at DESC")
+ when 'milestone_due_asc' then order_milestone_due_asc
+ when 'milestone_due_desc' then order_milestone_due_desc
+ else
+ order_by(method)
end
end
end
@@ -88,7 +87,7 @@ module Issuable
# Return the number of -1 comments (downvotes)
def downvotes
- notes.select(&:downvote?).size
+ filter_superceded_votes(notes.select(&:downvote?), notes).size
end
def downvotes_in_percent
@@ -101,7 +100,7 @@ module Issuable
# Return the number of +1 comments (upvotes)
def upvotes
- notes.select(&:upvote?).size
+ filter_superceded_votes(notes.select(&:upvote?), notes).size
end
def upvotes_in_percent
@@ -124,16 +123,19 @@ module Issuable
users << assignee if is_assigned?
mentions = []
mentions << self.mentioned_users
+
notes.each do |note|
users << note.author
mentions << note.mentioned_users
end
+
users.concat(mentions.reduce([], :|)).uniq
end
- def to_hook_data
+ def to_hook_data(user)
{
object_kind: self.class.name.underscore,
+ user: user.hook_attrs,
object_attributes: hook_attrs
}
end
@@ -148,9 +150,23 @@ module Issuable
def add_labels_by_names(label_names)
label_names.each do |label_name|
- label = project.labels.create_with(
- color: Label::DEFAULT_COLOR).find_or_create_by(title: label_name.strip)
+ label = project.labels.create_with(color: Label::DEFAULT_COLOR).
+ find_or_create_by(title: label_name.strip)
self.labels << label
end
end
+
+ private
+
+ def filter_superceded_votes(votes, notes)
+ filteredvotes = [] + votes
+
+ votes.each do |vote|
+ if vote.superceded?(notes)
+ filteredvotes.delete(vote)
+ end
+ end
+
+ filteredvotes
+ end
end
diff --git a/app/models/concerns/mentionable.rb b/app/models/concerns/mentionable.rb
index 6c1aa99668a..50be458bf24 100644
--- a/app/models/concerns/mentionable.rb
+++ b/app/models/concerns/mentionable.rb
@@ -50,10 +50,13 @@ module Mentionable
matches.each do |match|
identifier = match.delete "@"
if identifier == "all"
- users += project.team.members.flatten
- else
- id = User.find_by(username: identifier).try(:id)
- users << User.find(id) unless id.blank?
+ users.push(*project.team.members.flatten)
+ elsif namespace = Namespace.find_by(path: identifier)
+ if namespace.type == "Group"
+ users.push(*namespace.users)
+ else
+ users << namespace.owner
+ end
end
end
users.uniq
@@ -64,9 +67,10 @@ module Mentionable
return [] if text.blank?
ext = Gitlab::ReferenceExtractor.new
ext.analyze(text, p)
- (ext.issues_for +
- ext.merge_requests_for +
- ext.commits_for).uniq - [local_reference]
+
+ (ext.issues_for(p) +
+ ext.merge_requests_for(p) +
+ ext.commits_for(p)).uniq - [local_reference]
end
# Create a cross-reference Note for each GFM reference to another Mentionable found in +mentionable_text+.
diff --git a/app/models/concerns/sortable.rb b/app/models/concerns/sortable.rb
new file mode 100644
index 00000000000..0ad2654867d
--- /dev/null
+++ b/app/models/concerns/sortable.rb
@@ -0,0 +1,35 @@
+# == Sortable concern
+#
+# Set default scope for ordering objects
+#
+module Sortable
+ extend ActiveSupport::Concern
+
+ included do
+ # By default all models should be ordered
+ # by created_at field starting from newest
+ default_scope { order(created_at: :desc, id: :desc) }
+
+ scope :order_created_desc, -> { reorder(created_at: :desc, id: :desc) }
+ scope :order_created_asc, -> { reorder(created_at: :asc, id: :asc) }
+ scope :order_updated_desc, -> { reorder(updated_at: :desc, id: :desc) }
+ scope :order_updated_asc, -> { reorder(updated_at: :asc, id: :asc) }
+ scope :order_name_asc, -> { reorder(name: :asc) }
+ scope :order_name_desc, -> { reorder(name: :desc) }
+ end
+
+ module ClassMethods
+ def order_by(method)
+ case method.to_s
+ when 'name_asc' then order_name_asc
+ when 'name_desc' then order_name_desc
+ when 'updated_asc' then order_updated_asc
+ when 'updated_desc' then order_updated_desc
+ when 'created_asc' then order_created_asc
+ when 'created_desc' then order_created_desc
+ else
+ all
+ end
+ end
+ end
+end
diff --git a/app/models/email.rb b/app/models/email.rb
index 57f476bd519..556b0e9586e 100644
--- a/app/models/email.rb
+++ b/app/models/email.rb
@@ -10,6 +10,8 @@
#
class Email < ActiveRecord::Base
+ include Sortable
+
belongs_to :user
validates :user_id, presence: true
diff --git a/app/models/event.rb b/app/models/event.rb
index c0b126713a6..5579ab1dbb0 100644
--- a/app/models/event.rb
+++ b/app/models/event.rb
@@ -15,6 +15,7 @@
#
class Event < ActiveRecord::Base
+ include Sortable
default_scope { where.not(author_id: nil) }
CREATED = 1
@@ -46,31 +47,9 @@ class Event < ActiveRecord::Base
scope :recent, -> { order("created_at DESC") }
scope :code_push, -> { where(action: PUSHED) }
scope :in_projects, ->(project_ids) { where(project_id: project_ids).recent }
+ scope :with_associations, -> { includes(project: :namespace) }
class << self
- def create_ref_event(project, user, ref, action = 'add', prefix = 'refs/heads')
- commit = project.repository.commit(ref.target)
-
- if action.to_s == 'add'
- before = '00000000'
- after = commit.id
- else
- before = commit.id
- after = '00000000'
- end
-
- Event.create(
- project: project,
- action: Event::PUSHED,
- data: {
- ref: "#{prefix}/#{ref.name}",
- before: before,
- after: after
- },
- author_id: user.id
- )
- end
-
def reset_event_cache_for(target)
Event.where(target_id: target.id, target_type: target.class.to_s).
order('id DESC').limit(100).
@@ -83,6 +62,8 @@ class Event < ActiveRecord::Base
true
elsif membership_changed?
true
+ elsif created_project?
+ true
else
(issue? || merge_request? || note? || milestone?) && target
end
@@ -97,25 +78,51 @@ class Event < ActiveRecord::Base
end
def target_title
- if target && target.respond_to?(:title)
- target.title
- end
+ target.title if target && target.respond_to?(:title)
+ end
+
+ def created?
+ action == CREATED
end
def push?
- action == self.class::PUSHED && valid_push?
+ action == PUSHED && valid_push?
end
def merged?
- action == self.class::MERGED
+ action == MERGED
end
def closed?
- action == self.class::CLOSED
+ action == CLOSED
end
def reopened?
- action == self.class::REOPENED
+ action == REOPENED
+ end
+
+ def joined?
+ action == JOINED
+ end
+
+ def left?
+ action == LEFT
+ end
+
+ def commented?
+ action == COMMENTED
+ end
+
+ def membership_changed?
+ joined? || left?
+ end
+
+ def created_project?
+ created? && !target
+ end
+
+ def created_target?
+ created? && target
end
def milestone?
@@ -134,32 +141,32 @@ class Event < ActiveRecord::Base
target_type == "MergeRequest"
end
- def joined?
- action == JOINED
- end
-
- def left?
- action == LEFT
- end
-
- def membership_changed?
- joined? || left?
+ def milestone
+ target if milestone?
end
def issue
- target if target_type == "Issue"
+ target if issue?
end
def merge_request
- target if target_type == "MergeRequest"
+ target if merge_request?
end
def note
- target if target_type == "Note"
+ target if note?
end
def action_name
- if closed?
+ if push?
+ if new_ref?
+ "pushed new"
+ elsif rm_ref?
+ "deleted"
+ else
+ "pushed to"
+ end
+ elsif closed?
"closed"
elsif merged?
"accepted"
@@ -167,6 +174,10 @@ class Event < ActiveRecord::Base
'joined'
elsif left?
'left'
+ elsif commented?
+ "commented on"
+ elsif created_project?
+ "created"
else
"opened"
end
@@ -174,7 +185,7 @@ class Event < ActiveRecord::Base
def valid_push?
data[:ref] && ref_name.present?
- rescue => ex
+ rescue
false
end
@@ -186,10 +197,6 @@ class Event < ActiveRecord::Base
data[:ref]["refs/heads"]
end
- def new_branch?
- commit_from =~ /^00000/
- end
-
def new_ref?
commit_from =~ /^00000/
end
@@ -239,16 +246,6 @@ class Event < ActiveRecord::Base
tag? ? "tag" : "branch"
end
- def push_action_name
- if new_ref?
- "pushed new"
- elsif rm_ref?
- "deleted"
- else
- "pushed to"
- end
- end
-
def push_with_commits?
md_ref? && commits.any? && commit_from && commit_to
end
diff --git a/app/models/external_issue.rb b/app/models/external_issue.rb
new file mode 100644
index 00000000000..50efcb32f1b
--- /dev/null
+++ b/app/models/external_issue.rb
@@ -0,0 +1,25 @@
+class ExternalIssue
+ def initialize(issue_identifier, project)
+ @issue_identifier, @project = issue_identifier, project
+ end
+
+ def to_s
+ @issue_identifier.to_s
+ end
+
+ def id
+ @issue_identifier.to_s
+ end
+
+ def iid
+ @issue_identifier.to_s
+ end
+
+ def ==(other)
+ other.is_a?(self.class) && (to_s == other.to_s)
+ end
+
+ def project
+ @project
+ end
+end
diff --git a/app/models/group.rb b/app/models/group.rb
index b8ed3b8ac73..da9621a2a1a 100644
--- a/app/models/group.rb
+++ b/app/models/group.rb
@@ -21,9 +21,22 @@ class Group < Namespace
has_many :users, through: :group_members
validate :avatar_type, if: ->(user) { user.avatar_changed? }
- validates :avatar, file_size: { maximum: 100.kilobytes.to_i }
+ validates :avatar, file_size: { maximum: 200.kilobytes.to_i }
- mount_uploader :avatar, AttachmentUploader
+ mount_uploader :avatar, AvatarUploader
+
+ after_create :post_create_hook
+ after_destroy :post_destroy_hook
+
+ class << self
+ def search(query)
+ where("LOWER(namespaces.name) LIKE :query or LOWER(namespaces.path) LIKE :query", query: "%#{query.downcase}%")
+ end
+
+ def sort(method)
+ order_by(method)
+ end
+ end
def human_name
name
@@ -74,19 +87,15 @@ class Group < Namespace
projects.public_only.any?
end
- class << self
- def search(query)
- where("LOWER(namespaces.name) LIKE :query", query: "%#{query.downcase}%")
- end
+ def post_create_hook
+ system_hook_service.execute_hooks_for(self, :create)
+ end
- def sort(method)
- case method.to_s
- when "newest" then reorder("namespaces.created_at DESC")
- when "oldest" then reorder("namespaces.created_at ASC")
- when "recently_updated" then reorder("namespaces.updated_at DESC")
- when "last_updated" then reorder("namespaces.updated_at ASC")
- else reorder("namespaces.path, namespaces.name ASC")
- end
- end
+ def post_destroy_hook
+ system_hook_service.execute_hooks_for(self, :destroy)
+ end
+
+ def system_hook_service
+ SystemHooksService.new
end
end
diff --git a/app/models/group_milestone.rb b/app/models/group_milestone.rb
index 33915313789..7e4f16ebf16 100644
--- a/app/models/group_milestone.rb
+++ b/app/models/group_milestone.rb
@@ -66,15 +66,15 @@ class GroupMilestone
end
def issues
- @group_issues ||= milestones.map { |milestone| milestone.issues }.flatten.group_by(&:state)
+ @group_issues ||= milestones.map(&:issues).flatten.group_by(&:state)
end
def merge_requests
- @group_merge_requests ||= milestones.map { |milestone| milestone.merge_requests }.flatten.group_by(&:state)
+ @group_merge_requests ||= milestones.map(&:merge_requests).flatten.group_by(&:state)
end
def participants
- milestones.map { |milestone| milestone.participants.uniq }.reject(&:empty?).flatten
+ @group_participants ||= milestones.map(&:participants).flatten.compact.uniq
end
def opened_issues
diff --git a/app/models/hooks/web_hook.rb b/app/models/hooks/web_hook.rb
index 23fa01e0b70..defef7216f2 100644
--- a/app/models/hooks/web_hook.rb
+++ b/app/models/hooks/web_hook.rb
@@ -16,6 +16,7 @@
#
class WebHook < ActiveRecord::Base
+ include Sortable
include HTTParty
default_value_for :push_events, true
@@ -32,7 +33,10 @@ class WebHook < ActiveRecord::Base
def execute(data)
parsed_url = URI.parse(url)
if parsed_url.userinfo.blank?
- WebHook.post(url, body: data.to_json, headers: { "Content-Type" => "application/json" }, verify: false)
+ WebHook.post(url,
+ body: data.to_json,
+ headers: { "Content-Type" => "application/json" },
+ verify: false)
else
post_url = url.gsub("#{parsed_url.userinfo}@", "")
auth = {
@@ -41,10 +45,13 @@ class WebHook < ActiveRecord::Base
}
WebHook.post(post_url,
body: data.to_json,
- headers: {"Content-Type" => "application/json"},
+ headers: { "Content-Type" => "application/json" },
verify: false,
basic_auth: auth)
end
+ rescue SocketError, Errno::ECONNRESET, Errno::ECONNREFUSED, Net::OpenTimeout => e
+ logger.error("WebHook Error => #{e}")
+ false
end
def async_execute(data)
diff --git a/app/models/identity.rb b/app/models/identity.rb
new file mode 100644
index 00000000000..b2c3792d1ce
--- /dev/null
+++ b/app/models/identity.rb
@@ -0,0 +1,16 @@
+# == Schema Information
+#
+# Table name: identities
+#
+# id :integer not null, primary key
+# extern_uid :string(255)
+# provider :string(255)
+# user_id :integer
+#
+
+class Identity < ActiveRecord::Base
+ include Sortable
+ belongs_to :user
+
+ validates :extern_uid, allow_blank: true, uniqueness: { scope: :provider }
+end
diff --git a/app/models/issue.rb b/app/models/issue.rb
index 8a9e969248c..19e43ebd788 100644
--- a/app/models/issue.rb
+++ b/app/models/issue.rb
@@ -24,6 +24,7 @@ class Issue < ActiveRecord::Base
include Issuable
include InternalId
include Taskable
+ include Sortable
ActsAsTaggableOn.strict_case_match = true
diff --git a/app/models/key.rb b/app/models/key.rb
index 095c73d8baf..e2e59296eed 100644
--- a/app/models/key.rb
+++ b/app/models/key.rb
@@ -15,11 +15,12 @@
require 'digest/md5'
class Key < ActiveRecord::Base
+ include Sortable
include Gitlab::Popen
belongs_to :user
- before_validation :strip_white_space, :generate_fingerpint
+ before_validation :strip_white_space, :generate_fingerprint
validates :title, presence: true, length: { within: 0..255 }
validates :key, presence: true, length: { within: 0..5000 }, format: { with: /\A(ssh|ecdsa)-.*\Z/ }, uniqueness: true
@@ -76,7 +77,7 @@ class Key < ActiveRecord::Base
private
- def generate_fingerpint
+ def generate_fingerprint
self.fingerprint = nil
return unless key.present?
@@ -89,7 +90,7 @@ class Key < ActiveRecord::Base
end
if cmd_status.zero?
- cmd_output.gsub /([\d\h]{2}:)+[\d\h]{2}/ do |match|
+ cmd_output.gsub /(\h{2}:)+\h{2}/ do |match|
self.fingerprint = match
end
end
diff --git a/app/models/label.rb b/app/models/label.rb
index 2b2b02e0645..9d7099c5652 100644
--- a/app/models/label.rb
+++ b/app/models/label.rb
@@ -28,7 +28,7 @@ class Label < ActiveRecord::Base
format: { with: /\A[^&\?,&]+\z/ },
uniqueness: { scope: :project_id }
- scope :order_by_name, -> { reorder("labels.title ASC") }
+ default_scope { order(title: :asc) }
alias_attribute :name, :title
diff --git a/app/models/member.rb b/app/models/member.rb
index 671ef466baa..fe3d2f40e87 100644
--- a/app/models/member.rb
+++ b/app/models/member.rb
@@ -14,6 +14,7 @@
#
class Member < ActiveRecord::Base
+ include Sortable
include Notifiable
include Gitlab::Access
diff --git a/app/models/members/group_member.rb b/app/models/members/group_member.rb
index b7f296b13fb..28d0b4483b4 100644
--- a/app/models/members/group_member.rb
+++ b/app/models/members/group_member.rb
@@ -27,8 +27,9 @@ class GroupMember < Member
scope :with_group, ->(group) { where(source_id: group.id) }
scope :with_user, ->(user) { where(user_id: user.id) }
- after_create :notify_create
+ after_create :post_create_hook
after_update :notify_update
+ after_destroy :post_destroy_hook
def self.access_level_roles
Gitlab::Access.options_with_owner
@@ -42,8 +43,9 @@ class GroupMember < Member
access_level
end
- def notify_create
+ def post_create_hook
notification_service.new_group_member(self)
+ system_hook_service.execute_hooks_for(self, :create)
end
def notify_update
@@ -52,6 +54,14 @@ class GroupMember < Member
end
end
+ def post_destroy_hook
+ system_hook_service.execute_hooks_for(self, :destroy)
+ end
+
+ def system_hook_service
+ SystemHooksService.new
+ end
+
def notification_service
NotificationService.new
end
diff --git a/app/models/members/project_member.rb b/app/models/members/project_member.rb
index 30c09f768d7..e4791d0f0aa 100644
--- a/app/models/members/project_member.rb
+++ b/app/models/members/project_member.rb
@@ -114,13 +114,11 @@ class ProjectMember < Member
end
def post_create_hook
- Event.create(
- project_id: self.project.id,
- action: Event::JOINED,
- author_id: self.user.id
- )
-
- notification_service.new_team_member(self) unless owner?
+ unless owner?
+ event_service.join_project(self.project, self.user)
+ notification_service.new_team_member(self)
+ end
+
system_hook_service.execute_hooks_for(self, :create)
end
@@ -129,15 +127,14 @@ class ProjectMember < Member
end
def post_destroy_hook
- Event.create(
- project_id: self.project.id,
- action: Event::LEFT,
- author_id: self.user.id
- )
-
+ event_service.leave_project(self.project, self.user)
system_hook_service.execute_hooks_for(self, :destroy)
end
+ def event_service
+ EventCreateService.new
+ end
+
def notification_service
NotificationService.new
end
diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb
index 7c525b02f48..f758126cfeb 100644
--- a/app/models/merge_request.rb
+++ b/app/models/merge_request.rb
@@ -18,6 +18,7 @@
# iid :integer
# description :text
# position :integer default(0)
+# locked_at :datetime
#
require Rails.root.join("app/models/commit")
@@ -27,6 +28,7 @@ class MergeRequest < ActiveRecord::Base
include Issuable
include Taskable
include InternalId
+ include Sortable
belongs_to :target_project, foreign_key: :target_project_id, class_name: "Project"
belongs_to :source_project, foreign_key: :source_project_id, class_name: "Project"
@@ -70,6 +72,16 @@ class MergeRequest < ActiveRecord::Base
transition locked: :reopened
end
+ after_transition any => :locked do |merge_request, transition|
+ merge_request.locked_at = Time.now
+ merge_request.save
+ end
+
+ after_transition locked: (any - :locked) do |merge_request, transition|
+ merge_request.locked_at = nil
+ merge_request.save
+ end
+
state :opened
state :reopened
state :closed
@@ -179,7 +191,9 @@ class MergeRequest < ActiveRecord::Base
end
def automerge!(current_user, commit_message = nil)
- MergeRequests::AutoMergeService.new.execute(self, current_user, commit_message)
+ MergeRequests::AutoMergeService.
+ new(target_project, current_user).
+ execute(self, commit_message)
end
def open?
@@ -238,7 +252,8 @@ class MergeRequest < ActiveRecord::Base
def closes_issues
if target_branch == project.default_branch
issues = commits.flat_map { |c| c.closes_issues(project) }
- issues += Gitlab::ClosingIssueExtractor.closed_by_message_in_project(description, project)
+ issues.push(*Gitlab::ClosingIssueExtractor.
+ closed_by_message_in_project(description, project))
issues.uniq.sort_by(&:id)
else
[]
@@ -318,7 +333,7 @@ class MergeRequest < ActiveRecord::Base
end
# Return array of possible target branches
- # dependes on target project of MR
+ # depends on target project of MR
def target_branches
if target_project.nil?
[]
@@ -328,7 +343,7 @@ class MergeRequest < ActiveRecord::Base
end
# Return array of possible source branches
- # dependes on source project of MR
+ # depends on source project of MR
def source_branches
if source_project.nil?
[]
@@ -336,4 +351,8 @@ class MergeRequest < ActiveRecord::Base
source_project.repository.branch_names
end
end
+
+ def locked_long_ago?
+ locked_at && locked_at < (Time.now - 1.day)
+ end
end
diff --git a/app/models/merge_request_diff.rb b/app/models/merge_request_diff.rb
index a71122d5e07..acac1ca4cf7 100644
--- a/app/models/merge_request_diff.rb
+++ b/app/models/merge_request_diff.rb
@@ -14,6 +14,8 @@
require Rails.root.join("app/models/commit")
class MergeRequestDiff < ActiveRecord::Base
+ include Sortable
+
# Prevent store of diff
# if commits amount more then 200
COMMITS_SAFE_SIZE = 200
diff --git a/app/models/milestone.rb b/app/models/milestone.rb
index 8fd3e56d2ee..9bbb2bafb98 100644
--- a/app/models/milestone.rb
+++ b/app/models/milestone.rb
@@ -15,6 +15,7 @@
class Milestone < ActiveRecord::Base
include InternalId
+ include Sortable
belongs_to :project
has_many :issues
diff --git a/app/models/namespace.rb b/app/models/namespace.rb
index c0c6de0ee7d..2c7ed376265 100644
--- a/app/models/namespace.rb
+++ b/app/models/namespace.rb
@@ -14,21 +14,27 @@
#
class Namespace < ActiveRecord::Base
+ include Sortable
include Gitlab::ShellAdapter
has_many :projects, dependent: :destroy
belongs_to :owner, class_name: "User"
validates :owner, presence: true, unless: ->(n) { n.type == "Group" }
- validates :name, presence: true, uniqueness: true,
- length: { within: 0..255 },
- format: { with: Gitlab::Regex.name_regex,
- message: Gitlab::Regex.name_regex_message }
+ validates :name,
+ presence: true, uniqueness: true,
+ length: { within: 0..255 },
+ format: { with: Gitlab::Regex.name_regex,
+ message: Gitlab::Regex.name_regex_message }
+
validates :description, length: { within: 0..255 }
- validates :path, uniqueness: { case_sensitive: false }, presence: true, length: { within: 1..255 },
- exclusion: { in: Gitlab::Blacklist.path },
- format: { with: Gitlab::Regex.path_regex,
- message: Gitlab::Regex.path_regex_message }
+ validates :path,
+ uniqueness: { case_sensitive: false },
+ presence: true,
+ length: { within: 1..255 },
+ exclusion: { in: Gitlab::Blacklist.path },
+ format: { with: Gitlab::Regex.path_regex,
+ message: Gitlab::Regex.path_regex_message }
delegate :name, to: :owner, allow_nil: true, prefix: true
@@ -38,6 +44,10 @@ class Namespace < ActiveRecord::Base
scope :root, -> { where('type IS NULL') }
+ def self.by_path(path)
+ where('lower(path) = :value', value: path.downcase).first
+ end
+
def self.search(query)
where("name LIKE :query OR path LIKE :query", query: "%#{query}%")
end
@@ -90,4 +100,8 @@ class Namespace < ActiveRecord::Base
def kind
type == 'Group' ? 'group' : 'user'
end
+
+ def find_fork_of(project)
+ projects.joins(:forked_project_link).where('forked_project_links.forked_from_project_id = ?', project.id).first
+ end
end
diff --git a/app/models/network/graph.rb b/app/models/network/graph.rb
index 43979b5e807..f4e90125373 100644
--- a/app/models/network/graph.rb
+++ b/app/models/network/graph.rb
@@ -84,7 +84,7 @@ module Network
skip += self.class.max_count
end
else
- # Cant't find the target commit in the repo.
+ # Can't find the target commit in the repo.
offset = 0
end
end
@@ -226,7 +226,7 @@ module Network
reserved = []
for day in time_range
- reserved += @reserved[day]
+ reserved.push(*@reserved[day])
end
reserved.uniq!
diff --git a/app/models/note.rb b/app/models/note.rb
index f0ed7580b4c..e6c258ffbe9 100644
--- a/app/models/note.rb
+++ b/app/models/note.rb
@@ -49,7 +49,7 @@ class Note < ActiveRecord::Base
scope :not_inline, ->{ where(line_code: [nil, '']) }
scope :system, ->{ where(system: true) }
scope :common, ->{ where(noteable_type: ["", nil]) }
- scope :fresh, ->{ order("created_at ASC, id ASC") }
+ scope :fresh, ->{ order(created_at: :asc, id: :asc) }
scope :inc_author_project, ->{ includes(:project, :author) }
scope :inc_author, ->{ includes(:author) }
@@ -90,7 +90,7 @@ class Note < ActiveRecord::Base
note_options.merge!(noteable: noteable)
end
- create(note_options)
+ create(note_options) unless cross_reference_disallowed?(noteable, mentioner)
end
def create_milestone_change_note(noteable, project, author, milestone)
@@ -121,6 +121,36 @@ class Note < ActiveRecord::Base
})
end
+ def create_labels_change_note(noteable, project, author, added_labels, removed_labels)
+ labels_count = added_labels.count + removed_labels.count
+ added_labels = added_labels.map{ |label| "~#{label.id}" }.join(' ')
+ removed_labels = removed_labels.map{ |label| "~#{label.id}" }.join(' ')
+ message = ''
+
+ if added_labels.present?
+ message << "added #{added_labels}"
+ end
+
+ if added_labels.present? && removed_labels.present?
+ message << ' and '
+ end
+
+ if removed_labels.present?
+ message << "removed #{removed_labels}"
+ end
+
+ message << ' ' << 'label'.pluralize(labels_count)
+ body = "_#{message.capitalize}_"
+
+ create(
+ noteable: noteable,
+ project: project,
+ author: author,
+ note: body,
+ system: true
+ )
+ end
+
def create_new_commits_note(noteable, project, author, commits)
commits_text = ActionController::Base.helpers.pluralize(commits.size, 'new commit')
body = "Added #{commits_text}:\n\n"
@@ -165,6 +195,15 @@ class Note < ActiveRecord::Base
[:discussion, type.try(:underscore), id, line_code].join("-").to_sym
end
+ # Determine if cross reference note should be created.
+ # eg. mentioning a commit in MR comments which exists inside a MR
+ # should not create "mentioned in" note.
+ def cross_reference_disallowed?(noteable, mentioner)
+ if mentioner.kind_of?(MergeRequest)
+ mentioner.commits.map(&:id).include? noteable.id
+ end
+ end
+
# Determine whether or not a cross-reference note already exists.
def cross_reference_exists?(noteable, mentioner)
gfm_reference = mentioner_gfm_ref(noteable, mentioner)
@@ -251,8 +290,8 @@ 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.team.users.find_by(email: noteable.author_email) ||
+ project.team.users.find_by(name: noteable.author_name)
rescue
nil
end
@@ -287,6 +326,7 @@ class Note < ActiveRecord::Base
# If not - its outdated diff
def active?
return true unless self.diff
+ return false unless noteable
noteable.diffs.each do |mr_diff|
next unless mr_diff.new_path == self.diff.new_path
@@ -308,7 +348,7 @@ class Note < ActiveRecord::Base
end
def diff_file_index
- line_code.split('_')[0]
+ line_code.split('_')[0] if line_code
end
def diff_file_name
@@ -324,11 +364,11 @@ class Note < ActiveRecord::Base
end
def diff_old_line
- line_code.split('_')[1].to_i
+ line_code.split('_')[1].to_i if line_code
end
def diff_new_line
- line_code.split('_')[2].to_i
+ line_code.split('_')[2].to_i if line_code
end
def generate_line_code(line)
@@ -349,25 +389,39 @@ class Note < ActiveRecord::Base
@diff_line
end
+ def diff_line_type
+ return @diff_line_type if @diff_line_type
+
+ if diff
+ diff_lines.each do |line|
+ if generate_line_code(line) == self.line_code
+ @diff_line_type = line.type
+ end
+ end
+ end
+
+ @diff_line_type
+ end
+
def truncated_diff_lines
max_number_of_lines = 16
prev_match_line = nil
prev_lines = []
diff_lines.each do |line|
- if generate_line_code(line) != self.line_code
- if line.type == "match"
- prev_lines.clear
- prev_match_line = line
- else
- prev_lines.push(line)
- prev_lines.shift if prev_lines.length >= max_number_of_lines
- end
+ if line.type == "match"
+ prev_lines.clear
+ prev_match_line = line
else
prev_lines << line
- return prev_lines
+
+ break if generate_line_code(line) == self.line_code
+
+ prev_lines.shift if prev_lines.length >= max_number_of_lines
end
end
+
+ prev_lines
end
def diff_lines
@@ -435,6 +489,26 @@ class Note < ActiveRecord::Base
)
end
+ def superceded?(notes)
+ return false unless vote?
+
+ notes.each do |note|
+ next if note == self
+
+ if note.vote? &&
+ self[:author_id] == note[:author_id] &&
+ self[:created_at] <= note[:created_at]
+ return true
+ end
+ end
+
+ false
+ end
+
+ def vote?
+ upvote? || downvote?
+ end
+
def votable?
for_issue? || (for_merge_request? && !for_diff_line?)
end
@@ -456,7 +530,7 @@ class Note < ActiveRecord::Base
end
# FIXME: Hack for polymorphic associations with STI
- # For more information wisit http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html#label-Polymorphic+Associations
+ # For more information visit http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html#label-Polymorphic+Associations
def noteable_type=(sType)
super(sType.to_s.classify.constantize.base_class.to_s)
end
@@ -479,6 +553,6 @@ class Note < ActiveRecord::Base
end
def editable?
- !system
+ !read_attribute(:system)
end
end
diff --git a/app/models/notification.rb b/app/models/notification.rb
index b0f8ed6a4ec..1395274173d 100644
--- a/app/models/notification.rb
+++ b/app/models/notification.rb
@@ -6,12 +6,13 @@ class Notification
N_PARTICIPATING = 1
N_WATCH = 2
N_GLOBAL = 3
+ N_MENTION = 4
attr_accessor :target
class << self
def notification_levels
- [N_DISABLED, N_PARTICIPATING, N_WATCH]
+ [N_DISABLED, N_PARTICIPATING, N_WATCH, N_MENTION]
end
def options_with_labels
@@ -19,12 +20,13 @@ class Notification
disabled: N_DISABLED,
participating: N_PARTICIPATING,
watch: N_WATCH,
+ mention: N_MENTION,
global: N_GLOBAL
}
end
def project_notification_levels
- [N_DISABLED, N_PARTICIPATING, N_WATCH, N_GLOBAL]
+ [N_DISABLED, N_PARTICIPATING, N_WATCH, N_GLOBAL, N_MENTION]
end
end
@@ -48,6 +50,10 @@ class Notification
target.notification_level == N_GLOBAL
end
+ def mention?
+ target.notification_level == N_MENTION
+ end
+
def level
target.notification_level
end
diff --git a/app/models/project.rb b/app/models/project.rb
index c58c9b551c9..d33b25db201 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -24,9 +24,16 @@
# import_status :string(255)
# repository_size :float default(0.0)
# star_count :integer default(0), not null
+# import_type :string(255)
+# import_source :string(255)
+# avatar :string(255)
#
+require 'carrierwave/orm/activerecord'
+require 'file_size_validator'
+
class Project < ActiveRecord::Base
+ include Sortable
include Gitlab::ShellAdapter
include Gitlab::VisibilityLevel
include Gitlab::ConfigHelper
@@ -41,14 +48,20 @@ class Project < ActiveRecord::Base
default_value_for :wall_enabled, false
default_value_for :snippets_enabled, gitlab_config_features.snippets
+ # set last_activity_at to the same as created_at
+ after_create :set_last_activity_at
+ def set_last_activity_at
+ update_column(:last_activity_at, self.created_at)
+ end
+
ActsAsTaggableOn.strict_case_match = true
acts_as_taggable_on :tags
attr_accessor :new_default_branch
# Relations
- belongs_to :creator, foreign_key: "creator_id", class_name: "User"
- belongs_to :group, -> { where(type: Group) }, foreign_key: "namespace_id"
+ belongs_to :creator, foreign_key: 'creator_id', class_name: 'User'
+ belongs_to :group, -> { where(type: Group) }, foreign_key: 'namespace_id'
belongs_to :namespace
has_one :last_event, -> {order 'events.created_at DESC'}, class_name: 'Event', foreign_key: 'project_id'
@@ -62,25 +75,33 @@ class Project < ActiveRecord::Base
has_one :hipchat_service, dependent: :destroy
has_one :flowdock_service, dependent: :destroy
has_one :assembla_service, dependent: :destroy
+ has_one :asana_service, dependent: :destroy
has_one :gemnasium_service, dependent: :destroy
has_one :slack_service, dependent: :destroy
has_one :buildbox_service, dependent: :destroy
has_one :bamboo_service, dependent: :destroy
+ has_one :teamcity_service, dependent: :destroy
has_one :pushover_service, dependent: :destroy
+ has_one :jira_service, dependent: :destroy
+ has_one :redmine_service, dependent: :destroy
+ has_one :custom_issue_tracker_service, dependent: :destroy
+ has_one :gitlab_issue_tracker_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"
+ 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 'issues.state DESC, issues.created_at DESC' }, dependent: :destroy
+ has_many :fork_merge_requests, foreign_key: 'source_project_id', class_name: MergeRequest
+ has_many :issues, dependent: :destroy
has_many :labels, dependent: :destroy
has_many :services, dependent: :destroy
has_many :events, dependent: :destroy
has_many :milestones, dependent: :destroy
has_many :notes, dependent: :destroy
- has_many :snippets, dependent: :destroy, class_name: "ProjectSnippet"
- has_many :hooks, dependent: :destroy, class_name: "ProjectHook"
+ has_many :snippets, dependent: :destroy, class_name: 'ProjectSnippet'
+ has_many :hooks, dependent: :destroy, class_name: 'ProjectHook'
has_many :protected_branches, dependent: :destroy
has_many :project_members, dependent: :destroy, as: :source, class_name: 'ProjectMember'
has_many :users, through: :project_members
@@ -95,13 +116,16 @@ class Project < ActiveRecord::Base
# Validations
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: Gitlab::Regex.project_regex_message }
- validates :path, presence: true, length: { within: 0..255 },
- exclusion: { in: Gitlab::Blacklist.path },
- format: { with: Gitlab::Regex.path_regex,
- message: Gitlab::Regex.path_regex_message }
+ validates :name,
+ presence: true,
+ length: { within: 0..255 },
+ format: { with: Gitlab::Regex.project_name_regex,
+ message: Gitlab::Regex.project_regex_message }
+ validates :path,
+ presence: true,
+ length: { within: 0..255 },
+ format: { with: Gitlab::Regex.path_regex,
+ message: Gitlab::Regex.path_regex_message }
validates :issues_enabled, :merge_requests_enabled,
:wiki_enabled, inclusion: { in: [true, false] }
validates :visibility_level,
@@ -112,50 +136,55 @@ class Project < ActiveRecord::Base
validates_uniqueness_of :name, scope: :namespace_id
validates_uniqueness_of :path, scope: :namespace_id
validates :import_url,
- format: { with: URI::regexp(%w(git http https)), message: "should be a valid url" },
+ format: { with: URI::regexp(%w(ssh git http https)), message: 'should be a valid url' },
if: :import?
validates :star_count, numericality: { greater_than_or_equal_to: 0 }
validate :check_limit, on: :create
+ validate :avatar_type,
+ if: ->(project) { project.avatar && project.avatar_changed? }
+ validates :avatar, file_size: { maximum: 200.kilobytes.to_i }
+
+ mount_uploader :avatar, AvatarUploader
# Scopes
- scope :without_user, ->(user) { where("projects.id NOT IN (:ids)", ids: user.authorized_projects.map(&:id) ) }
- scope :without_team, ->(team) { team.projects.present? ? where("projects.id NOT IN (:ids)", ids: team.projects.map(&:id)) : scoped }
- scope :not_in_group, ->(group) { where("projects.id NOT IN (:ids)", ids: group.project_ids ) }
- scope :in_team, ->(team) { where("projects.id IN (:ids)", ids: team.projects.map(&:id)) }
+ scope :sorted_by_activity, -> { reorder(last_activity_at: :desc) }
+ scope :sorted_by_stars, -> { reorder('projects.star_count DESC') }
+ scope :sorted_by_names, -> { joins(:namespace).reorder('namespaces.name ASC, projects.name ASC') }
+
+ scope :without_user, ->(user) { where('projects.id NOT IN (:ids)', ids: user.authorized_projects.map(&:id) ) }
+ scope :without_team, ->(team) { team.projects.present? ? where('projects.id NOT IN (:ids)', ids: team.projects.map(&:id)) : scoped }
+ scope :not_in_group, ->(group) { where('projects.id NOT IN (:ids)', ids: group.project_ids ) }
+ scope :in_team, ->(team) { where('projects.id IN (:ids)', ids: team.projects.map(&:id)) }
scope :in_namespace, ->(namespace) { where(namespace_id: namespace.id) }
scope :in_group_namespace, -> { joins(:group) }
- scope :sorted_by_activity, -> { reorder("projects.last_activity_at DESC") }
- scope :sorted_by_stars, -> { reorder("projects.star_count DESC") }
scope :personal, ->(user) { where(namespace_id: user.namespace_id) }
- scope :joined, ->(user) { where("namespace_id != ?", user.namespace_id) }
+ scope :joined, ->(user) { where('namespace_id != ?', user.namespace_id) }
scope :public_only, -> { where(visibility_level: Project::PUBLIC) }
scope :public_and_internal_only, -> { where(visibility_level: Project.public_and_internal_levels) }
scope :non_archived, -> { where(archived: false) }
- enumerize :issues_tracker, in: (Gitlab.config.issues_tracker.keys).append(:gitlab), default: :gitlab
-
state_machine :import_status, initial: :none do
event :import_start do
- transition :none => :started
+ transition [:none, :finished] => :started
end
event :import_finish do
- transition :started => :finished
+ transition started: :finished
end
event :import_fail do
- transition :started => :failed
+ transition started: :failed
end
event :import_retry do
- transition :failed => :started
+ transition failed: :started
end
state :started
state :finished
state :failed
- after_transition any => :started, :do => :add_import_job
+ after_transition any => :started, do: :add_import_job
end
class << self
@@ -169,7 +198,7 @@ class Project < ActiveRecord::Base
def publicish(user)
visibility_levels = [Project::PUBLIC]
- visibility_levels += [Project::INTERNAL] if user
+ visibility_levels << Project::INTERNAL if user
where(visibility_level: visibility_levels)
end
@@ -178,21 +207,26 @@ class Project < ActiveRecord::Base
end
def active
- joins(:issues, :notes, :merge_requests).order("issues.created_at, notes.created_at, merge_requests.created_at DESC")
+ joins(:issues, :notes, :merge_requests).order('issues.created_at, notes.created_at, merge_requests.created_at DESC')
end
def search(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}%")
+ joins(:namespace).where('projects.archived = ?', false).
+ where('LOWER(projects.name) LIKE :query OR
+ LOWER(projects.path) LIKE :query OR
+ LOWER(namespaces.name) LIKE :query OR
+ LOWER(projects.description) LIKE :query',
+ query: "%#{query.try(:downcase)}%")
end
def search_by_title(query)
- where("projects.archived = ?", false).where("LOWER(projects.name) LIKE :query", query: "%#{query.downcase}%")
+ where('projects.archived = ?', false).where('LOWER(projects.name) LIKE :query', query: "%#{query.downcase}%")
end
def find_with_namespace(id)
- return nil unless id.include?("/")
+ return nil unless id.include?('/')
- id = id.split("/")
+ id = id.split('/')
namespace = Namespace.find_by(path: id.first)
return nil unless namespace
@@ -204,13 +238,10 @@ class Project < ActiveRecord::Base
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')
- when 'largest_repository' then reorder('projects.repository_size DESC')
- else reorder("namespaces.path, projects.name ASC")
+ if method == 'repository_size_desc'
+ reorder(repository_size: :desc, id: :desc)
+ else
+ order_by(method)
end
end
end
@@ -260,19 +291,19 @@ class Project < ActiveRecord::Base
end
def to_param
- namespace.path + "/" + path
+ path
end
def web_url
- [gitlab_config.url, path_with_namespace].join("/")
+ [gitlab_config.url, path_with_namespace].join('/')
end
def web_url_without_protocol
- web_url.split("://")[1]
+ web_url.split('://')[1]
end
def build_commit_note(commit)
- notes.new(commit_id: commit.id, noteable_type: "Commit")
+ notes.new(commit_id: commit.id, noteable_type: 'Commit')
end
def last_activity
@@ -288,33 +319,68 @@ class Project < ActiveRecord::Base
end
def issue_exists?(issue_id)
- if used_default_issues_tracker?
+ if default_issues_tracker?
self.issues.where(iid: issue_id).first.present?
else
true
end
end
- def used_default_issues_tracker?
- self.issues_tracker == Project.issues_tracker.default_value
+ def default_issue_tracker
+ gitlab_issue_tracker_service || create_gitlab_issue_tracker_service
+ end
+
+ def issues_tracker
+ if external_issue_tracker
+ external_issue_tracker
+ else
+ default_issue_tracker
+ end
+ end
+
+ def default_issues_tracker?
+ if external_issue_tracker
+ false
+ else
+ true
+ end
+ end
+
+ def external_issues_trackers
+ services.select(&:issue_tracker?).reject(&:default?)
+ end
+
+ def external_issue_tracker
+ @external_issues_tracker ||= external_issues_trackers.select(&:activated?).first
end
def can_have_issues_tracker_id?
- self.issues_enabled && !self.used_default_issues_tracker?
+ self.issues_enabled && !self.default_issues_tracker?
end
def build_missing_services
- available_services_names.each do |service_name|
- service = services.find { |service| service.to_param == service_name }
+ services_templates = Service.where(template: true)
+
+ Service.available_services_names.each do |service_name|
+ service = find_service(services, service_name)
# If service is available but missing in db
- # we should create an instance. Ex `create_gitlab_ci_service`
- service = self.send :"create_#{service_name}_service" if service.nil?
+ if service.nil?
+ # We should check if template for the service exists
+ template = find_service(services_templates, service_name)
+
+ if template.nil?
+ # If no template, we should create an instance. Ex `create_gitlab_ci_service`
+ service = self.send :"create_#{service_name}_service"
+ else
+ Service.create_from_template(self.id, template)
+ end
+ end
end
end
- def available_services_names
- %w(gitlab_ci campfire hipchat pivotaltracker flowdock assembla emails_on_push gemnasium slack pushover buildbox bamboo)
+ def find_service(list, name)
+ list.find { |service| service.to_param == name }
end
def gitlab_ci?
@@ -329,6 +395,19 @@ class Project < ActiveRecord::Base
@ci_service ||= ci_services.select(&:activated?).first
end
+ def avatar_type
+ unless self.avatar.image?
+ self.errors.add :avatar, 'only images allowed'
+ end
+ end
+
+ def avatar_in_git
+ @avatar_file ||= 'logo.png' if repository.blob_at_branch('master', 'logo.png')
+ @avatar_file ||= 'logo.jpg' if repository.blob_at_branch('master', 'logo.jpg')
+ @avatar_file ||= 'logo.gif' if repository.blob_at_branch('master', 'logo.gif')
+ @avatar_file
+ end
+
# For compatibility with old code
def code
path
@@ -356,7 +435,7 @@ class Project < ActiveRecord::Base
end
def team_member_by_name_or_email(name = nil, email = nil)
- user = users.where("name like ? or email like ?", name, email).first
+ user = users.where('name like ? or email like ?', name, email).first
project_members.where(user: user) if user
end
@@ -368,7 +447,7 @@ class Project < ActiveRecord::Base
def name_with_namespace
@name_with_namespace ||= begin
if namespace
- namespace.human_name + " / " + name
+ namespace.human_name + ' / ' + name
else
name
end
@@ -390,61 +469,20 @@ class Project < ActiveRecord::Base
end
def execute_services(data)
- services.each do |service|
-
- # Call service hook only if it is active
- begin
- service.execute(data) if service.active
- rescue => e
- logger.error(e)
- end
+ services.select(&:active).each do |service|
+ service.async_execute(data)
end
end
def update_merge_requests(oldrev, newrev, ref, user)
- return true unless ref =~ /heads/
- branch_name = ref.gsub("refs/heads/", "")
- commits = self.repository.commits_between(oldrev, newrev)
- c_ids = commits.map(&:id)
-
- # Close merge requests
- 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.uniq.each do |merge_request|
- MergeRequests::MergeService.new.execute(merge_request, user, nil)
- end
-
- # Update code for merge requests into project between project branches
- 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).to_a
-
- mrs.uniq.each do |merge_request|
- merge_request.reload_code
- merge_request.mark_as_unchecked
- end
-
- # Add comment about pushing new commits to merge requests
- comment_mr_with_commits(branch_name, commits, user)
-
- true
- end
-
- def comment_mr_with_commits(branch_name, commits, user)
- mrs = self.origin_merge_requests.opened.where(source_branch: branch_name).to_a
- mrs += self.fork_merge_requests.opened.where(source_branch: branch_name).to_a
-
- mrs.uniq.each do |merge_request|
- Note.create_new_commits_note(merge_request, merge_request.project,
- user, commits)
- end
+ MergeRequests::RefreshService.new(self, user).
+ execute(oldrev, newrev, ref)
end
def valid_repo?
repository.exists?
rescue
- errors.add(:path, "Invalid repository path")
+ errors.add(:path, 'Invalid repository path')
false
end
@@ -503,7 +541,7 @@ class Project < ActiveRecord::Base
end
def http_url_to_repo
- [gitlab_config.url, "/", path_with_namespace, ".git"].join('')
+ [gitlab_config.url, '/', path_with_namespace, '.git'].join('')
end
# Check if current branch name is marked as protected in the system
@@ -511,6 +549,10 @@ class Project < ActiveRecord::Base
protected_branches_names.include?(branch_name)
end
+ def developers_can_push_to_protected_branch?(branch_name)
+ protected_branches.any? { |pb| pb.name == branch_name && pb.developers_can_push }
+ end
+
def forked?
!(forked_project_link.nil? || forked_project_link.forked_from_project.nil?)
end
@@ -562,6 +604,7 @@ class Project < ActiveRecord::Base
# Since we do cache @event we need to reset cache in special cases:
# * when project was moved
# * when project was renamed
+ # * when the project avatar changes
# Events cache stored like events/23-20130109142513.
# The cache key includes updated_at timestamp.
# Thus it will automatically generate a new fragment
@@ -621,4 +664,25 @@ class Project < ActiveRecord::Base
def origin_merge_requests
merge_requests.where(source_project_id: self.id)
end
+
+ def create_repository
+ if gitlab_shell.add_repository(path_with_namespace)
+ true
+ else
+ errors.add(:base, 'Failed to create repository')
+ false
+ end
+ end
+
+ def repository_exists?
+ !!repository.exists?
+ end
+
+ def create_wiki
+ ProjectWiki.new(self, self.owner).wiki
+ true
+ rescue ProjectWiki::CouldNotCreateWikiError => ex
+ errors.add(:base, 'Failed create wiki')
+ false
+ end
end
diff --git a/app/models/project_contributions.rb b/app/models/project_contributions.rb
new file mode 100644
index 00000000000..8ab2d814a94
--- /dev/null
+++ b/app/models/project_contributions.rb
@@ -0,0 +1,23 @@
+class ProjectContributions
+ attr_reader :project, :user
+
+ def initialize(project, user)
+ @project, @user = project, user
+ end
+
+ def commits_log
+ repository = project.repository
+
+ if !repository.exists? || repository.empty?
+ return {}
+ end
+
+ Rails.cache.fetch(cache_key) do
+ repository.commits_per_day_for_user(user)
+ end
+ end
+
+ def cache_key
+ "#{Date.today.to_s}-commits-log-#{project.id}-#{user.email}"
+ end
+end
diff --git a/app/models/project_services/asana_service.rb b/app/models/project_services/asana_service.rb
new file mode 100644
index 00000000000..66b72572b9c
--- /dev/null
+++ b/app/models/project_services/asana_service.rb
@@ -0,0 +1,117 @@
+# == Schema Information
+#
+# Table name: services
+#
+# id :integer not null, primary key
+# type :string(255)
+# title :string(255)
+# project_id :integer
+# created_at :datetime
+# updated_at :datetime
+# active :boolean default(FALSE), not null
+# properties :text
+# template :boolean default(FALSE)
+#
+
+require 'asana'
+
+class AsanaService < Service
+ prop_accessor :api_key, :restrict_to_branch
+ validates :api_key, presence: true, if: :activated?
+
+ def title
+ 'Asana'
+ end
+
+ def description
+ 'Asana - Teamwork without email'
+ end
+
+ def help
+ 'This service adds commit messages as comments to Asana tasks.
+Once enabled, commit messages are checked for Asana task URLs
+(for example, `https://app.asana.com/0/123456/987654`) or task IDs
+starting with # (for example, `#987654`). Every task ID found will
+get the commit comment added to it.
+
+You can also close a task with a message containing: `fix #123456`.
+
+You can find your Api Keys here:
+http://developer.asana.com/documentation/#api_keys'
+ end
+
+ def to_param
+ 'asana'
+ end
+
+ def fields
+ [
+ {
+ type: 'text',
+ name: 'api_key',
+ placeholder: 'User API token. User must have access to task,
+all comments will be attributed to this user.'
+ },
+ {
+ type: 'text',
+ name: 'restrict_to_branch',
+ placeholder: 'Comma-separated list of branches which will be
+automatically inspected. Leave blank to include all branches.'
+ }
+ ]
+ end
+
+ def execute(push)
+ Asana.configure do |client|
+ client.api_key = api_key
+ end
+
+ user = push[:user_name]
+ branch = push[:ref].gsub('refs/heads/', '')
+
+ branch_restriction = restrict_to_branch.to_s
+
+ # check the branch restriction is poplulated and branch is not included
+ if branch_restriction.length > 0 && branch_restriction.index(branch) == nil
+ return
+ end
+
+ project_name = project.name_with_namespace
+ push_msg = user + ' pushed to branch ' + branch + ' of ' + project_name
+
+ push[:commits].each do |commit|
+ check_commit(' ( ' + commit[:url] + ' ): ' + commit[:message], push_msg)
+ end
+ end
+
+ def check_commit(message, push_msg)
+ task_list = []
+ close_list = []
+
+ message.split("\n").each do |line|
+ # look for a task ID or a full Asana url
+ task_list.concat(line.scan(/#(\d+)/))
+ task_list.concat(line.scan(/https:\/\/app\.asana\.com\/\d+\/\d+\/(\d+)/))
+ # look for a word starting with 'fix' followed by a task ID
+ close_list.concat(line.scan(/(fix\w*)\W*#(\d+)/i))
+ end
+
+ # post commit to every taskid found
+ task_list.each do |taskid|
+ task = Asana::Task.find(taskid[0])
+
+ if task
+ task.create_story(text: push_msg + ' ' + message)
+ end
+ end
+
+ # close all tasks that had 'fix(ed/es/ing) #:id' in them
+ close_list.each do |taskid|
+ task = Asana::Task.find(taskid.last)
+
+ if task
+ task.modify(completed: true)
+ end
+ end
+ end
+end
diff --git a/app/models/project_services/assembla_service.rb b/app/models/project_services/assembla_service.rb
index 0b90a14f39c..cf7598f35eb 100644
--- a/app/models/project_services/assembla_service.rb
+++ b/app/models/project_services/assembla_service.rb
@@ -5,11 +5,12 @@
# id :integer not null, primary key
# type :string(255)
# title :string(255)
-# project_id :integer not null
+# project_id :integer
# created_at :datetime
# updated_at :datetime
# active :boolean default(FALSE), not null
# properties :text
+# template :boolean default(FALSE)
#
class AssemblaService < Service
diff --git a/app/models/project_services/bamboo_service.rb b/app/models/project_services/bamboo_service.rb
index b9eec9ab21e..df68803152f 100644
--- a/app/models/project_services/bamboo_service.rb
+++ b/app/models/project_services/bamboo_service.rb
@@ -1,15 +1,36 @@
+# == Schema Information
+#
+# Table name: services
+#
+# id :integer not null, primary key
+# type :string(255)
+# title :string(255)
+# project_id :integer
+# created_at :datetime
+# updated_at :datetime
+# active :boolean default(FALSE), not null
+# properties :text
+# template :boolean default(FALSE)
+#
+
class BambooService < CiService
include HTTParty
prop_accessor :bamboo_url, :build_key, :username, :password
- validates :bamboo_url, presence: true,
- format: { with: URI::regexp }, if: :activated?
+ validates :bamboo_url,
+ presence: true,
+ format: { with: URI::regexp },
+ if: :activated?
validates :build_key, presence: true, if: :activated?
- validates :username, presence: true,
- if: ->(service) { service.password? }, if: :activated?
- validates :password, presence: true,
- if: ->(service) { service.username? }, if: :activated?
+ validates :username,
+ presence: true,
+ if: ->(service) { service.password? },
+ if: :activated?
+ validates :password,
+ presence: true,
+ if: ->(service) { service.username? },
+ if: :activated?
attr_accessor :response
diff --git a/app/models/project_services/buildbox_service.rb b/app/models/project_services/buildbox_service.rb
index 0ab67b79fe4..058c890ae45 100644
--- a/app/models/project_services/buildbox_service.rb
+++ b/app/models/project_services/buildbox_service.rb
@@ -5,13 +5,13 @@
# id :integer not null, primary key
# type :string(255)
# title :string(255)
-# project_id :integer not null
+# project_id :integer
# created_at :datetime
# updated_at :datetime
# active :boolean default(FALSE), not null
# properties :text
+# template :boolean default(FALSE)
#
-
require "addressable/uri"
class BuildboxService < CiService
diff --git a/app/models/project_services/campfire_service.rb b/app/models/project_services/campfire_service.rb
index 0736ddab99b..14b6b87a0b7 100644
--- a/app/models/project_services/campfire_service.rb
+++ b/app/models/project_services/campfire_service.rb
@@ -5,11 +5,12 @@
# id :integer not null, primary key
# type :string(255)
# title :string(255)
-# project_id :integer not null
+# project_id :integer
# created_at :datetime
# updated_at :datetime
# active :boolean default(FALSE), not null
# properties :text
+# template :boolean default(FALSE)
#
class CampfireService < Service
@@ -60,9 +61,9 @@ class CampfireService < Service
message << "[#{project.name_with_namespace}] "
message << "#{push[:user_name]} "
- if before =~ /000000/
+ if before.include?('000000')
message << "pushed new branch #{ref} \n"
- elsif after =~ /000000/
+ elsif after.include?('000000')
message << "removed branch #{ref} \n"
else
message << "pushed #{push[:total_commits_count]} commits to #{ref}. "
diff --git a/app/models/project_services/ci_service.rb b/app/models/project_services/ci_service.rb
index b1d5e49ede3..5a26c25b3c3 100644
--- a/app/models/project_services/ci_service.rb
+++ b/app/models/project_services/ci_service.rb
@@ -5,11 +5,12 @@
# id :integer not null, primary key
# type :string(255)
# title :string(255)
-# project_id :integer not null
+# project_id :integer
# created_at :datetime
# updated_at :datetime
# active :boolean default(FALSE), not null
# properties :text
+# template :boolean default(FALSE)
#
# Base class for CI services
diff --git a/app/models/project_services/custom_issue_tracker_service.rb b/app/models/project_services/custom_issue_tracker_service.rb
new file mode 100644
index 00000000000..b29d1c86881
--- /dev/null
+++ b/app/models/project_services/custom_issue_tracker_service.rb
@@ -0,0 +1,53 @@
+# == Schema Information
+#
+# Table name: services
+#
+# id :integer not null, primary key
+# type :string(255)
+# title :string(255)
+# project_id :integer
+# created_at :datetime
+# updated_at :datetime
+# active :boolean default(FALSE), not null
+# properties :text
+# template :boolean default(FALSE)
+#
+
+class CustomIssueTrackerService < IssueTrackerService
+
+ prop_accessor :title, :description, :project_url, :issues_url, :new_issue_url
+
+ def title
+ if self.properties && self.properties['title'].present?
+ self.properties['title']
+ else
+ 'Custom Issue Tracker'
+ end
+ end
+
+ def description
+ if self.properties && self.properties['description'].present?
+ self.properties['description']
+ else
+ 'Custom issue tracker'
+ end
+ end
+
+ def to_param
+ 'custom_issue_tracker'
+ end
+
+ def fields
+ [
+ { type: 'text', name: 'title', placeholder: title },
+ { type: 'text', name: 'description', placeholder: description },
+ { type: 'text', name: 'project_url', placeholder: 'Project url' },
+ { type: 'text', name: 'issues_url', placeholder: 'Issue url' },
+ { type: 'text', name: 'new_issue_url', placeholder: 'New Issue url' }
+ ]
+ end
+
+ def initialize_properties
+ self.properties = {} if properties.nil?
+ end
+end
diff --git a/app/models/project_services/emails_on_push_service.rb b/app/models/project_services/emails_on_push_service.rb
index b9071b98295..86693ad0c7e 100644
--- a/app/models/project_services/emails_on_push_service.rb
+++ b/app/models/project_services/emails_on_push_service.rb
@@ -5,11 +5,12 @@
# id :integer not null, primary key
# type :string(255)
# title :string(255)
-# project_id :integer not null
+# project_id :integer
# created_at :datetime
# updated_at :datetime
# active :boolean default(FALSE), not null
# properties :text
+# template :boolean default(FALSE)
#
class EmailsOnPushService < Service
diff --git a/app/models/project_services/flowdock_service.rb b/app/models/project_services/flowdock_service.rb
index 0020b4482e5..13e2dfceb1a 100644
--- a/app/models/project_services/flowdock_service.rb
+++ b/app/models/project_services/flowdock_service.rb
@@ -5,11 +5,12 @@
# id :integer not null, primary key
# type :string(255)
# title :string(255)
-# project_id :integer not null
+# project_id :integer
# created_at :datetime
# updated_at :datetime
# active :boolean default(FALSE), not null
# properties :text
+# template :boolean default(FALSE)
#
require "flowdock-git-hook"
@@ -37,13 +38,12 @@ class FlowdockService < Service
end
def execute(push_data)
- repo_path = File.join(Gitlab.config.gitlab_shell.repos_path, "#{project.path_with_namespace}.git")
Flowdock::Git.post(
push_data[:ref],
push_data[:before],
push_data[:after],
token: token,
- repo: repo_path,
+ repo: project.repository.path_to_repo,
repo_url: "#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}",
commit_url: "#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}/commit/%s",
diff_url: "#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}/compare/%s...%s",
diff --git a/app/models/project_services/gemnasium_service.rb b/app/models/project_services/gemnasium_service.rb
index 6d2fc06a5d0..a2c87ae88f1 100644
--- a/app/models/project_services/gemnasium_service.rb
+++ b/app/models/project_services/gemnasium_service.rb
@@ -5,11 +5,12 @@
# id :integer not null, primary key
# type :string(255)
# title :string(255)
-# project_id :integer not null
+# project_id :integer
# created_at :datetime
# updated_at :datetime
# active :boolean default(FALSE), not null
# properties :text
+# template :boolean default(FALSE)
#
require "gemnasium/gitlab_service"
@@ -38,14 +39,13 @@ class GemnasiumService < Service
end
def execute(push_data)
- repo_path = File.join(Gitlab.config.gitlab_shell.repos_path, "#{project.path_with_namespace}.git")
Gemnasium::GitlabService.execute(
ref: push_data[:ref],
before: push_data[:before],
after: push_data[:after],
token: token,
api_key: api_key,
- repo: repo_path
+ repo: project.repository.path_to_repo
)
end
end
diff --git a/app/models/project_services/gitlab_ci_service.rb b/app/models/project_services/gitlab_ci_service.rb
index a897c4ab76b..f4b463e8199 100644
--- a/app/models/project_services/gitlab_ci_service.rb
+++ b/app/models/project_services/gitlab_ci_service.rb
@@ -5,11 +5,12 @@
# id :integer not null, primary key
# type :string(255)
# title :string(255)
-# project_id :integer not null
+# project_id :integer
# created_at :datetime
# updated_at :datetime
# active :boolean default(FALSE), not null
# properties :text
+# template :boolean default(FALSE)
#
class GitlabCiService < CiService
@@ -28,7 +29,7 @@ class GitlabCiService < CiService
end
def commit_status_path(sha)
- project_url + "/builds/#{sha}/status.json?token=#{token}"
+ project_url + "/commits/#{sha}/status.json?token=#{token}"
end
def get_ci_build(sha)
@@ -55,7 +56,7 @@ class GitlabCiService < CiService
end
def build_page(sha)
- project_url + "/builds/#{sha}"
+ project_url + "/commits/#{sha}"
end
def builds_path
@@ -81,7 +82,7 @@ class GitlabCiService < CiService
def fields
[
{ type: 'text', name: 'token', placeholder: 'GitLab CI project specific token' },
- { type: 'text', name: 'project_url', placeholder: 'http://ci.gitlabhq.com/projects/3'}
+ { type: 'text', name: 'project_url', placeholder: 'http://ci.gitlabhq.com/projects/3' }
]
end
end
diff --git a/app/models/project_services/gitlab_issue_tracker_service.rb b/app/models/project_services/gitlab_issue_tracker_service.rb
new file mode 100644
index 00000000000..05c048e4e45
--- /dev/null
+++ b/app/models/project_services/gitlab_issue_tracker_service.rb
@@ -0,0 +1,46 @@
+# == Schema Information
+#
+# Table name: services
+#
+# id :integer not null, primary key
+# type :string(255)
+# title :string(255)
+# project_id :integer
+# created_at :datetime
+# updated_at :datetime
+# active :boolean default(FALSE), not null
+# properties :text
+# template :boolean default(FALSE)
+#
+
+class GitlabIssueTrackerService < IssueTrackerService
+ include Rails.application.routes.url_helpers
+ prop_accessor :title, :description, :project_url, :issues_url, :new_issue_url
+
+
+ def default?
+ true
+ end
+
+ def to_param
+ 'gitlab'
+ end
+
+ def project_url
+ "#{gitlab_url}#{namespace_project_issues_path(project.namespace, project)}"
+ end
+
+ def new_issue_url
+ "#{gitlab_url}#{new_namespace_project_issue_path(namespace_id: project.namespace, project_id: project)}"
+ end
+
+ def issue_url(iid)
+ "#{gitlab_url}#{namespace_project_issue_path(namespace_id: project.namespace, project_id: project, id: iid)}"
+ end
+
+ private
+
+ def gitlab_url
+ Gitlab.config.gitlab.relative_url_root.chomp("/") if Gitlab.config.gitlab.relative_url_root
+ end
+end
diff --git a/app/models/project_services/hipchat_service.rb b/app/models/project_services/hipchat_service.rb
index 4078938cdbb..003e06a4c80 100644
--- a/app/models/project_services/hipchat_service.rb
+++ b/app/models/project_services/hipchat_service.rb
@@ -5,21 +5,22 @@
# id :integer not null, primary key
# type :string(255)
# title :string(255)
-# project_id :integer not null
+# project_id :integer
# created_at :datetime
# updated_at :datetime
# active :boolean default(FALSE), not null
# properties :text
+# template :boolean default(FALSE)
#
class HipchatService < Service
MAX_COMMITS = 3
- prop_accessor :token, :room
+ prop_accessor :token, :room, :server
validates :token, presence: true, if: :activated?
def title
- 'Hipchat'
+ 'HipChat'
end
def description
@@ -32,8 +33,10 @@ class HipchatService < Service
def fields
[
- { type: 'text', name: 'token', placeholder: '' },
- { type: 'text', name: 'room', placeholder: '' }
+ { type: 'text', name: 'token', placeholder: 'Room token' },
+ { type: 'text', name: 'room', placeholder: 'Room name or ID' },
+ { type: 'text', name: 'server',
+ placeholder: 'Leave blank for default. https://hipchat.example.com' }
]
end
@@ -44,7 +47,9 @@ class HipchatService < Service
private
def gate
- @gate ||= HipChat::Client.new(token)
+ options = { api_version: 'v2' }
+ options[:server_url] = server unless server.blank?
+ @gate ||= HipChat::Client.new(token, options)
end
def create_message(push)
@@ -54,12 +59,12 @@ class HipchatService < Service
message = ""
message << "#{push[:user_name]} "
- if before =~ /000000/
+ if before.include?('000000')
message << "pushed new branch <a href=\""\
"#{project.web_url}/commits/#{URI.escape(ref)}\">#{ref}</a>"\
" to <a href=\"#{project.web_url}\">"\
"#{project.name_with_namespace.gsub!(/\s/, "")}</a>\n"
- elsif after =~ /000000/
+ elsif after.include?('000000')
message << "removed branch #{ref} from <a href=\"#{project.web_url}\">#{project.name_with_namespace.gsub!(/\s/,'')}</a> \n"
else
message << "pushed to branch <a href=\""\
diff --git a/app/models/project_services/issue_tracker_service.rb b/app/models/project_services/issue_tracker_service.rb
new file mode 100644
index 00000000000..c991a34ecdb
--- /dev/null
+++ b/app/models/project_services/issue_tracker_service.rb
@@ -0,0 +1,114 @@
+# == Schema Information
+#
+# Table name: services
+#
+# id :integer not null, primary key
+# type :string(255)
+# title :string(255)
+# project_id :integer
+# created_at :datetime
+# updated_at :datetime
+# active :boolean default(FALSE), not null
+# properties :text
+# template :boolean default(FALSE)
+#
+
+class IssueTrackerService < Service
+
+ validates :project_url, :issues_url, :new_issue_url, presence: true, if: :activated?
+
+ def category
+ :issue_tracker
+ end
+
+ def default?
+ false
+ end
+
+ def project_url
+ # implement inside child
+ end
+
+ def issues_url
+ # implement inside child
+ end
+
+ def new_issue_url
+ # implement inside child
+ end
+
+ def issue_url(iid)
+ self.issues_url.gsub(':id', iid.to_s)
+ end
+
+ def fields
+ [
+ { type: 'text', name: 'description', placeholder: description },
+ { type: 'text', name: 'project_url', placeholder: 'Project url' },
+ { type: 'text', name: 'issues_url', placeholder: 'Issue url' },
+ { type: 'text', name: 'new_issue_url', placeholder: 'New Issue url' }
+ ]
+ end
+
+ def initialize_properties
+ if properties.nil?
+ if enabled_in_gitlab_config
+ self.properties = {
+ title: issues_tracker['title'],
+ project_url: set_project_url,
+ issues_url: issues_tracker['issues_url'],
+ new_issue_url: issues_tracker['new_issue_url']
+ }
+ else
+ self.properties = {}
+ end
+ end
+ end
+
+ def execute(data)
+ message = "#{self.type} was unable to reach #{self.project_url}. Check the url and try again."
+ result = false
+
+ begin
+ url = URI.parse(self.project_url)
+
+ if url.host && url.port
+ http = Net::HTTP.start(url.host, url.port, { open_timeout: 5, read_timeout: 5 })
+ response = http.head("/")
+
+ if response
+ message = "#{self.type} received response #{response.code} when attempting to connect to #{self.project_url}"
+ result = true
+ end
+ end
+ rescue Timeout::Error, SocketError, Errno::ECONNRESET, Errno::ECONNREFUSED => error
+ message = "#{self.type} had an error when trying to connect to #{self.project_url}: #{error.message}"
+ end
+ Rails.logger.info(message)
+ result
+ end
+
+ private
+
+ def enabled_in_gitlab_config
+ Gitlab.config.issues_tracker &&
+ Gitlab.config.issues_tracker.values.any? &&
+ issues_tracker
+ end
+
+ def issues_tracker
+ Gitlab.config.issues_tracker[to_param]
+ end
+
+ def set_project_url
+ if self.project
+ id = self.project.issues_tracker_id
+
+ if id
+ issues_tracker['project_url'].gsub(":issues_tracker_id", id)
+ end
+ end
+
+ issues_tracker['project_url']
+ end
+end
diff --git a/app/models/project_services/jira_service.rb b/app/models/project_services/jira_service.rb
new file mode 100644
index 00000000000..4c056605ea8
--- /dev/null
+++ b/app/models/project_services/jira_service.rb
@@ -0,0 +1,53 @@
+# == Schema Information
+#
+# Table name: services
+#
+# id :integer not null, primary key
+# type :string(255)
+# title :string(255)
+# project_id :integer
+# created_at :datetime
+# updated_at :datetime
+# active :boolean default(FALSE), not null
+# properties :text
+# template :boolean default(FALSE)
+#
+
+class JiraService < IssueTrackerService
+ include Rails.application.routes.url_helpers
+
+ prop_accessor :title, :description, :project_url, :issues_url, :new_issue_url
+
+ def help
+ issue_tracker_link = help_page_path("integration", "external-issue-tracker")
+
+ line1 = "Setting `project_url`, `issues_url` and `new_issue_url` will "\
+ "allow a user to easily navigate to the Jira issue tracker. "\
+ "See the [integration doc](#{issue_tracker_link}) for details."
+
+ line2 = 'Support for referencing commits and automatic closing of Jira issues directly ' \
+ 'from GitLab is [available in GitLab EE.](http://doc.gitlab.com/ee/integration/jira.html)'
+
+ [line1, line2].join("\n\n")
+ end
+
+ def title
+ if self.properties && self.properties['title'].present?
+ self.properties['title']
+ else
+ 'JIRA'
+ end
+ end
+
+ def description
+ if self.properties && self.properties['description'].present?
+ self.properties['description']
+ else
+ 'Jira issue tracker'
+ end
+ end
+
+ def to_param
+ 'jira'
+ end
+end
diff --git a/app/models/project_services/pivotaltracker_service.rb b/app/models/project_services/pivotaltracker_service.rb
index 09e114f9cca..287812c57a5 100644
--- a/app/models/project_services/pivotaltracker_service.rb
+++ b/app/models/project_services/pivotaltracker_service.rb
@@ -5,11 +5,12 @@
# id :integer not null, primary key
# type :string(255)
# title :string(255)
-# project_id :integer not null
+# project_id :integer
# created_at :datetime
# updated_at :datetime
# active :boolean default(FALSE), not null
# properties :text
+# template :boolean default(FALSE)
#
class PivotaltrackerService < Service
diff --git a/app/models/project_services/pushover_service.rb b/app/models/project_services/pushover_service.rb
index f247fde7762..3a3af59390a 100644
--- a/app/models/project_services/pushover_service.rb
+++ b/app/models/project_services/pushover_service.rb
@@ -5,11 +5,12 @@
# id :integer not null, primary key
# type :string(255)
# title :string(255)
-# project_id :integer not null
+# project_id :integer
# created_at :datetime
# updated_at :datetime
# active :boolean default(FALSE), not null
# properties :text
+# template :boolean default(FALSE)
#
class PushoverService < Service
@@ -80,9 +81,9 @@ class PushoverService < Service
before = push_data[:before]
after = push_data[:after]
- if before =~ /000000/
+ if before.include?('000000')
message = "#{push_data[:user_name]} pushed new branch \"#{ref}\"."
- elsif after =~ /000000/
+ elsif after.include?('000000')
message = "#{push_data[:user_name]} deleted branch \"#{ref}\"."
else
message = "#{push_data[:user_name]} push to branch \"#{ref}\"."
diff --git a/app/models/project_services/redmine_service.rb b/app/models/project_services/redmine_service.rb
new file mode 100644
index 00000000000..e1dc10415e0
--- /dev/null
+++ b/app/models/project_services/redmine_service.rb
@@ -0,0 +1,39 @@
+# == Schema Information
+#
+# Table name: services
+#
+# id :integer not null, primary key
+# type :string(255)
+# title :string(255)
+# project_id :integer
+# created_at :datetime
+# updated_at :datetime
+# active :boolean default(FALSE), not null
+# properties :text
+# template :boolean default(FALSE)
+#
+
+class RedmineService < IssueTrackerService
+
+ prop_accessor :title, :description, :project_url, :issues_url, :new_issue_url
+
+ def title
+ if self.properties && self.properties['title'].present?
+ self.properties['title']
+ else
+ 'Redmine'
+ end
+ end
+
+ def description
+ if self.properties && self.properties['description'].present?
+ self.properties['description']
+ else
+ 'Redmine issue tracker'
+ end
+ end
+
+ def to_param
+ 'redmine'
+ end
+end
diff --git a/app/models/project_services/slack_message.rb b/app/models/project_services/slack_message.rb
index 28204e5ea60..6c6446db45f 100644
--- a/app/models/project_services/slack_message.rb
+++ b/app/models/project_services/slack_message.rb
@@ -1,6 +1,14 @@
require 'slack-notifier'
class SlackMessage
+ attr_reader :after
+ attr_reader :before
+ attr_reader :commits
+ attr_reader :project_name
+ attr_reader :project_url
+ attr_reader :ref
+ attr_reader :username
+
def initialize(params)
@after = params.fetch(:after)
@before = params.fetch(:before)
@@ -23,14 +31,6 @@ class SlackMessage
private
- attr_reader :after
- attr_reader :before
- attr_reader :commits
- attr_reader :project_name
- attr_reader :project_url
- attr_reader :ref
- attr_reader :username
-
def message
if new_branch?
new_branch_message
@@ -77,11 +77,11 @@ class SlackMessage
end
def new_branch?
- before =~ /000000/
+ before.include?('000000')
end
def removed_branch?
- after =~ /000000/
+ after.include?('000000')
end
def branch_url
diff --git a/app/models/project_services/slack_service.rb b/app/models/project_services/slack_service.rb
index 963f5440b6f..297d8bbb5d4 100644
--- a/app/models/project_services/slack_service.rb
+++ b/app/models/project_services/slack_service.rb
@@ -5,11 +5,12 @@
# id :integer not null, primary key
# type :string(255)
# title :string(255)
-# project_id :integer not null
+# project_id :integer
# created_at :datetime
# updated_at :datetime
# active :boolean default(FALSE), not null
# properties :text
+# template :boolean default(FALSE)
#
class SlackService < Service
diff --git a/app/models/project_services/teamcity_service.rb b/app/models/project_services/teamcity_service.rb
new file mode 100644
index 00000000000..c4b6ef5d9a9
--- /dev/null
+++ b/app/models/project_services/teamcity_service.rb
@@ -0,0 +1,134 @@
+# == Schema Information
+#
+# Table name: services
+#
+# id :integer not null, primary key
+# type :string(255)
+# title :string(255)
+# project_id :integer
+# created_at :datetime
+# updated_at :datetime
+# active :boolean default(FALSE), not null
+# properties :text
+# template :boolean default(FALSE)
+#
+
+class TeamcityService < CiService
+ include HTTParty
+
+ prop_accessor :teamcity_url, :build_type, :username, :password
+
+ validates :teamcity_url,
+ presence: true,
+ format: { with: URI::regexp }, if: :activated?
+ validates :build_type, presence: true, if: :activated?
+ validates :username,
+ presence: true,
+ if: ->(service) { service.password? }, if: :activated?
+ validates :password,
+ presence: true,
+ if: ->(service) { service.username? }, if: :activated?
+
+ attr_accessor :response
+
+ after_save :compose_service_hook, if: :activated?
+
+ def compose_service_hook
+ hook = service_hook || build_service_hook
+ hook.save
+ end
+
+ def title
+ 'JetBrains TeamCity CI'
+ end
+
+ def description
+ 'A continuous integration and build server'
+ end
+
+ def help
+ 'The build configuration in Teamcity must use the build format '\
+ 'number %build.vcs.number% '\
+ 'you will also want to configure monitoring of all branches so merge '\
+ 'requests build, that setting is in the vsc root advanced settings.'
+ end
+
+ def to_param
+ 'teamcity'
+ end
+
+ def fields
+ [
+ { type: 'text', name: 'teamcity_url',
+ placeholder: 'TeamCity root URL like https://teamcity.example.com' },
+ { type: 'text', name: 'build_type',
+ placeholder: 'Build configuration ID' },
+ { type: 'text', name: 'username',
+ placeholder: 'A user with permissions to trigger a manual build' },
+ { type: 'password', name: 'password' },
+ ]
+ end
+
+ def build_info(sha)
+ url = URI.parse("#{teamcity_url}/httpAuth/app/rest/builds/"\
+ "branch:unspecified:any,number:#{sha}")
+ auth = {
+ username: username,
+ password: password,
+ }
+ @response = HTTParty.get("#{url}", verify: false, basic_auth: auth)
+ end
+
+ def build_page(sha)
+ build_info(sha) if @response.nil? || !@response.code
+
+ if @response.code != 200
+ # If actual build link can't be determined,
+ # send user to build summary page.
+ "#{teamcity_url}/viewLog.html?buildTypeId=#{build_type}"
+ else
+ # If actual build link is available, go to build result page.
+ built_id = @response['build']['id']
+ "#{teamcity_url}/viewLog.html?buildId=#{built_id}"\
+ "&buildTypeId=#{build_type}"
+ end
+ end
+
+ def commit_status(sha)
+ build_info(sha) if @response.nil? || !@response.code
+ return :error unless @response.code == 200 || @response.code == 404
+
+ status = if @response.code == 404
+ 'Pending'
+ else
+ @response['build']['status']
+ end
+
+ if status.include?('SUCCESS')
+ 'success'
+ elsif status.include?('FAILURE')
+ 'failed'
+ elsif status.include?('Pending')
+ 'pending'
+ else
+ :error
+ end
+ end
+
+ def execute(data)
+ auth = {
+ username: username,
+ password: password,
+ }
+
+ branch = data[:ref]
+
+ self.class.post("#{teamcity_url}/httpAuth/app/rest/buildQueue",
+ body: "<build branchName=\"#{branch}\">"\
+ "<buildType id=\"#{build_type}\"/>"\
+ '</build>',
+ headers: { 'Content-type' => 'application/xml' },
+ basic_auth: auth
+ )
+ end
+end
diff --git a/app/models/project_team.rb b/app/models/project_team.rb
index 657ee23ae23..bc9c3ce58f6 100644
--- a/app/models/project_team.rb
+++ b/app/models/project_team.rb
@@ -160,7 +160,7 @@ class ProjectTeam
end
user_ids = project_members.pluck(:user_id)
- user_ids += group_members.pluck(:user_id) if group
+ user_ids.push(*group_members.pluck(:user_id)) if group
User.where(id: user_ids)
end
diff --git a/app/models/project_wiki.rb b/app/models/project_wiki.rb
index 770a26ed894..55438bee245 100644
--- a/app/models/project_wiki.rb
+++ b/app/models/project_wiki.rb
@@ -5,7 +5,7 @@ class ProjectWiki
'Markdown' => :markdown,
'RDoc' => :rdoc,
'AsciiDoc' => :asciidoc
- }
+ } unless defined?(MARKUPS)
class CouldNotCreateWikiError < StandardError; end
@@ -136,7 +136,7 @@ class ProjectWiki
def commit_details(action, message = nil, title = nil)
commit_message = message || default_message(action, title)
- {email: @user.email, name: @user.name, message: commit_message}
+ { email: @user.email, name: @user.name, message: commit_message }
end
def default_message(action, title)
diff --git a/app/models/protected_branch.rb b/app/models/protected_branch.rb
index 1b06dd77523..97207ba1272 100644
--- a/app/models/protected_branch.rb
+++ b/app/models/protected_branch.rb
@@ -2,11 +2,12 @@
#
# Table name: protected_branches
#
-# id :integer not null, primary key
-# project_id :integer not null
-# name :string(255) not null
-# created_at :datetime
-# updated_at :datetime
+# id :integer not null, primary key
+# project_id :integer not null
+# name :string(255) not null
+# created_at :datetime
+# updated_at :datetime
+# developers_can_push :boolean default(FALSE), not null
#
class ProtectedBranch < ActiveRecord::Base
diff --git a/app/models/repository.rb b/app/models/repository.rb
index 93994123a90..bbf35f04bbc 100644
--- a/app/models/repository.rb
+++ b/app/models/repository.rb
@@ -30,7 +30,7 @@ class Repository
commit = Gitlab::Git::Commit.find(raw_repository, id)
commit = Commit.new(commit) if commit
commit
- rescue Rugged::OdbError => ex
+ rescue Rugged::OdbError
nil
end
@@ -61,25 +61,25 @@ class Repository
end
def add_branch(branch_name, ref)
- Rails.cache.delete(cache_key(:branch_names))
+ cache.expire(:branch_names)
gitlab_shell.add_branch(path_with_namespace, branch_name, ref)
end
def add_tag(tag_name, ref, message = nil)
- Rails.cache.delete(cache_key(:tag_names))
+ cache.expire(:tag_names)
gitlab_shell.add_tag(path_with_namespace, tag_name, ref, message)
end
def rm_branch(branch_name)
- Rails.cache.delete(cache_key(:branch_names))
+ cache.expire(:branch_names)
gitlab_shell.rm_branch(path_with_namespace, branch_name)
end
def rm_tag(tag_name)
- Rails.cache.delete(cache_key(:tag_names))
+ cache.expire(:tag_names)
gitlab_shell.rm_tag(path_with_namespace, tag_name)
end
@@ -97,19 +97,15 @@ class Repository
end
def branch_names
- Rails.cache.fetch(cache_key(:branch_names)) do
- raw_repository.branch_names
- end
+ cache.fetch(:branch_names) { raw_repository.branch_names }
end
def tag_names
- Rails.cache.fetch(cache_key(:tag_names)) do
- raw_repository.tag_names
- end
+ cache.fetch(:tag_names) { raw_repository.tag_names }
end
def commit_count
- Rails.cache.fetch(cache_key(:commit_count)) do
+ cache.fetch(:commit_count) do
begin
raw_repository.commit_count(self.root_ref)
rescue
@@ -121,26 +117,21 @@ class Repository
# Return repo size in megabytes
# Cached in redis
def size
- Rails.cache.fetch(cache_key(:size)) do
- raw_repository.size
- end
+ cache.fetch(:size) { raw_repository.size }
end
def expire_cache
- Rails.cache.delete(cache_key(:size))
- Rails.cache.delete(cache_key(:branch_names))
- 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))
- Rails.cache.delete(cache_key(:version))
- Rails.cache.delete(cache_key(:contribution_guide))
+ %i(size branch_names tag_names commit_count graph_log
+ readme version contribution_guide).each do |key|
+ cache.expire(key)
+ end
end
def graph_log
- Rails.cache.fetch(cache_key(:graph_log)) do
+ cache.fetch(:graph_log) do
commits = raw_repository.log(limit: 6000, skip_merges: true,
ref: root_ref)
+
commits.map do |rugged_commit|
commit = Gitlab::Git::Commit.new(rugged_commit)
@@ -148,14 +139,30 @@ class Repository
author_name: commit.author_name.force_encoding('UTF-8'),
author_email: commit.author_email.force_encoding('UTF-8'),
additions: commit.stats.additions,
- deletions: commit.stats.deletions
+ deletions: commit.stats.deletions,
}
end
end
end
- def cache_key(type)
- "#{type}:#{path_with_namespace}"
+ def timestamps_by_user_log(user)
+ args = %W(git log --author=#{user.email} --since=#{(Date.today - 1.year).to_s} --pretty=format:%cd --date=short)
+ dates = Gitlab::Popen.popen(args, path_to_repo).first.split("\n")
+
+ if dates.present?
+ dates
+ else
+ []
+ end
+ end
+
+ def commits_per_day_for_user(user)
+ timestamps_by_user_log(user).
+ group_by { |commit_date| commit_date }.
+ inject({}) do |hash, (timestamp_date, commits)|
+ hash[timestamp_date] = commits.count
+ hash
+ end
end
def method_missing(m, *args, &block)
@@ -177,13 +184,11 @@ class Repository
end
def readme
- Rails.cache.fetch(cache_key(:readme)) do
- tree(:head).readme
- end
+ cache.fetch(:readme) { tree(:head).readme }
end
def version
- Rails.cache.fetch(cache_key(:version)) do
+ cache.fetch(:version) do
tree(:head).blobs.find do |file|
file.name.downcase == 'version'
end
@@ -191,9 +196,7 @@ class Repository
end
def contribution_guide
- Rails.cache.fetch(cache_key(:contribution_guide)) do
- tree(:head).contribution_guide
- end
+ cache.fetch(:contribution_guide) { tree(:head).contribution_guide }
end
def head_commit
@@ -235,7 +238,7 @@ class Repository
end
def last_commit_for_path(sha, path)
- args = %W(git rev-list --max-count 1 #{sha} -- #{path})
+ args = %W(git rev-list --max-count=1 #{sha} -- #{path})
sha = Gitlab::Popen.popen(args, path_to_repo).first.strip
commit(sha)
end
@@ -312,4 +315,27 @@ class Repository
[]
end
end
+
+ def tag_names_contains(sha)
+ args = %W(git tag --contains #{sha})
+ names = Gitlab::Popen.popen(args, path_to_repo).first
+
+ if names.respond_to?(:split)
+ names = names.split("\n").map(&:strip)
+
+ names.each do |name|
+ name.slice! '* '
+ end
+
+ names
+ else
+ []
+ end
+ end
+
+ private
+
+ def cache
+ @cache ||= RepositoryCache.new(path_with_namespace)
+ end
end
diff --git a/app/models/service.rb b/app/models/service.rb
index c489c1e96e1..f87d875c10a 100644
--- a/app/models/service.rb
+++ b/app/models/service.rb
@@ -5,16 +5,17 @@
# id :integer not null, primary key
# type :string(255)
# title :string(255)
-# project_id :integer not null
+# project_id :integer
# created_at :datetime
# updated_at :datetime
# active :boolean default(FALSE), not null
# properties :text
-#
+# template :boolean default(FALSE)
# To add new service you should build a class inherited from Service
# and implement a set of methods
class Service < ActiveRecord::Base
+ include Sortable
serialize :properties, JSON
default_value_for :active, false
@@ -24,12 +25,18 @@ class Service < ActiveRecord::Base
belongs_to :project
has_one :service_hook
- validates :project_id, presence: true
+ validates :project_id, presence: true, unless: Proc.new { |service| service.template? }
+
+ scope :visible, -> { where.not(type: 'GitlabIssueTrackerService') }
def activated?
active
end
+ def template?
+ template
+ end
+
def category
:common
end
@@ -82,4 +89,24 @@ class Service < ActiveRecord::Base
}
end
end
+
+ def async_execute(data)
+ Sidekiq::Client.enqueue(ProjectServiceWorker, id, data)
+ end
+
+ def issue_tracker?
+ self.category == :issue_tracker
+ end
+
+ def self.available_services_names
+ %w(gitlab_ci campfire hipchat pivotaltracker flowdock assembla asana
+ emails_on_push gemnasium slack pushover buildbox bamboo teamcity jira redmine custom_issue_tracker)
+ end
+
+ def self.create_from_template(project_id, template)
+ service = template.dup
+ service.template = false
+ service.project_id = project_id
+ service if service.save
+ end
end
diff --git a/app/models/snippet.rb b/app/models/snippet.rb
index a47fbca3260..82c1ab94446 100644
--- a/app/models/snippet.rb
+++ b/app/models/snippet.rb
@@ -16,6 +16,7 @@
#
class Snippet < ActiveRecord::Base
+ include Sortable
include Linguist::BlobHelper
include Gitlab::VisibilityLevel
@@ -29,7 +30,11 @@ class Snippet < ActiveRecord::Base
validates :author, presence: true
validates :title, presence: true, length: { within: 0..255 }
- validates :file_name, presence: true, length: { within: 0..255 }
+ validates :file_name,
+ presence: true,
+ length: { within: 0..255 },
+ format: { with: Gitlab::Regex.path_regex,
+ message: Gitlab::Regex.path_regex_message }
validates :content, presence: true
validates :visibility_level, inclusion: { in: Gitlab::VisibilityLevel.values }
@@ -62,6 +67,10 @@ class Snippet < ActiveRecord::Base
file_name
end
+ def sanitized_file_name
+ file_name.gsub(/[^a-zA-Z0-9_\-\.]+/, '')
+ end
+
def mode
nil
end
@@ -72,7 +81,7 @@ class Snippet < ActiveRecord::Base
def visibility_level_field
visibility_level
- end
+ end
class << self
def search(query)
diff --git a/app/models/user.rb b/app/models/user.rb
index 154cc0f3e16..55768a351e3 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -26,8 +26,6 @@
# bio :string(255)
# failed_attempts :integer default(0)
# locked_at :datetime
-# extern_uid :string(255)
-# provider :string(255)
# username :string(255)
# can_create_group :boolean default(TRUE), not null
# can_create_team :boolean default(TRUE), not null
@@ -36,29 +34,36 @@
# notification_level :integer default(1), not null
# password_expires_at :datetime
# created_by_id :integer
-# last_credential_check_at :datetime
# 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)
+# hide_no_password :boolean default(FALSE)
# website_url :string(255) default(""), not null
+# last_credential_check_at :datetime
+# github_access_token :string(255)
+# notification_email :string(255)
+# password_automatically_set :boolean default(FALSE)
+# bitbucket_access_token :string(255)
#
require 'carrierwave/orm/activerecord'
require 'file_size_validator'
class User < ActiveRecord::Base
+ include Sortable
include Gitlab::ConfigHelper
- extend Gitlab::ConfigHelper
include TokenAuthenticatable
+ extend Gitlab::ConfigHelper
+ include Gitlab::CurrentSettings
default_value_for :admin, false
default_value_for :can_create_group, gitlab_config.default_can_create_group
default_value_for :can_create_team, false
default_value_for :hide_no_ssh_key, false
- default_value_for :projects_limit, gitlab_config.default_projects_limit
+ default_value_for :hide_no_password, false
default_value_for :theme_id, gitlab_config.default_theme
devise :database_authenticatable, :lockable, :async,
@@ -79,6 +84,7 @@ class User < ActiveRecord::Base
# Profile
has_many :keys, dependent: :destroy
has_many :emails, dependent: :destroy
+ has_many :identities, dependent: :destroy
# Groups
has_many :members, dependent: :destroy
@@ -105,32 +111,38 @@ class User < ActiveRecord::Base
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 :oauth_applications, class_name: 'Doorkeeper::Application', as: :owner, dependent: :destroy
#
# Validations
#
validates :name, presence: true
- validates :email, presence: true, email: {strict_mode: true}, uniqueness: true
+ validates :email, presence: true, email: { strict_mode: true }, uniqueness: true
+ validates :notification_email, presence: true, email: { strict_mode: 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: { case_sensitive: false },
- exclusion: { in: Gitlab::Blacklist.path },
- format: { with: Gitlab::Regex.username_regex,
- message: Gitlab::Regex.username_regex_message }
+ validates :projects_limit, presence: true, numericality: { greater_than_or_equal_to: 0 }
+ validates :username,
+ presence: true,
+ uniqueness: { case_sensitive: false },
+ exclusion: { in: Gitlab::Blacklist.path },
+ format: { with: Gitlab::Regex.username_regex,
+ message: Gitlab::Regex.username_regex_message }
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? }
validate :unique_email, if: ->(user) { user.email_changed? }
- validates :avatar, file_size: { maximum: 100.kilobytes.to_i }
+ validate :owns_notification_email, if: ->(user) { user.notification_email_changed? }
+ validates :avatar, file_size: { maximum: 200.kilobytes.to_i }
before_validation :generate_password, on: :create
before_validation :sanitize_attrs
+ before_validation :set_notification_email, if: ->(user) { user.email_changed? }
before_save :ensure_authentication_token
after_save :ensure_namespace_correct
+ after_initialize :set_projects_limit
after_create :post_create_hook
after_destroy :post_destroy_hook
@@ -167,18 +179,16 @@ class User < ActiveRecord::Base
end
end
- mount_uploader :avatar, AttachmentUploader
+ mount_uploader :avatar, AvatarUploader
# Scopes
scope :admins, -> { where(admin: true) }
scope :blocked, -> { with_state(:blocked) }
scope :active, -> { with_state(:active) }
- 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) ) : all }
scope :without_projects, -> { where('id NOT IN (SELECT DISTINCT(user_id) FROM members)') }
- scope :ldap, -> { where('provider LIKE ?', 'ldap%') }
scope :potential_team_members, ->(team) { team.members.any? ? active.not_in_team(team) : active }
#
@@ -197,11 +207,10 @@ class User < ActiveRecord::Base
def sort(method)
case method.to_s
- when 'recent_sign_in' then reorder('users.last_sign_in_at DESC')
- when 'oldest_sign_in' then reorder('users.last_sign_in_at ASC')
- when 'recently_created' then reorder('users.created_at DESC')
- when 'late_created' then reorder('users.created_at ASC')
- else reorder("users.name ASC")
+ when 'recent_sign_in' then reorder(last_sign_in_at: :desc)
+ when 'oldest_sign_in' then reorder(last_sign_in_at: :asc)
+ else
+ order_by(method)
end
end
@@ -226,6 +235,11 @@ class User < ActiveRecord::Base
where("lower(name) LIKE :query OR lower(email) LIKE :query OR lower(username) LIKE :query", query: "%#{query.downcase}%")
end
+ def by_login(login)
+ where('lower(username) = :value OR lower(email) = :value',
+ value: login.to_s.downcase).first
+ end
+
def by_username_or_id(name_or_id)
where('users.username = ? OR users.id = ?', name_or_id.to_s, name_or_id.to_i).first
end
@@ -233,6 +247,22 @@ class User < ActiveRecord::Base
def build_user(attrs = {})
User.new(attrs)
end
+
+ def clean_username(username)
+ username.gsub!(/@.*\z/, "")
+ username.gsub!(/\.git\z/, "")
+ username.gsub!(/\A-/, "")
+ username.gsub!(/[^a-zA-Z0-9_\-\.]/, "")
+
+ counter = 0
+ base = username
+ while User.by_login(username).present? || Namespace.by_path(username).present?
+ counter += 1
+ username = "#{base}#{counter}"
+ end
+
+ username
+ end
end
#
@@ -264,7 +294,8 @@ class User < ActiveRecord::Base
def namespace_uniq
namespace_name = self.username
- if Namespace.find_by(path: namespace_name)
+ existing_namespace = Namespace.by_path(namespace_name)
+ if existing_namespace && existing_namespace != self.namespace
self.errors.add :username, "already exists"
end
end
@@ -279,11 +310,15 @@ class User < ActiveRecord::Base
self.errors.add(:email, 'has already been taken') if Email.exists?(email: self.email)
end
+ def owns_notification_email
+ self.errors.add(:notification_email, "is not an email you own") unless self.all_emails.include?(self.notification_email)
+ end
+
# Groups user has access to
def authorized_groups
@authorized_groups ||= begin
group_ids = (groups.pluck(:id) + authorized_projects.pluck(:namespace_id))
- Group.where(id: group_ids).order('namespaces.name ASC')
+ Group.where(id: group_ids)
end
end
@@ -292,9 +327,9 @@ class User < ActiveRecord::Base
def authorized_projects
@authorized_projects ||= begin
project_ids = personal_projects.pluck(:id)
- project_ids += groups_projects.pluck(:id)
- project_ids += projects.pluck(:id).uniq
- Project.where(id: project_ids).joins(:namespace).order('namespaces.name ASC')
+ project_ids.push(*groups_projects.pluck(:id))
+ project_ids.push(*projects.pluck(:id).uniq)
+ Project.where(id: project_ids)
end
end
@@ -317,6 +352,10 @@ class User < ActiveRecord::Base
keys.count == 0
end
+ def require_password?
+ password_automatically_set? && !ldap_user?
+ end
+
def can_change_username?
gitlab_config.username_changing_enabled
end
@@ -402,7 +441,11 @@ class User < ActiveRecord::Base
end
def ldap_user?
- extern_uid && provider.start_with?('ldap')
+ identities.exists?(["provider LIKE ? AND extern_uid IS NOT NULL", "ldap%"])
+ end
+
+ def ldap_identity
+ @ldap_identity ||= identities.find_by(["provider LIKE ?", "ldap%"])
end
def accessible_deploy_keys
@@ -420,6 +463,19 @@ class User < ActiveRecord::Base
end
end
+ def set_notification_email
+ if self.notification_email.blank? || !self.all_emails.include?(self.notification_email)
+ self.notification_email = self.email
+ end
+ end
+
+ def set_projects_limit
+ connection_default_value_defined = new_record? && !projects_limit_changed?
+ return unless self.projects_limit.nil? || connection_default_value_defined
+
+ self.projects_limit = current_application_settings.default_projects_limit
+ end
+
def requires_ldap_check?
if !Gitlab.config.ldap.enabled
false
@@ -478,7 +534,7 @@ class User < ActiveRecord::Base
end
def temp_oauth_email?
- email =~ /\Atemp-email-for-oauth/
+ email.start_with?('temp-email-for-oauth')
end
def public_profile?
@@ -487,12 +543,24 @@ class User < ActiveRecord::Base
def avatar_url(size = nil)
if avatar.present?
- [gitlab_config.url, avatar.url].join("/")
+ [gitlab_config.url, avatar.url].join
else
GravatarService.new.execute(email, size)
end
end
+ def all_emails
+ [self.email, *self.emails.map(&:email)]
+ end
+
+ def hook_attrs
+ {
+ name: name,
+ username: username,
+ avatar_url: avatar_url
+ }
+ end
+
def ensure_namespace_correct
# Ensure user has namespace
self.create_namespace!(path: self.username, name: self.username) unless self.namespace
@@ -504,7 +572,7 @@ class User < ActiveRecord::Base
def post_create_hook
log_info("User \"#{self.name}\" (#{self.email}) was created")
- notification_service.new_user(self, @reset_token)
+ notification_service.new_user(self, @reset_token) if self.created_by_id
system_hook_service.execute_hooks_for(self, :create)
end
@@ -538,4 +606,29 @@ class User < ActiveRecord::Base
UsersStarProject.create!(project: project, user: self)
end
end
+
+ def manageable_namespaces
+ @manageable_namespaces ||=
+ begin
+ namespaces = []
+ namespaces << namespace
+ namespaces += owned_groups
+ namespaces += masters_groups
+ end
+ end
+
+ def oauth_authorized_tokens
+ Doorkeeper::AccessToken.where(resource_owner_id: self.id, revoked_at: nil)
+ end
+
+ def contributed_projects_ids
+ Event.where(author_id: self).
+ where("created_at > ?", Time.now - 1.year).
+ where("action = :pushed OR (target_type = 'MergeRequest' AND action = :created)",
+ pushed: Event::PUSHED, created: Event::CREATED).
+ reorder(project_id: :desc).
+ select(:project_id).
+ uniq
+ .map(&:project_id)
+ end
end
diff --git a/app/models/wiki_page.rb b/app/models/wiki_page.rb
index b9ab6702c53..32981a0e664 100644
--- a/app/models/wiki_page.rb
+++ b/app/models/wiki_page.rb
@@ -43,7 +43,7 @@ class WikiPage
@attributes[:slug]
end
- alias :to_param :slug
+ alias_method :to_param, :slug
# The formatted title of this page.
def title
diff --git a/app/services/base_service.rb b/app/services/base_service.rb
index 0d46eeaa18f..52ab29f1492 100644
--- a/app/services/base_service.rb
+++ b/app/services/base_service.rb
@@ -1,4 +1,6 @@
class BaseService
+ include Gitlab::CurrentSettings
+
attr_accessor :project, :current_user, :params
def initialize(project, user, params = {})
@@ -29,13 +31,20 @@ class BaseService
SystemHooksService.new
end
+ def current_application_settings
+ ApplicationSetting.current
+ end
+
private
- def error(message)
- {
+ def error(message, http_status = nil)
+ result = {
message: message,
status: :error
}
+
+ result[:http_status] = http_status if http_status
+ result
end
def success
diff --git a/app/services/create_branch_service.rb b/app/services/create_branch_service.rb
index 901f67bafb3..5e971c7891c 100644
--- a/app/services/create_branch_service.rb
+++ b/app/services/create_branch_service.rb
@@ -17,7 +17,7 @@ class CreateBranchService < BaseService
new_branch = repository.find_branch(branch_name)
if new_branch
- Event.create_ref_event(project, current_user, new_branch, 'add')
+ EventCreateService.new.push_ref(project, current_user, new_branch, 'add')
return success(new_branch)
else
return error('Invalid reference name')
diff --git a/app/services/create_tag_service.rb b/app/services/create_tag_service.rb
index 9b2a2270233..a735d3f7f20 100644
--- a/app/services/create_tag_service.rb
+++ b/app/services/create_tag_service.rb
@@ -21,10 +21,15 @@ class CreateTagService < BaseService
new_tag = repository.find_tag(tag_name)
if new_tag
- Event.create_ref_event(project, current_user, new_tag, 'add', 'refs/tags')
- return success(new_tag)
+ if project.gitlab_ci?
+ push_data = create_push_data(project, current_user, new_tag)
+ project.gitlab_ci_service.async_execute(push_data)
+ end
+
+ EventCreateService.new.push_ref(project, current_user, new_tag, 'add', 'refs/tags')
+ success(new_tag)
else
- return error('Invalid reference name')
+ error('Invalid reference name')
end
end
@@ -33,4 +38,9 @@ class CreateTagService < BaseService
out[:tag] = branch
out
end
+
+ def create_push_data(project, user, tag)
+ Gitlab::PushDataBuilder.
+ build(project, user, Gitlab::Git::BLANK_SHA, tag.target, 'refs/tags/' + tag.name, [])
+ end
end
diff --git a/app/services/delete_branch_service.rb b/app/services/delete_branch_service.rb
index cae6327fe72..c26aee2b0aa 100644
--- a/app/services/delete_branch_service.rb
+++ b/app/services/delete_branch_service.rb
@@ -25,7 +25,7 @@ class DeleteBranchService < BaseService
end
if repository.rm_branch(branch_name)
- Event.create_ref_event(project, current_user, branch, 'rm')
+ EventCreateService.new.push_ref(project, current_user, branch, 'rm')
success('Branch was removed')
else
return error('Failed to remove branch')
diff --git a/app/services/event_create_service.rb b/app/services/event_create_service.rb
index 8d8a5873e62..ba9547b9242 100644
--- a/app/services/event_create_service.rb
+++ b/app/services/event_create_service.rb
@@ -7,58 +7,98 @@
#
class EventCreateService
def open_issue(issue, current_user)
- create_event(issue, current_user, Event::CREATED)
+ create_record_event(issue, current_user, Event::CREATED)
end
def close_issue(issue, current_user)
- create_event(issue, current_user, Event::CLOSED)
+ create_record_event(issue, current_user, Event::CLOSED)
end
def reopen_issue(issue, current_user)
- create_event(issue, current_user, Event::REOPENED)
+ create_record_event(issue, current_user, Event::REOPENED)
end
def open_mr(merge_request, current_user)
- create_event(merge_request, current_user, Event::CREATED)
+ create_record_event(merge_request, current_user, Event::CREATED)
end
def close_mr(merge_request, current_user)
- create_event(merge_request, current_user, Event::CLOSED)
+ create_record_event(merge_request, current_user, Event::CLOSED)
end
def reopen_mr(merge_request, current_user)
- create_event(merge_request, current_user, Event::REOPENED)
+ create_record_event(merge_request, current_user, Event::REOPENED)
end
def merge_mr(merge_request, current_user)
- create_event(merge_request, current_user, Event::MERGED)
+ create_record_event(merge_request, current_user, Event::MERGED)
end
def open_milestone(milestone, current_user)
- create_event(milestone, current_user, Event::CREATED)
+ create_record_event(milestone, current_user, Event::CREATED)
end
def close_milestone(milestone, current_user)
- create_event(milestone, current_user, Event::CLOSED)
+ create_record_event(milestone, current_user, Event::CLOSED)
end
def reopen_milestone(milestone, current_user)
- create_event(milestone, current_user, Event::REOPENED)
+ create_record_event(milestone, current_user, Event::REOPENED)
end
def leave_note(note, current_user)
- create_event(note, current_user, Event::COMMENTED)
+ create_record_event(note, current_user, Event::COMMENTED)
+ end
+
+ def join_project(project, current_user)
+ create_event(project, current_user, Event::JOINED)
+ end
+
+ def leave_project(project, current_user)
+ create_event(project, current_user, Event::LEFT)
+ end
+
+ def create_project(project, current_user)
+ create_event(project, current_user, Event::CREATED)
+ end
+
+ def push_ref(project, current_user, ref, action = 'add', prefix = 'refs/heads')
+ commit = project.repository.commit(ref.target)
+
+ if action.to_s == 'add'
+ before = '00000000'
+ after = commit.id
+ else
+ before = commit.id
+ after = '00000000'
+ end
+
+ data = {
+ ref: "#{prefix}/#{ref.name}",
+ before: before,
+ after: after
+ }
+
+ push(project, current_user, data)
+ end
+
+ def push(project, current_user, push_data)
+ create_event(project, current_user, Event::PUSHED, data: push_data)
end
private
- def create_event(record, current_user, status)
- Event.create(
- project: record.project,
- target_id: record.id,
- target_type: record.class.name,
+ def create_record_event(record, current_user, status)
+ create_event(record.project, current_user, status, target_id: record.id, target_type: record.class.name)
+ end
+
+ def create_event(project, current_user, status, attributes = {})
+ attributes.reverse_merge!(
+ project: project,
action: status,
author_id: current_user.id
)
+
+ Event.create(attributes)
end
end
diff --git a/app/services/files/create_service.rb b/app/services/files/create_service.rb
index 82e4d7b684f..de5322e990a 100644
--- a/app/services/files/create_service.rb
+++ b/app/services/files/create_service.rb
@@ -3,20 +3,12 @@ require_relative "base_service"
module Files
class CreateService < BaseService
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
+ allowed = Gitlab::GitAccess.can_push_to_branch?(current_user, project, ref)
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
@@ -27,17 +19,27 @@ module Files
)
end
- blob = repository.blob_at_branch(ref, file_path)
+ if project.empty_repo?
+ # everything is ok because repo does not have a commits yet
+ else
+ unless repository.branch_names.include?(ref)
+ return error("You can only create files if you are on top of a branch")
+ end
- if blob
- return error("Your changes could not be committed, because file with such name exists")
+ blob = repository.blob_at_branch(ref, file_path)
+
+ if blob
+ return error("Your changes could not be committed, because file with such name exists")
+ end
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],
- params[:encoding]
+ params[:encoding],
+ params[:new_branch]
)
if created_successfully
diff --git a/app/services/files/delete_service.rb b/app/services/files/delete_service.rb
index ff5dc6ef34c..8e73c2e2727 100644
--- a/app/services/files/delete_service.rb
+++ b/app/services/files/delete_service.rb
@@ -3,11 +3,7 @@ require_relative "base_service"
module Files
class DeleteService < BaseService
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
+ allowed = ::Gitlab::GitAccess.can_push_to_branch?(current_user, project, ref)
unless allowed
return error("You are not allowed to push into this branch")
diff --git a/app/services/files/update_service.rb b/app/services/files/update_service.rb
index a0f40154db0..328cf3a4b06 100644
--- a/app/services/files/update_service.rb
+++ b/app/services/files/update_service.rb
@@ -3,11 +3,7 @@ require_relative "base_service"
module Files
class UpdateService < BaseService
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
+ allowed = ::Gitlab::GitAccess.can_push_to_branch?(current_user, project, ref)
unless allowed
return error("You are not allowed to push into this branch")
@@ -24,17 +20,20 @@ module Files
end
edit_file_action = Gitlab::Satellite::EditFileAction.new(current_user, project, ref, path)
- created_successfully = edit_file_action.commit!(
+ edit_file_action.commit!(
params[:content],
params[:commit_message],
- params[:encoding]
+ params[:encoding],
+ params[:new_branch]
)
- if created_successfully
- success
- else
- error("Your changes could not be committed. Maybe the file was changed by another process or there was nothing to commit?")
- end
+ success
+ rescue Gitlab::Satellite::CheckoutFailed => ex
+ error("Your changes could not be committed because ref '#{ref}' could not be checked out", 400)
+ rescue Gitlab::Satellite::CommitFailed => ex
+ error("Your changes could not be committed. Maybe there was nothing to commit?", 409)
+ rescue Gitlab::Satellite::PushFailed => ex
+ error("Your changes could not be committed. Maybe the file was changed by another process?", 409)
end
end
end
diff --git a/app/services/git_push_service.rb b/app/services/git_push_service.rb
index 8f2b0e347f6..f21e6ac207d 100644
--- a/app/services/git_push_service.rb
+++ b/app/services/git_push_service.rb
@@ -1,5 +1,7 @@
class GitPushService
attr_accessor :project, :user, :push_data, :push_commits
+ include Gitlab::CurrentSettings
+ include Gitlab::Access
# This method will be called after each git update
# and only if the provided user and project is present in GitLab.
@@ -29,8 +31,12 @@ class GitPushService
if is_default_branch?(ref)
# Initial push to the default branch. Take the full history of that branch as "newly pushed".
@push_commits = project.repository.commits(newrev)
- # Default branch is protected by default
- project.protected_branches.create({ name: project.default_branch })
+
+ # Set protection on the default branch if configured
+ if (current_application_settings.default_branch_protection != PROTECTION_NONE)
+ developers_can_push = current_application_settings.default_branch_protection == PROTECTION_DEV_CAN_PUSH ? true : false
+ project.protected_branches.create({ name: project.default_branch, developers_can_push: developers_can_push })
+ end
else
# Use the pushed commits that aren't reachable by the default branch
# as a heuristic. This may include more commits than are actually pushed, but
@@ -46,33 +52,14 @@ class GitPushService
end
@push_data = post_receive_data(oldrev, newrev, ref)
- create_push_event(@push_data)
+ EventCreateService.new.push(project, user, @push_data)
project.execute_hooks(@push_data.dup, :push_hooks)
project.execute_services(@push_data.dup)
end
end
- # This method provide a sample data
- # generated with post_receive_data method
- # for given project
- #
- def sample_data(project, user)
- @project, @user = project, user
- @push_commits = project.repository.commits(project.default_branch, nil, 3)
- post_receive_data(@push_commits.last.id, @push_commits.first.id, "refs/heads/#{project.default_branch}")
- end
-
protected
- def create_push_event(push_data)
- Event.create!(
- project: project,
- action: Event::PUSHED,
- data: push_data,
- author_id: push_data[:user_id]
- )
- end
-
# Extract any GFM references from the pushed commit messages. If the configured issue-closing regex is matched,
# close the referenced Issue. Create cross-reference Notes corresponding to any other referenced Mentionables.
def process_commit_messages(ref)
@@ -83,9 +70,14 @@ class GitPushService
# closing regex. Exclude any mentioned Issues from cross-referencing even if the commits are being pushed to
# a different branch.
issues_to_close = commit.closes_issues(project)
- author = commit_user(commit)
- if !issues_to_close.empty? && is_default_branch
+ # Load commit author only if needed.
+ # For push with 1k commits it prevents 900+ requests in database
+ author = nil
+
+ if issues_to_close.present? && is_default_branch
+ author ||= commit_user(commit)
+
issues_to_close.each do |issue|
Issues::CloseService.new(project, author, {}).execute(issue, commit)
end
@@ -96,87 +88,43 @@ class GitPushService
# being pushed to a different branch).
refs = commit.references(project) - issues_to_close
refs.reject! { |r| commit.has_mentioned?(r) }
- refs.each do |r|
- Note.create_cross_reference_note(r, commit, author, project)
+
+ if refs.present?
+ author ||= commit_user(commit)
+
+ refs.each do |r|
+ Note.create_cross_reference_note(r, commit, author, project)
+ end
end
end
end
- # Produce a hash of post-receive data
- #
- # data = {
- # before: String,
- # after: String,
- # ref: String,
- # user_id: String,
- # user_name: String,
- # project_id: String,
- # repository: {
- # name: String,
- # url: String,
- # description: String,
- # homepage: String,
- # },
- # commits: Array,
- # total_commits_count: Fixnum
- # }
- #
def post_receive_data(oldrev, newrev, ref)
- # Total commits count
- push_commits_count = push_commits.size
-
- # Get latest 20 commits ASC
- push_commits_limited = push_commits.last(20)
-
- # Hash to be passed as post_receive_data
- data = {
- before: oldrev,
- after: newrev,
- ref: ref,
- user_id: user.id,
- user_name: user.name,
- project_id: project.id,
- repository: {
- name: project.name,
- url: project.url_to_repo,
- description: project.description,
- homepage: project.web_url,
- },
- commits: [],
- total_commits_count: push_commits_count
- }
-
- # For performance purposes maximum 20 latest commits
- # will be passed as post receive hook data.
- #
- push_commits_limited.each do |commit|
- data[:commits] << commit.hook_attrs(project)
- end
-
- data
+ Gitlab::PushDataBuilder.
+ build(project, user, oldrev, newrev, ref, push_commits)
end
def push_to_existing_branch?(ref, oldrev)
ref_parts = ref.split('/')
# Return if this is not a push to a branch (e.g. new commits)
- ref_parts[1] =~ /heads/ && oldrev != "0000000000000000000000000000000000000000"
+ ref_parts[1].include?('heads') && oldrev != Gitlab::Git::BLANK_SHA
end
def push_to_new_branch?(ref, oldrev)
ref_parts = ref.split('/')
- ref_parts[1] =~ /heads/ && oldrev == "0000000000000000000000000000000000000000"
+ ref_parts[1].include?('heads') && oldrev == Gitlab::Git::BLANK_SHA
end
def push_remove_branch?(ref, newrev)
ref_parts = ref.split('/')
- ref_parts[1] =~ /heads/ && newrev == "0000000000000000000000000000000000000000"
+ ref_parts[1].include?('heads') && newrev == Gitlab::Git::BLANK_SHA
end
def push_to_branch?(ref)
- ref =~ /refs\/heads/
+ ref.include?('refs/heads')
end
def is_default_branch?(ref)
diff --git a/app/services/git_tag_push_service.rb b/app/services/git_tag_push_service.rb
index 62eaf9b4f51..46d8987f12d 100644
--- a/app/services/git_tag_push_service.rb
+++ b/app/services/git_tag_push_service.rb
@@ -5,36 +5,21 @@ class GitTagPushService
@project, @user = project, user
@push_data = create_push_data(oldrev, newrev, ref)
- create_push_event
+ EventCreateService.new.push(project, user, @push_data)
project.repository.expire_cache
project.execute_hooks(@push_data.dup, :tag_push_hooks)
+
+ if project.gitlab_ci?
+ project.gitlab_ci_service.async_execute(@push_data)
+ end
+
+ true
end
private
def create_push_data(oldrev, newrev, ref)
- data = {
- ref: ref,
- before: oldrev,
- after: newrev,
- user_id: user.id,
- user_name: user.name,
- project_id: project.id,
- repository: {
- name: project.name,
- url: project.url_to_repo,
- description: project.description,
- homepage: project.web_url
- }
- }
- end
-
- def create_push_event
- Event.create!(
- project: project,
- action: Event::PUSHED,
- data: push_data,
- author_id: push_data[:user_id]
- )
+ Gitlab::PushDataBuilder.
+ build(project, user, oldrev, newrev, ref, [])
end
end
diff --git a/app/services/gravatar_service.rb b/app/services/gravatar_service.rb
index a69c7c78377..4bee0c26a68 100644
--- a/app/services/gravatar_service.rb
+++ b/app/services/gravatar_service.rb
@@ -1,6 +1,8 @@
class GravatarService
+ include Gitlab::CurrentSettings
+
def execute(email, size = nil)
- if gravatar_config.enabled && email.present?
+ if current_application_settings.gravatar_enabled? && email.present?
size = 40 if size.nil? || size <= 0
sprintf gravatar_url,
diff --git a/app/services/issuable_base_service.rb b/app/services/issuable_base_service.rb
index e3371ec3c1b..5e1906ad2ae 100644
--- a/app/services/issuable_base_service.rb
+++ b/app/services/issuable_base_service.rb
@@ -10,4 +10,9 @@ class IssuableBaseService < BaseService
Note.create_milestone_change_note(
issuable, issuable.project, current_user, issuable.milestone)
end
+
+ def create_labels_note(issuable, added_labels, removed_labels)
+ Note.create_labels_change_note(
+ issuable, issuable.project, current_user, added_labels, removed_labels)
+ end
end
diff --git a/app/services/issues/base_service.rb b/app/services/issues/base_service.rb
index 41948f226a6..755c0ef45a8 100644
--- a/app/services/issues/base_service.rb
+++ b/app/services/issues/base_service.rb
@@ -4,7 +4,7 @@ module Issues
private
def execute_hooks(issue, action = 'open')
- issue_data = issue.to_hook_data
+ issue_data = issue.to_hook_data(current_user)
issue_url = Gitlab::UrlBuilder.new(:issue).build(issue.id)
issue_data[:object_attributes].merge!(url: issue_url, action: action)
issue.project.execute_hooks(issue_data, :issue_hooks)
diff --git a/app/services/issues/close_service.rb b/app/services/issues/close_service.rb
index ffed13a12e1..f670019cc63 100644
--- a/app/services/issues/close_service.rb
+++ b/app/services/issues/close_service.rb
@@ -2,9 +2,9 @@ module Issues
class CloseService < Issues::BaseService
def execute(issue, commit = nil)
if issue.close
- notification_service.close_issue(issue, current_user)
event_service.close_issue(issue, current_user)
create_note(issue, commit)
+ notification_service.close_issue(issue, current_user)
execute_hooks(issue, 'close')
end
diff --git a/app/services/issues/update_service.rb b/app/services/issues/update_service.rb
index 0ee9635ed99..c61d67a7893 100644
--- a/app/services/issues/update_service.rb
+++ b/app/services/issues/update_service.rb
@@ -14,17 +14,24 @@ module Issues
issue.update_nth_task(params[:task_num].to_i, false)
end
+ old_labels = issue.labels.to_a
+
if params.present? && issue.update_attributes(params.except(:state_event,
:task_num))
issue.reset_events_cache
+ if issue.labels != old_labels
+ create_labels_note(
+ issue, issue.labels - old_labels, old_labels - issue.labels)
+ end
+
if issue.previous_changes.include?('milestone_id')
create_milestone_note(issue)
end
if issue.previous_changes.include?('assignee_id')
- notification_service.reassigned_issue(issue, current_user)
create_assignee_note(issue)
+ notification_service.reassigned_issue(issue, current_user)
end
issue.notice_added_references(issue.project, current_user)
diff --git a/app/services/merge_requests/auto_merge_service.rb b/app/services/merge_requests/auto_merge_service.rb
index 20b88d1510c..378b39bb9d6 100644
--- a/app/services/merge_requests/auto_merge_service.rb
+++ b/app/services/merge_requests/auto_merge_service.rb
@@ -5,15 +5,16 @@ module MergeRequests
# mark merge request as merged and execute all hooks and notifications
# Called when you do merge via GitLab UI
class AutoMergeService < BaseMergeService
- def execute(merge_request, current_user, commit_message)
+ def execute(merge_request, commit_message)
merge_request.lock_mr
if Gitlab::Satellite::MergeAction.new(current_user, merge_request).merge!(commit_message)
merge_request.merge
- notification.merge_mr(merge_request, current_user)
create_merge_event(merge_request, current_user)
- execute_project_hooks(merge_request)
+ create_note(merge_request)
+ notification_service.merge_mr(merge_request, current_user)
+ execute_hooks(merge_request)
true
else
diff --git a/app/services/merge_requests/base_merge_service.rb b/app/services/merge_requests/base_merge_service.rb
index 9bc50d3d16c..9579573adf9 100644
--- a/app/services/merge_requests/base_merge_service.rb
+++ b/app/services/merge_requests/base_merge_service.rb
@@ -1,20 +1,10 @@
module MergeRequests
- class BaseMergeService
+ class BaseMergeService < MergeRequests::BaseService
private
- def notification
- NotificationService.new
- end
-
def create_merge_event(merge_request, current_user)
EventCreateService.new.merge_mr(merge_request, current_user)
end
-
- def execute_project_hooks(merge_request)
- if merge_request.project
- merge_request.project.execute_hooks(merge_request.to_hook_data, :merge_request_hooks)
- end
- end
end
end
diff --git a/app/services/merge_requests/base_service.rb b/app/services/merge_requests/base_service.rb
index 694994001b0..b4199d1c800 100644
--- a/app/services/merge_requests/base_service.rb
+++ b/app/services/merge_requests/base_service.rb
@@ -5,9 +5,13 @@ module MergeRequests
Note.create_status_change_note(merge_request, merge_request.target_project, current_user, merge_request.state, nil)
end
- def execute_hooks(merge_request)
+ def execute_hooks(merge_request, action = 'open')
if merge_request.project
- merge_request.project.execute_hooks(merge_request.to_hook_data, :merge_request_hooks)
+ hook_data = merge_request.to_hook_data(current_user)
+ merge_request_url = Gitlab::UrlBuilder.new(:merge_request).build(merge_request.id)
+ hook_data[:object_attributes][:url] = merge_request_url
+ hook_data[:object_attributes][:action] = action
+ merge_request.project.execute_hooks(hook_data, :merge_request_hooks)
end
end
end
diff --git a/app/services/merge_requests/build_service.rb b/app/services/merge_requests/build_service.rb
index 1475973e543..a44b91166e8 100644
--- a/app/services/merge_requests/build_service.rb
+++ b/app/services/merge_requests/build_service.rb
@@ -13,12 +13,9 @@ module MergeRequests
merge_request.target_branch ||= merge_request.target_project.default_branch
unless merge_request.target_branch && merge_request.source_branch
- return build_failed(merge_request, "You must select source and target branches")
+ return build_failed(merge_request, nil)
end
- # Generate suggested MR title based on source branch name
- merge_request.title = merge_request.source_branch.titleize.humanize
-
compare_result = CompareService.new.execute(
current_user,
merge_request.source_project,
@@ -52,6 +49,15 @@ module MergeRequests
merge_request.compare_failed = false
end
+ commits = merge_request.compare_commits
+ if commits && commits.count == 1
+ commit = commits.first
+ merge_request.title = commit.title
+ merge_request.description = commit.description.try(:strip)
+ else
+ merge_request.title = merge_request.source_branch.titleize.humanize
+ end
+
merge_request
rescue Gitlab::Satellite::BranchesWithoutParent
@@ -59,7 +65,7 @@ module MergeRequests
end
def build_failed(merge_request, message)
- merge_request.errors.add(:base, message)
+ merge_request.errors.add(:base, message) unless message.nil?
merge_request.compare_commits = []
merge_request.can_be_created = false
merge_request
diff --git a/app/services/merge_requests/close_service.rb b/app/services/merge_requests/close_service.rb
index 64e37a23e6b..47454f9f0c2 100644
--- a/app/services/merge_requests/close_service.rb
+++ b/app/services/merge_requests/close_service.rb
@@ -7,9 +7,9 @@ module MergeRequests
if merge_request.close
event_service.close_mr(merge_request, current_user)
- notification_service.close_mr(merge_request, current_user)
create_note(merge_request)
- execute_hooks(merge_request)
+ notification_service.close_mr(merge_request, current_user)
+ execute_hooks(merge_request, 'close')
end
merge_request
diff --git a/app/services/merge_requests/merge_service.rb b/app/services/merge_requests/merge_service.rb
index 680766140bd..327ead4ff3f 100644
--- a/app/services/merge_requests/merge_service.rb
+++ b/app/services/merge_requests/merge_service.rb
@@ -6,12 +6,13 @@ module MergeRequests
# Called when you do merge via command line and push code
# to target branch
class MergeService < BaseMergeService
- def execute(merge_request, current_user, commit_message)
+ def execute(merge_request, commit_message)
merge_request.merge
- notification.merge_mr(merge_request, current_user)
create_merge_event(merge_request, current_user)
- execute_project_hooks(merge_request)
+ create_note(merge_request)
+ notification_service.merge_mr(merge_request, current_user)
+ execute_hooks(merge_request, 'merge')
true
rescue
diff --git a/app/services/merge_requests/refresh_service.rb b/app/services/merge_requests/refresh_service.rb
new file mode 100644
index 00000000000..96761bec99f
--- /dev/null
+++ b/app/services/merge_requests/refresh_service.rb
@@ -0,0 +1,94 @@
+module MergeRequests
+ class RefreshService < MergeRequests::BaseService
+ def execute(oldrev, newrev, ref)
+ return true unless ref =~ /heads/
+
+ @oldrev, @newrev = oldrev, newrev
+ @branch_name = ref.gsub("refs/heads/", "")
+ @fork_merge_requests = @project.fork_merge_requests.opened
+ @commits = @project.repository.commits_between(oldrev, newrev)
+
+ close_merge_requests
+ reload_merge_requests
+ comment_mr_with_commits
+
+ true
+ end
+
+ private
+
+ # Collect open merge requests that target same branch we push into
+ # and close if push to master include last commit from merge request
+ # We need this to close(as merged) merge requests that were merged into
+ # target branch manually
+ def close_merge_requests
+ commit_ids = @commits.map(&:id)
+ merge_requests = @project.merge_requests.opened.where(target_branch: @branch_name).to_a
+ merge_requests = merge_requests.select(&:last_commit)
+
+ merge_requests = merge_requests.select do |merge_request|
+ commit_ids.include?(merge_request.last_commit.id)
+ end
+
+
+ merge_requests.uniq.select(&:source_project).each do |merge_request|
+ MergeRequests::MergeService.
+ new(merge_request.target_project, @current_user).
+ execute(merge_request, nil)
+ end
+ end
+
+ def force_push?
+ Gitlab::ForcePushCheck.force_push?(@project, @oldrev, @newrev)
+ end
+
+ # Refresh merge request diff if we push to source or target branch of merge request
+ # Note: we should update merge requests from forks too
+ def reload_merge_requests
+ merge_requests = @project.merge_requests.opened.by_branch(@branch_name).to_a
+ merge_requests += @fork_merge_requests.by_branch(@branch_name).to_a
+ merge_requests = filter_merge_requests(merge_requests)
+
+ merge_requests.each do |merge_request|
+
+ if merge_request.source_branch == @branch_name || force_push?
+ merge_request.reload_code
+ update_merge_request(merge_request)
+ else
+ mr_commit_ids = merge_request.commits.map(&:id)
+ push_commit_ids = @commits.map(&:id)
+ matches = mr_commit_ids & push_commit_ids
+
+ if matches.any?
+ merge_request.reload_code
+ update_merge_request(merge_request)
+ else
+ update_merge_request(merge_request)
+ end
+ end
+ end
+ end
+
+ def update_merge_request(merge_request)
+ MergeRequests::UpdateService.new(
+ merge_request.target_project,
+ @current_user, merge_status: 'unchecked').execute(merge_request)
+ end
+
+ # Add comment about pushing new commits to merge requests
+ def comment_mr_with_commits
+ merge_requests = @project.origin_merge_requests.opened.where(source_branch: @branch_name).to_a
+ merge_requests += @fork_merge_requests.where(source_branch: @branch_name).to_a
+ merge_requests = filter_merge_requests(merge_requests)
+
+ merge_requests.each do |merge_request|
+ Note.create_new_commits_note(merge_request, merge_request.project,
+ @current_user, @commits)
+ end
+ end
+
+ def filter_merge_requests(merge_requests)
+ merge_requests.uniq.select(&:source_project)
+ end
+ end
+end
diff --git a/app/services/merge_requests/reopen_service.rb b/app/services/merge_requests/reopen_service.rb
index bd68919a550..8279ad2001b 100644
--- a/app/services/merge_requests/reopen_service.rb
+++ b/app/services/merge_requests/reopen_service.rb
@@ -3,9 +3,9 @@ module MergeRequests
def execute(merge_request)
if merge_request.reopen
event_service.reopen_mr(merge_request, current_user)
- notification_service.reopen_mr(merge_request, current_user)
create_note(merge_request)
- execute_hooks(merge_request)
+ notification_service.reopen_mr(merge_request, current_user)
+ execute_hooks(merge_request, 'reopen')
merge_request.reload_code
merge_request.mark_as_unchecked
end
diff --git a/app/services/merge_requests/update_service.rb b/app/services/merge_requests/update_service.rb
index fc26619cd17..870b50bb60d 100644
--- a/app/services/merge_requests/update_service.rb
+++ b/app/services/merge_requests/update_service.rb
@@ -23,22 +23,32 @@ module MergeRequests
merge_request.update_nth_task(params[:task_num].to_i, false)
end
+ old_labels = merge_request.labels.to_a
+
if params.present? && merge_request.update_attributes(
params.except(:state_event, :task_num)
)
merge_request.reset_events_cache
+ if merge_request.labels != old_labels
+ create_labels_note(
+ merge_request,
+ merge_request.labels - old_labels,
+ old_labels - merge_request.labels
+ )
+ end
+
if merge_request.previous_changes.include?('milestone_id')
create_milestone_note(merge_request)
end
if merge_request.previous_changes.include?('assignee_id')
- notification_service.reassigned_merge_request(merge_request, current_user)
create_assignee_note(merge_request)
+ notification_service.reassigned_merge_request(merge_request, current_user)
end
merge_request.notice_added_references(merge_request.project, current_user)
- execute_hooks(merge_request)
+ execute_hooks(merge_request, 'update')
end
merge_request
diff --git a/app/services/notes/update_service.rb b/app/services/notes/update_service.rb
new file mode 100644
index 00000000000..63431b82471
--- /dev/null
+++ b/app/services/notes/update_service.rb
@@ -0,0 +1,25 @@
+module Notes
+ class UpdateService < BaseService
+ def execute
+ note = project.notes.find(params[:note_id])
+ note.note = params[:note]
+ if note.save
+ notification_service.new_note(note)
+
+ # Skip system notes, like status changes and cross-references.
+ unless note.system
+ event_service.leave_note(note, note.author)
+
+ # Create a cross-reference note if this Note contains GFM that
+ # names an issue, merge request, or commit.
+ note.references.each do |mentioned|
+ Note.create_cross_reference_note(mentioned, note.noteable,
+ note.author, note.project)
+ end
+ end
+ end
+
+ note
+ end
+ end
+end
diff --git a/app/services/notification_service.rb b/app/services/notification_service.rb
index c9a1574b84e..2fc63b9f4b7 100644
--- a/app/services/notification_service.rb
+++ b/app/services/notification_service.rb
@@ -107,7 +107,7 @@ class NotificationService
# Notify new user with email after creation
def new_user(user, token = nil)
# Don't email omniauth created users
- mailer.new_user_email(user.id, user.password, token) unless user.extern_uid?
+ mailer.new_user_email(user.id, token) unless user.identities.any?
end
# Notify users on new note in system
@@ -118,7 +118,7 @@ class NotificationService
return true unless note.noteable_type.present?
# ignore gitlab service messages
- return true if note.note =~ /\A_Status changed to closed_/
+ return true if note.note.start_with?('_Status changed to closed_')
return true if note.cross_reference? && note.system == true
opts = { noteable_type: note.noteable_type, project_id: note.project_id }
@@ -144,6 +144,10 @@ class NotificationService
# Merge project watchers
recipients = recipients.concat(project_watchers(note.project)).compact.uniq
+ # Reject mention users unless mentioned in comment
+ recipients = reject_mention_users(recipients - note.mentioned_users, note.project)
+ recipients = recipients + note.mentioned_users
+
# Reject mutes users
recipients = reject_muted_users(recipients, note.project)
@@ -238,7 +242,7 @@ class NotificationService
users
end
- # Build a list of users based on group notifcation settings
+ # Build a list of users based on group notification settings
def select_users_group_setting(project, project_members, global_setting, users_global_level_watch)
uids = users_group_notification(project, Notification::N_WATCH)
@@ -285,14 +289,32 @@ class NotificationService
end
end
- def new_resource_email(target, project, method)
- if target.respond_to?(:participants)
- recipients = target.participants
- else
- recipients = []
+ # Remove users with notification level 'Mentioned'
+ def reject_mention_users(users, project = nil)
+ users = users.to_a.compact.uniq
+
+ users.reject do |user|
+ next user.notification.mention? unless project
+
+ tm = project.project_members.find_by(user_id: user.id)
+
+ if !tm && project.group
+ tm = project.group.group_members.find_by(user_id: user.id)
+ end
+
+ # reject users who globally set mention notification and has no membership
+ next user.notification.mention? unless tm
+
+ # reject users who set mention notification in project
+ next true if tm.notification.mention?
+
+ # reject users who have N_MENTION in project and disabled in global settings
+ tm.notification.global? && user.notification.mention?
end
- recipients = reject_muted_users(recipients, project)
- recipients = recipients.concat(project_watchers(project)).uniq
+ end
+
+ def new_resource_email(target, project, method)
+ recipients = build_recipients(target, project)
recipients.delete(target.author)
recipients.each do |recipient|
@@ -301,8 +323,7 @@ class NotificationService
end
def close_resource_email(target, project, current_user, method)
- recipients = reject_muted_users([target.author, target.assignee], project)
- recipients = recipients.concat(project_watchers(project)).uniq
+ recipients = build_recipients(target, project)
recipients.delete(current_user)
recipients.each do |recipient|
@@ -312,16 +333,7 @@ class NotificationService
def reassign_resource_email(target, project, current_user, method)
assignee_id_was = previous_record(target, "assignee_id")
-
- recipients = User.where(id: [target.assignee_id, assignee_id_was])
-
- # Add watchers to email list
- recipients = recipients.concat(project_watchers(project))
-
- # reject users with disabled notifications
- recipients = reject_muted_users(recipients, project)
-
- # Reject me from recipients if I reassign an item
+ recipients = build_recipients(target, project)
recipients.delete(current_user)
recipients.each do |recipient|
@@ -330,8 +342,7 @@ class NotificationService
end
def reopen_resource_email(target, project, current_user, method, status)
- recipients = reject_muted_users([target.author, target.assignee], project)
- recipients = recipients.concat(project_watchers(project)).uniq
+ recipients = build_recipients(target, project)
recipients.delete(current_user)
recipients.each do |recipient|
@@ -339,6 +350,20 @@ class NotificationService
end
end
+ def build_recipients(target, project)
+ recipients =
+ if target.respond_to?(:participants)
+ target.participants
+ else
+ [target.author, target.assignee]
+ end
+
+ recipients = reject_muted_users(recipients, project)
+ recipients = reject_mention_users(recipients, project)
+ recipients = recipients.concat(project_watchers(project)).uniq
+ recipients
+ end
+
def mailer
Notify.delay
end
diff --git a/app/services/oauth2/access_token_validation_service.rb b/app/services/oauth2/access_token_validation_service.rb
new file mode 100644
index 00000000000..6194f6ce91e
--- /dev/null
+++ b/app/services/oauth2/access_token_validation_service.rb
@@ -0,0 +1,41 @@
+module Oauth2::AccessTokenValidationService
+ # Results:
+ VALID = :valid
+ EXPIRED = :expired
+ REVOKED = :revoked
+ INSUFFICIENT_SCOPE = :insufficient_scope
+
+ class << self
+ def validate(token, scopes: [])
+ if token.expired?
+ return EXPIRED
+
+ elsif token.revoked?
+ return REVOKED
+
+ elsif !self.sufficient_scope?(token, scopes)
+ return INSUFFICIENT_SCOPE
+
+ else
+ return VALID
+ end
+ end
+
+ protected
+ # True if the token's scope is a superset of required scopes,
+ # or the required scopes is empty.
+ def sufficient_scope?(token, scopes)
+ if scopes.blank?
+ # if no any scopes required, the scopes of token is sufficient.
+ return true
+ else
+ # If there are scopes required, then check whether
+ # the set of authorized scopes is a superset of the set of required scopes
+ required_scopes = Set.new(scopes)
+ authorized_scopes = Set.new(token.scopes)
+
+ return authorized_scopes >= required_scopes
+ end
+ end
+ end
+end
diff --git a/app/services/projects/autocomplete_service.rb b/app/services/projects/autocomplete_service.rb
new file mode 100644
index 00000000000..7408e09ed1e
--- /dev/null
+++ b/app/services/projects/autocomplete_service.rb
@@ -0,0 +1,15 @@
+module Projects
+ class AutocompleteService < BaseService
+ def initialize(project)
+ @project = project
+ end
+
+ def issues
+ @project.issues.opened.select([:iid, :title])
+ end
+
+ def merge_requests
+ @project.merge_requests.opened.select([:iid, :title])
+ end
+ end
+end
diff --git a/app/services/projects/create_service.rb b/app/services/projects/create_service.rb
index 12386792aab..4fe790b98f1 100644
--- a/app/services/projects/create_service.rb
+++ b/app/services/projects/create_service.rb
@@ -7,17 +7,22 @@ module Projects
def execute
@project = Project.new(params)
- # Reset visibility levet if is not allowed to set it
+ # Reset visibility level if is not allowed to set it
unless Gitlab::VisibilityLevel.allowed_for?(current_user, params[:visibility_level])
@project.visibility_level = default_features.visibility_level
end
- # Parametrize path for project
- #
- # Ex.
- # 'GitLab HQ'.parameterize => "gitlab-hq"
- #
- @project.path = @project.name.dup.parameterize unless @project.path.present?
+ # Set project name from path
+ if @project.name.present? && @project.path.present?
+ # if both name and path set - everything is ok
+ elsif @project.path.present?
+ # Set project name from path
+ @project.name = @project.path.dup
+ elsif @project.name.present?
+ # For compatibility - set path from name
+ # TODO: remove this in 8.0
+ @project.path = @project.name.dup.parameterize
+ end
# get namespace id
namespace_id = params[:namespace_id]
@@ -37,37 +42,18 @@ module Projects
@project.creator = current_user
- if @project.save
- log_info("#{@project.owner.name} created a new project \"#{@project.name_with_namespace}\"")
- system_hook_service.execute_hooks_for(@project, :create)
-
- unless @project.group
- @project.team << [current_user, :master]
- end
-
- @project.update_column(:last_activity_at, @project.created_at)
+ Project.transaction do
+ @project.save
- if @project.import?
- @project.import_start
- else
- GitlabShellWorker.perform_async(
- :add_repository,
- @project.path_with_namespace
- )
- end
-
- if @project.wiki_enabled?
- begin
- # force the creation of a wiki,
- ProjectWiki.new(@project, @project.owner).wiki
- rescue ProjectWiki::CouldNotCreateWikiError => ex
- # Prevent project observer crash
- # if failed to create wiki
- nil
+ unless @project.import?
+ unless @project.create_repository
+ raise 'Failed to create repository'
end
end
end
+ after_create_actions if @project.persisted?
+
@project
rescue => ex
@project.errors.add(:base, "Can't save project. Please try again later")
@@ -84,5 +70,24 @@ module Projects
namespace = Namespace.find_by(id: namespace_id)
current_user.can?(:create_projects, namespace)
end
+
+ def after_create_actions
+ log_info("#{@project.owner.name} created a new project \"#{@project.name_with_namespace}\"")
+
+ @project.create_wiki if @project.wiki_enabled?
+
+ event_service.create_project(@project, current_user)
+ system_hook_service.execute_hooks_for(@project, :create)
+
+ unless @project.group
+ @project.team << [current_user, :master]
+ end
+
+ @project.update_column(:last_activity_at, @project.created_at)
+
+ if @project.import?
+ @project.import_start
+ end
+ end
end
end
diff --git a/app/services/projects/fork_service.rb b/app/services/projects/fork_service.rb
index a59311bf942..6b0d4aca3e1 100644
--- a/app/services/projects/fork_service.rb
+++ b/app/services/projects/fork_service.rb
@@ -2,11 +2,9 @@ module Projects
class ForkService < BaseService
include Gitlab::ShellAdapter
- def initialize(project, user)
- @from_project, @current_user = project, user
- end
-
def execute
+ @from_project = @project
+
project_params = {
visibility_level: @from_project.visibility_level,
description: @from_project.description,
@@ -15,8 +13,21 @@ module Projects
project = Project.new(project_params)
project.name = @from_project.name
project.path = @from_project.path
- project.namespace = current_user.namespace
- project.creator = current_user
+ project.creator = @current_user
+ if @from_project.avatar.present? && @from_project.avatar.image?
+ project.avatar = @from_project.avatar
+ end
+
+ if namespace = @params[:namespace]
+ project.namespace = namespace
+ else
+ project.namespace = @current_user.namespace
+ end
+
+ unless @current_user.can?(:create_projects, project.namespace)
+ project.errors.add(:namespace, 'insufficient access rights')
+ return project
+ end
# If the project cannot save, we do not want to trigger the project destroy
# as this can have the side effect of deleting a repo attached to an existing
@@ -27,23 +38,23 @@ module Projects
#First save the DB entries as they can be rolled back if the repo fork fails
project.build_forked_project_link(forked_to_project_id: project.id, forked_from_project_id: @from_project.id)
if project.save
- project.team << [current_user, :master]
+ project.team << [@current_user, :master]
end
#Now fork the repo
unless gitlab_shell.fork_repository(@from_project.path_with_namespace, project.namespace.path)
- raise "forking failed in gitlab-shell"
+ raise 'forking failed in gitlab-shell'
end
project.ensure_satellite_exists
end
rescue => ex
- project.errors.add(:base, "Fork transaction failed.")
+ project.errors.add(:base, 'Fork transaction failed.')
project.destroy
end
else
- project.errors.add(:base, "Invalid fork destination")
+ project.errors.add(:base, 'Invalid fork destination')
end
- project
+ project
end
end
end
diff --git a/app/services/projects/image_service.rb b/app/services/projects/image_service.rb
deleted file mode 100644
index c79ddddd972..00000000000
--- a/app/services/projects/image_service.rb
+++ /dev/null
@@ -1,39 +0,0 @@
-module Projects
- class ImageService < BaseService
- include Rails.application.routes.url_helpers
- def initialize(repository, params, root_url)
- @repository, @params, @root_url = repository, params.dup, root_url
- end
-
- def execute
- uploader = FileUploader.new('uploads', upload_path, accepted_images)
- image = @params['markdown_img']
-
- if image && correct_mime_type?(image)
- alt = image.original_filename
- uploader.store!(image)
- link = {
- 'alt' => File.basename(alt, '.*'),
- 'url' => File.join(@root_url, uploader.url)
- }
- else
- link = nil
- end
- end
-
- protected
-
- def upload_path
- base_dir = FileUploader.generate_dir
- File.join(@repository.path_with_namespace, base_dir)
- end
-
- def accepted_images
- %w(png jpg jpeg gif)
- end
-
- def correct_mime_type?(image)
- accepted_images.map{ |format| image.content_type.include? format }.any?
- end
- end
-end
diff --git a/app/services/projects/participants_service.rb b/app/services/projects/participants_service.rb
index c4d2c0963b7..f6f9aceef95 100644
--- a/app/services/projects/participants_service.rb
+++ b/app/services/projects/participants_service.rb
@@ -1,17 +1,19 @@
module Projects
class ParticipantsService < BaseService
- def initialize(project)
- @project = project
+ def initialize(project, user)
+ @project = project
+ @user = user
end
def execute(note_type, note_id)
- participating = if note_type && note_id
- participants_in(note_type, note_id)
- else
- []
- end
+ participating =
+ if note_type && note_id
+ participants_in(note_type, note_id)
+ else
+ []
+ end
team_members = sorted(@project.team.members)
- participants = all_members + team_members + participating
+ participants = all_members + groups + team_members + participating
participants.uniq
end
@@ -33,11 +35,21 @@ module Projects
end
def sorted(users)
- users.uniq.to_a.compact.sort_by(&:username).map { |user| { username: user.username, name: user.name } }
+ users.uniq.to_a.compact.sort_by(&:username).map do |user|
+ { username: user.username, name: user.name }
+ end
+ end
+
+ def groups
+ @user.authorized_groups.sort_by(&:path).map do |group|
+ count = group.users.count
+ { username: group.path, name: "#{group.name} (#{count})" }
+ end
end
def all_members
- [{ username: "all", name: "Project and Group Members" }]
+ count = @project.team.members.flatten.count
+ [{ username: "all", name: "All Project and Group Members (#{count})" }]
end
end
end
diff --git a/app/services/projects/transfer_service.rb b/app/services/projects/transfer_service.rb
index e39fe882cb1..3372cfc11d0 100644
--- a/app/services/projects/transfer_service.rb
+++ b/app/services/projects/transfer_service.rb
@@ -12,7 +12,7 @@ module Projects
class TransferError < StandardError; end
def execute
- namespace_id = params[:namespace_id]
+ namespace_id = params[:new_namespace_id]
namespace = Namespace.find_by(id: namespace_id)
if allowed_transfer?(current_user, project, namespace)
diff --git a/app/services/projects/upload_service.rb b/app/services/projects/upload_service.rb
new file mode 100644
index 00000000000..a186c97628f
--- /dev/null
+++ b/app/services/projects/upload_service.rb
@@ -0,0 +1,22 @@
+module Projects
+ class UploadService < BaseService
+ def initialize(project, file)
+ @project, @file = project, file
+ end
+
+ def execute
+ return nil unless @file
+
+ uploader = FileUploader.new(@project)
+ uploader.store!(@file)
+
+ filename = uploader.image? ? uploader.file.basename : uploader.file.filename
+
+ {
+ 'alt' => filename,
+ 'url' => uploader.secure_url,
+ 'is_image' => uploader.image?
+ }
+ end
+ end
+end
diff --git a/app/services/system_hooks_service.rb b/app/services/system_hooks_service.rb
index a6b68749a71..46f6e91e808 100644
--- a/app/services/system_hooks_service.rb
+++ b/app/services/system_hooks_service.rb
@@ -18,7 +18,7 @@ class SystemHooksService
def build_event_data(model, event)
data = {
event_name: build_event_name(model, event),
- created_at: model.created_at
+ created_at: model.created_at.xmlschema
}
case model
@@ -60,6 +60,26 @@ class SystemHooksService
access_level: model.human_access,
project_visibility: Project.visibility_levels.key(model.project.visibility_level_field).downcase
})
+ when Group
+ owner = model.owner
+
+ data.merge!(
+ name: model.name,
+ path: model.path,
+ group_id: model.id,
+ owner_name: owner.respond_to?(:name) ? owner.name : nil,
+ owner_email: owner.respond_to?(:email) ? owner.email : nil,
+ )
+ when GroupMember
+ data.merge!(
+ group_name: model.group.name,
+ group_path: model.group.path,
+ group_id: model.group.id,
+ user_name: model.user.name,
+ user_email: model.user.email,
+ user_id: model.user.id,
+ group_access: model.human_access,
+ )
end
end
@@ -68,6 +88,9 @@ class SystemHooksService
when ProjectMember
return "user_add_to_team" if event == :create
return "user_remove_from_team" if event == :destroy
+ when GroupMember
+ return 'user_add_to_group' if event == :create
+ return 'user_remove_from_group' if event == :destroy
else
"#{model.class.name.downcase}_#{event.to_s}"
end
diff --git a/app/services/test_hook_service.rb b/app/services/test_hook_service.rb
index b6b1ef29b51..21ec2c01cb8 100644
--- a/app/services/test_hook_service.rb
+++ b/app/services/test_hook_service.rb
@@ -1,9 +1,6 @@
class TestHookService
def execute(hook, current_user)
- data = GitPushService.new.sample_data(hook.project, current_user)
+ data = Gitlab::PushDataBuilder.build_sample(hook.project, current_user)
hook.execute(data)
- true
- rescue SocketError
- false
end
end
diff --git a/app/uploaders/attachment_uploader.rb b/app/uploaders/attachment_uploader.rb
index b122b6c8658..a9691bee46e 100644
--- a/app/uploaders/attachment_uploader.rb
+++ b/app/uploaders/attachment_uploader.rb
@@ -3,8 +3,6 @@
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
@@ -22,15 +20,7 @@ class AttachmentUploader < CarrierWave::Uploader::Base
false
end
- def secure_url
- Gitlab.config.gitlab.relative_url_root + "/files/#{model.class.to_s.underscore}/#{model.id}/#{file.filename}"
- end
-
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/uploaders/avatar_uploader.rb b/app/uploaders/avatar_uploader.rb
new file mode 100644
index 00000000000..7cad044555b
--- /dev/null
+++ b/app/uploaders/avatar_uploader.rb
@@ -0,0 +1,32 @@
+# encoding: utf-8
+
+class AvatarUploader < CarrierWave::Uploader::Base
+ storage :file
+
+ after :store, :reset_events_cache
+
+ def store_dir
+ "uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
+ end
+
+ def image?
+ img_ext = %w(png jpg jpeg gif bmp tiff)
+ if file.respond_to?(:extension)
+ img_ext.include?(file.extension.downcase)
+ else
+ # Not all CarrierWave storages respond to :extension
+ ext = file.path.split('.').last.downcase
+ img_ext.include?(ext)
+ end
+ rescue
+ false
+ end
+
+ 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/uploaders/file_uploader.rb b/app/uploaders/file_uploader.rb
index 0fa987c93f6..f9673abbfe8 100644
--- a/app/uploaders/file_uploader.rb
+++ b/app/uploaders/file_uploader.rb
@@ -2,40 +2,47 @@
class FileUploader < CarrierWave::Uploader::Base
storage :file
- def initialize(base_dir, path = '', allowed_extensions = nil)
- @base_dir = base_dir
- @path = path
- @allowed_extensions = allowed_extensions
+ attr_accessor :project, :secret
+
+ def initialize(project, secret = self.class.generate_secret)
+ @project = project
+ @secret = secret
end
def base_dir
- @base_dir
+ "uploads"
end
def store_dir
- File.join(@base_dir, @path)
+ File.join(base_dir, @project.path_with_namespace, @secret)
end
def cache_dir
- File.join(@base_dir, 'tmp', @path)
+ File.join(base_dir, 'tmp', @project.path_with_namespace, @secret)
end
- def extension_white_list
- @allowed_extensions
+ def self.generate_secret
+ SecureRandom.hex
end
- def store!(file)
- @filename = self.class.generate_filename(file)
- super
+ def secure_url
+ File.join(Gitlab.config.gitlab.url, @project.path_with_namespace, "uploads", @secret, file.filename)
end
- def self.generate_filename(file)
- original_filename = File.basename(file.original_filename, '.*')
- extension = File.extname(file.original_filename)
- new_filename = Digest::MD5.hexdigest(original_filename) + extension
+ def file_storage?
+ self.class.storage == CarrierWave::Storage::File
end
- def self.generate_dir
- SecureRandom.hex(5)
+ def image?
+ img_ext = %w(png jpg jpeg gif bmp tiff)
+ if file.respond_to?(:extension)
+ img_ext.include?(file.extension.downcase)
+ else
+ # Not all CarrierWave storages respond to :extension
+ ext = file.path.split('.').last.downcase
+ img_ext.include?(ext)
+ end
+ rescue
+ false
end
end
diff --git a/app/views/admin/application_settings/_form.html.haml b/app/views/admin/application_settings/_form.html.haml
new file mode 100644
index 00000000000..f528d69f431
--- /dev/null
+++ b/app/views/admin/application_settings/_form.html.haml
@@ -0,0 +1,48 @@
+= form_for @application_setting, url: admin_application_settings_path, html: { class: 'form-horizontal fieldset-form' } do |f|
+ - if @application_setting.errors.any?
+ #error_explanation
+ .alert.alert-danger
+ - @application_setting.errors.full_messages.each do |msg|
+ %p= msg
+
+ %fieldset
+ %legend Features
+ .form-group
+ = f.label :signup_enabled, class: 'control-label'
+ .col-sm-10
+ = f.check_box :signup_enabled, class: 'checkbox'
+ .form-group
+ = f.label :signin_enabled, class: 'control-label'
+ .col-sm-10
+ = f.check_box :signin_enabled, class: 'checkbox'
+ .form-group
+ = f.label :gravatar_enabled, class: 'control-label'
+ .col-sm-10
+ = f.check_box :gravatar_enabled, class: 'checkbox'
+ .form-group
+ = f.label :twitter_sharing_enabled, "Twitter enabled", class: 'control-label'
+ .col-sm-10
+ = f.check_box :twitter_sharing_enabled, class: 'checkbox'
+ %span.help-block Show users button to share their newly created public or internal projects on twitter
+ %fieldset
+ %legend Misc
+ .form-group
+ = f.label :default_projects_limit, class: 'control-label'
+ .col-sm-10
+ = f.number_field :default_projects_limit, class: 'form-control'
+ .form-group
+ = f.label :default_branch_protection, class: 'control-label'
+ .col-sm-10
+ = f.select :default_branch_protection, options_for_select(Gitlab::Access.protection_options, @application_setting.default_branch_protection), {}, class: 'form-control'
+ .form-group
+ = f.label :home_page_url, class: 'control-label'
+ .col-sm-10
+ = f.text_field :home_page_url, class: 'form-control', placeholder: 'http://company.example.com'
+ %span.help-block We will redirect non-logged in users to this page
+ .form-group
+ = f.label :sign_in_text, class: 'control-label'
+ .col-sm-10
+ = f.text_area :sign_in_text, class: 'form-control', rows: 4
+ .help-block Markdown enabled
+ .form-actions
+ = f.submit 'Save', class: 'btn btn-primary'
diff --git a/app/views/admin/application_settings/show.html.haml b/app/views/admin/application_settings/show.html.haml
new file mode 100644
index 00000000000..39b66647a5a
--- /dev/null
+++ b/app/views/admin/application_settings/show.html.haml
@@ -0,0 +1,3 @@
+%h3.page-title Application settings
+%hr
+= render 'form'
diff --git a/app/views/admin/applications/_delete_form.html.haml b/app/views/admin/applications/_delete_form.html.haml
new file mode 100644
index 00000000000..371ac55209f
--- /dev/null
+++ b/app/views/admin/applications/_delete_form.html.haml
@@ -0,0 +1,4 @@
+- submit_btn_css ||= 'btn btn-link btn-remove btn-small'
+= form_tag admin_application_path(application) do
+ %input{:name => "_method", :type => "hidden", :value => "delete"}/
+ = submit_tag 'Destroy', onclick: "return confirm('Are you sure?')", class: submit_btn_css \ No newline at end of file
diff --git a/app/views/admin/applications/_form.html.haml b/app/views/admin/applications/_form.html.haml
new file mode 100644
index 00000000000..b77d188a38d
--- /dev/null
+++ b/app/views/admin/applications/_form.html.haml
@@ -0,0 +1,24 @@
+= form_for [:admin, @application], url: @url, html: {class: 'form-horizontal', role: 'form'} do |f|
+ - if application.errors.any?
+ .alert.alert-danger{"data-alert" => ""}
+ %p Whoops! Check your form for possible errors
+ = content_tag :div, class: "form-group#{' has-error' if application.errors[:name].present?}" do
+ = f.label :name, class: 'col-sm-2 control-label'
+ .col-sm-10
+ = f.text_field :name, class: 'form-control'
+ = doorkeeper_errors_for application, :name
+ = content_tag :div, class: "form-group#{' has-error' if application.errors[:redirect_uri].present?}" do
+ = f.label :redirect_uri, class: 'col-sm-2 control-label'
+ .col-sm-10
+ = f.text_area :redirect_uri, class: 'form-control'
+ = doorkeeper_errors_for application, :redirect_uri
+ %span.help-block
+ Use one line per URI
+ - if Doorkeeper.configuration.native_redirect_uri
+ %span.help-block
+ Use
+ %code= Doorkeeper.configuration.native_redirect_uri
+ for local tests
+ .form-actions
+ = f.submit 'Submit', class: "btn btn-primary wide"
+ = link_to "Cancel", admin_applications_path, class: "btn btn-default"
diff --git a/app/views/admin/applications/edit.html.haml b/app/views/admin/applications/edit.html.haml
new file mode 100644
index 00000000000..e408ae2f29d
--- /dev/null
+++ b/app/views/admin/applications/edit.html.haml
@@ -0,0 +1,3 @@
+%h3.page-title Edit application
+- @url = admin_application_path(@application)
+= render 'form', application: @application \ No newline at end of file
diff --git a/app/views/admin/applications/index.html.haml b/app/views/admin/applications/index.html.haml
new file mode 100644
index 00000000000..d550278710e
--- /dev/null
+++ b/app/views/admin/applications/index.html.haml
@@ -0,0 +1,22 @@
+%h3.page-title
+ System OAuth applications
+%p.light
+ System OAuth application does not belong to certain user and can be managed only by admins
+%hr
+%p= link_to 'New Application', new_admin_application_path, class: 'btn btn-success'
+%table.table.table-striped
+ %thead
+ %tr
+ %th Name
+ %th Callback URL
+ %th Clients
+ %th
+ %th
+ %tbody.oauth-applications
+ - @applications.each do |application|
+ %tr{:id => "application_#{application.id}"}
+ %td= link_to application.name, admin_application_path(application)
+ %td= application.redirect_uri
+ %td= application.access_tokens.map(&:resource_owner_id).uniq.count
+ %td= link_to 'Edit', edit_admin_application_path(application), class: 'btn btn-link'
+ %td= render 'delete_form', application: application
diff --git a/app/views/admin/applications/new.html.haml b/app/views/admin/applications/new.html.haml
new file mode 100644
index 00000000000..7c62425f19c
--- /dev/null
+++ b/app/views/admin/applications/new.html.haml
@@ -0,0 +1,3 @@
+%h3.page-title New application
+- @url = admin_applications_path
+= render 'form', application: @application \ No newline at end of file
diff --git a/app/views/admin/applications/show.html.haml b/app/views/admin/applications/show.html.haml
new file mode 100644
index 00000000000..2abe390ce13
--- /dev/null
+++ b/app/views/admin/applications/show.html.haml
@@ -0,0 +1,26 @@
+%h3.page-title
+ Application: #{@application.name}
+
+
+%table.table
+ %tr
+ %td
+ Application Id
+ %td
+ %code#application_id= @application.uid
+ %tr
+ %td
+ Secret:
+ %td
+ %code#secret= @application.secret
+
+ %tr
+ %td
+ Callback url
+ %td
+ - @application.redirect_uri.split.each do |uri|
+ %div
+ %span.monospace= uri
+.form-actions
+ = link_to 'Edit', edit_admin_application_path(@application), class: 'btn btn-primary wide pull-left'
+ = render 'delete_form', application: @application, submit_btn_css: 'btn btn-danger prepend-left-10'
diff --git a/app/views/admin/dashboard/index.html.haml b/app/views/admin/dashboard/index.html.haml
index 7427cea7e8b..931b0c5c107 100644
--- a/app/views/admin/dashboard/index.html.haml
+++ b/app/views/admin/dashboard/index.html.haml
@@ -1,69 +1,7 @@
-%h3.page-title
- Admin area
-%p.light
- You can manage projects, users and other GitLab data from here.
-%hr
.admin-dashboard
.row
- .col-sm-4
- .light-well
- %h4 Projects
- .data
- = link_to admin_projects_path do
- %h1= Project.count
- %hr
- = link_to 'New Project', new_project_path, class: "btn btn-new"
- .col-sm-4
- .light-well
- %h4 Users
- .data
- = link_to admin_users_path do
- %h1= User.count
- %hr
- = link_to 'New User', new_admin_user_path, class: "btn btn-new"
- .col-sm-4
- .light-well
- %h4 Groups
- .data
- = link_to admin_groups_path do
- %h1= Group.count
- %hr
- = link_to 'New Group', new_admin_group_path, class: "btn btn-new"
-
- .row.prepend-top-10
- .col-md-4
- %h4 Latest projects
- %hr
- - @projects.each do |project|
- %p
- = link_to project.name_with_namespace, [:admin, project], class: 'str-truncated'
- %span.light.pull-right
- #{time_ago_with_tooltip(project.created_at)}
-
- .col-md-4
- %h4 Latest users
- %hr
- - @users.each do |user|
- %p
- = link_to [:admin, user], class: 'str-truncated' do
- = user.name
- %span.light.pull-right
- #{time_ago_with_tooltip(user.created_at)}
-
- .col-md-4
- %h4 Latest groups
- %hr
- - @groups.each do |group|
- %p
- = link_to [:admin, group], class: 'str-truncated' do
- = group.name
- %span.light.pull-right
- #{time_ago_with_tooltip(group.created_at)}
-
- %br
- .row
.col-md-4
- %h4 Stats
+ %h4 Statistics
%hr
%p
Forks
@@ -94,7 +32,7 @@
%span.light.pull-right
= Milestone.count
%p
- Active users last 30 days
+ Users who signed in during last 30 days
%span.light.pull-right
= User.where("current_sign_in_at > ?", 30.days.ago).count
.col-md-4
@@ -104,7 +42,7 @@
%p
Sign up
%span.light.pull-right
- = boolean_to_icon gitlab_config.signup_enabled
+ = boolean_to_icon signup_enabled?
%p
LDAP
%span.light.pull-right
@@ -112,7 +50,7 @@
%p
Gravatar
%span.light.pull-right
- = boolean_to_icon Gitlab.config.gravatar.enabled
+ = boolean_to_icon gravatar_enabled?
%p
OmniAuth
%span.light.pull-right
@@ -141,3 +79,59 @@
Rails
%span.pull-right
#{Rails::VERSION::STRING}
+ %hr
+ .row
+ .col-sm-4
+ .light-well
+ %h4 Projects
+ .data
+ = link_to admin_namespaces_projects_path do
+ %h1= Project.count
+ %hr
+ = link_to('New Project', new_project_path, class: "btn btn-new")
+ .col-sm-4
+ .light-well
+ %h4 Users
+ .data
+ = link_to admin_users_path do
+ %h1= User.count
+ %hr
+ = link_to 'New User', new_admin_user_path, class: "btn btn-new"
+ .col-sm-4
+ .light-well
+ %h4 Groups
+ .data
+ = link_to admin_groups_path do
+ %h1= Group.count
+ %hr
+ = link_to 'New Group', new_admin_group_path, class: "btn btn-new"
+
+ .row.prepend-top-10
+ .col-md-4
+ %h4 Latest projects
+ %hr
+ - @projects.each do |project|
+ %p
+ = link_to project.name_with_namespace, [:admin, project.namespace.becomes(Namespace), project], class: 'str-truncated'
+ %span.light.pull-right
+ #{time_ago_with_tooltip(project.created_at)}
+
+ .col-md-4
+ %h4 Latest users
+ %hr
+ - @users.each do |user|
+ %p
+ = link_to [:admin, user], class: 'str-truncated' do
+ = user.name
+ %span.light.pull-right
+ #{time_ago_with_tooltip(user.created_at)}
+
+ .col-md-4
+ %h4 Latest groups
+ %hr
+ - @groups.each do |group|
+ %p
+ = link_to [:admin, group], class: 'str-truncated' do
+ = group.name
+ %span.light.pull-right
+ #{time_ago_with_tooltip(group.created_at)}
diff --git a/app/views/admin/groups/_form.html.haml b/app/views/admin/groups/_form.html.haml
index f4d7e25fd74..86a73200609 100644
--- a/app/views/admin/groups/_form.html.haml
+++ b/app/views/admin/groups/_form.html.haml
@@ -21,17 +21,6 @@
= link_to 'Cancel', admin_groups_path, class: "btn btn-cancel"
- else
- .form-group.group_name_holder
- = f.label :path, class: 'control-label' do
- %span Group path
- .col-sm-10
- = f.text_field :path, placeholder: "example-group", class: "form-control danger"
- .bs-callout.bs-callout-danger
- %ul
- %li Changing group path can have unintended side effects.
- %li Renaming group path will rename directory for all related projects
- %li It will change web url for access group and group projects.
- %li It will change the git path to repositories under this group.
.form-actions
= f.submit 'Save changes', class: "btn btn-primary"
= link_to 'Cancel', admin_group_path(@group), class: "btn btn-cancel"
diff --git a/app/views/admin/groups/index.html.haml b/app/views/admin/groups/index.html.haml
index 1d7fef43184..8ae9a1edea9 100644
--- a/app/views/admin/groups/index.html.haml
+++ b/app/views/admin/groups/index.html.haml
@@ -8,10 +8,31 @@
%hr
= form_tag admin_groups_path, method: :get, class: 'form-inline' do
+ = hidden_field_tag :sort, @sort
.form-group
= text_field_tag :name, params[:name], class: "form-control input-mn-300"
= button_tag "Search", class: "btn submit btn-primary"
+ .pull-right
+ .dropdown.inline
+ %a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"}
+ %span.light sort:
+ - if @sort.present?
+ = sort_options_hash[@sort]
+ - else
+ = sort_title_recently_created
+ %b.caret
+ %ul.dropdown-menu
+ %li
+ = link_to admin_groups_path(sort: sort_value_recently_created) do
+ = sort_title_recently_created
+ = link_to admin_groups_path(sort: sort_value_oldest_created) do
+ = sort_title_oldest_created
+ = link_to admin_groups_path(sort: sort_value_recently_updated) do
+ = sort_title_recently_updated
+ = link_to admin_groups_path(sort: sort_value_oldest_updated) do
+ = sort_title_oldest_updated
+
%hr
%ul.bordered-list
diff --git a/app/views/admin/groups/show.html.haml b/app/views/admin/groups/show.html.haml
index 8057de38805..bb7f1972925 100644
--- a/app/views/admin/groups/show.html.haml
+++ b/app/views/admin/groups/show.html.haml
@@ -41,7 +41,7 @@
- @projects.each do |project|
%li
%strong
- = link_to project.name_with_namespace, [:admin, project]
+ = link_to project.name_with_namespace, [:admin, project.namespace.becomes(Namespace), project]
%span.label.label-gray
= repository_size(project)
%span.pull-right.light
@@ -64,7 +64,7 @@
%div.prepend-top-10
= select_tag :access_level, options_for_select(GroupMember.access_level_roles), class: "project-access-select select2"
%hr
- = button_tag 'Add users into group', class: "btn btn-create"
+ = button_tag 'Add users to group', class: "btn btn-create"
.panel.panel-default
.panel-heading
%h3.panel-title
diff --git a/app/views/admin/keys/show.html.haml b/app/views/admin/keys/show.html.haml
new file mode 100644
index 00000000000..5b23027b3ab
--- /dev/null
+++ b/app/views/admin/keys/show.html.haml
@@ -0,0 +1 @@
+= render "profiles/keys/key_details", admin: true
diff --git a/app/views/admin/projects/index.html.haml b/app/views/admin/projects/index.html.haml
index 2cd6b12be7f..3780500a447 100644
--- a/app/views/admin/projects/index.html.haml
+++ b/app/views/admin/projects/index.html.haml
@@ -1,7 +1,9 @@
.row
- .col-md-3
+ = link_to '#aside', class: 'show-aside' do
+ %i.fa.fa-angle-left
+ %aside.col-md-3
.admin-filter
- = form_tag admin_projects_path, method: :get, class: '' do
+ = form_tag admin_namespaces_projects_path, method: :get, class: '' do
.form-group
= label_tag :name, 'Name:'
= text_field_tag :name, params[:name], class: "form-control"
@@ -13,15 +15,13 @@
.form-group
%strong Activity
.checkbox
- = label_tag :with_push, 'Not empty'
- = check_box_tag :with_push, 1, params[:with_push]
- &nbsp;
- %span.light Projects with push events
+ = label_tag :with_push do
+ = check_box_tag :with_push, 1, params[:with_push]
+ %span Projects with push events
.checkbox
- = label_tag :abandoned, 'Abandoned'
- = check_box_tag :abandoned, 1, params[:abandoned]
- &nbsp;
- %span.light No activity over 6 month
+ = label_tag :abandoned do
+ = check_box_tag :abandoned, 1, params[:abandoned]
+ %span No activity over 6 month
%fieldset
%strong Visibility level:
@@ -36,9 +36,9 @@
%hr
= hidden_field_tag :sort, params[:sort]
= button_tag "Search", class: "btn submit btn-primary"
- = link_to "Reset", admin_projects_path, class: "btn btn-cancel"
+ = link_to "Reset", admin_namespaces_projects_path, class: "btn btn-cancel"
- .col-md-9
+ %section.col-md-9
.panel.panel-default
.panel-heading
Projects (#{@projects.total_count})
@@ -47,24 +47,22 @@
%a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"}
%span.light sort:
- if @sort.present?
- = @sort.humanize
+ = sort_options_hash[@sort]
- else
- Name
+ = sort_title_recently_created
%b.caret
%ul.dropdown-menu
%li
- = link_to admin_projects_path(sort: nil) do
- Name
- = link_to admin_projects_path(sort: 'newest') do
- Newest
- = link_to admin_projects_path(sort: 'oldest') do
- Oldest
- = link_to admin_projects_path(sort: 'recently_updated') do
- Recently updated
- = link_to admin_projects_path(sort: 'last_updated') do
- Last updated
- = link_to admin_projects_path(sort: 'largest_repository') do
- Largest repository
+ = link_to admin_namespaces_projects_path(sort: sort_value_recently_created) do
+ = sort_title_recently_created
+ = link_to admin_namespaces_projects_path(sort: sort_value_oldest_created) do
+ = sort_title_oldest_created
+ = link_to admin_namespaces_projects_path(sort: sort_value_recently_updated) do
+ = sort_title_recently_updated
+ = link_to admin_namespaces_projects_path(sort: sort_value_oldest_updated) do
+ = sort_title_oldest_updated
+ = link_to admin_namespaces_projects_path(sort: sort_value_largest_repo) do
+ = sort_title_largest_repo
= link_to 'New Project', new_project_path, class: "btn btn-new"
%ul.well-list
- @projects.each do |project|
@@ -72,12 +70,12 @@
.list-item-name
%span{ class: visibility_level_color(project.visibility_level) }
= visibility_level_icon(project.visibility_level)
- = link_to project.name_with_namespace, [:admin, project]
+ = link_to project.name_with_namespace, [:admin, project.namespace.becomes(Namespace), 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], data: { confirm: remove_project_message(project) }, method: :delete, class: "btn btn-small btn-remove"
+ = link_to 'Edit', edit_namespace_project_path(project.namespace, project), id: "edit_#{dom_id(project)}", class: "btn btn-small"
+ = link_to 'Destroy', [project.namespace.becomes(Namespace), project], data: { confirm: remove_project_message(project) }, method: :delete, class: "btn btn-small btn-remove"
- if @projects.blank?
.nothing-here-block 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 6d536199851..1421c2ea909 100644
--- a/app/views/admin/projects/show.html.haml
+++ b/app/views/admin/projects/show.html.haml
@@ -79,11 +79,11 @@
.panel-heading
Transfer project
.panel-body
- = form_for @project, url: transfer_admin_project_path(@project), method: :put, html: { class: 'form-horizontal' } do |f|
+ = form_for @project, url: transfer_admin_namespace_project_path(@project.namespace, @project), method: :put, html: { class: 'form-horizontal' } do |f|
.form-group
- = f.label :namespace_id, "Namespace", class: 'control-label'
+ = f.label :new_namespace_id, "Namespace", class: 'control-label'
.col-sm-10
- = namespace_select_tag :namespace_id, selected: params[:namespace_id], class: 'input-large'
+ = namespace_select_tag :new_namespace_id, selected: params[:namespace_id], class: 'input-large'
.form-group
.col-sm-2
@@ -111,7 +111,7 @@
%small
(#{@project.users.count})
.pull-right
- = link_to project_team_index_path(@project), class: "btn btn-tiny" do
+ = link_to namespace_project_team_index_path(@project.namespace, @project), class: "btn btn-tiny" do
%i.fa.fa-pencil-square-o
Manage Access
%ul.well-list.team_members
@@ -126,7 +126,7 @@
%span.light Owner
- else
%span.light= project_member.human_access
- = 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
+ = link_to namespace_project_team_member_path(@project.namespace, @project, user), data: { confirm: remove_from_project_team_message(@project, user)}, method: :delete, remote: true, class: "btn btn-small btn-remove" do
%i.fa.fa-times
.panel-footer
= paginate @project_members, param_name: 'project_members_page', theme: 'gitlab'
diff --git a/app/views/admin/services/_form.html.haml b/app/views/admin/services/_form.html.haml
new file mode 100644
index 00000000000..5df8849317b
--- /dev/null
+++ b/app/views/admin/services/_form.html.haml
@@ -0,0 +1,40 @@
+%h3.page-title
+ = @service.title
+
+%p #{@service.description} template
+
+= form_for :service, url: admin_application_settings_service_path, method: :put, html: { class: 'form-horizontal fieldset-form' } do |f|
+ - if @service.errors.any?
+ #error_explanation
+ .alert.alert-danger
+ - @service.errors.full_messages.each do |msg|
+ %p= msg
+ - if @service.help.present?
+ .bs-callout
+ = preserve do
+ = markdown @service.help
+
+ - @service.fields.each do |field|
+ - name = field[:name]
+ - value = @service.send(name) unless field[:type] == 'password'
+ - type = field[:type]
+ - placeholder = field[:placeholder]
+ - choices = field[:choices]
+ - default_choice = field[:default_choice]
+
+ .form-group
+ = f.label name, class: "control-label"
+ .col-sm-10
+ - if type == 'text'
+ = 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
+ - elsif type == 'select'
+ = f.select name, options_for_select(choices, value ? value : default_choice), {}, { class: "form-control" }
+ - elsif type == 'password'
+ = f.password_field name, class: 'form-control'
+
+ .form-actions
+ = f.submit 'Save', class: 'btn btn-save'
diff --git a/app/views/admin/services/edit.html.haml b/app/views/admin/services/edit.html.haml
new file mode 100644
index 00000000000..bcc5832792f
--- /dev/null
+++ b/app/views/admin/services/edit.html.haml
@@ -0,0 +1 @@
+= render 'form'
diff --git a/app/views/admin/services/index.html.haml b/app/views/admin/services/index.html.haml
new file mode 100644
index 00000000000..1d3e192a325
--- /dev/null
+++ b/app/views/admin/services/index.html.haml
@@ -0,0 +1,22 @@
+%h3.page-title Service templates
+%p.light Service template allows you to set default values for project services
+
+%table.table
+ %thead
+ %tr
+ %th
+ %th Service
+ %th Desription
+ %th Last edit
+ - @services.sort_by(&:title).each do |service|
+ %tr
+ %td
+ = icon("copy", class: 'clgray')
+ %td
+ = link_to edit_admin_application_settings_service_path(service.id) do
+ %strong= service.title
+ %td
+ = service.description
+ %td.light
+ = time_ago_in_words service.updated_at
+ ago
diff --git a/app/views/admin/users/index.html.haml b/app/views/admin/users/index.html.haml
index 92c619738a2..4a4f0549ada 100644
--- a/app/views/admin/users/index.html.haml
+++ b/app/views/admin/users/index.html.haml
@@ -1,5 +1,7 @@
.row
- .col-md-3
+ = link_to '#aside', class: 'show-aside' do
+ %i.fa.fa-angle-left
+ %aside.col-md-3
.admin-filter
%ul.nav.nav-pills.nav-stacked
%li{class: "#{'active' unless params[:filter]}"}
@@ -27,7 +29,7 @@
%hr
= link_to 'Reset', admin_users_path, class: "btn btn-cancel"
- .col-md-9
+ %section.col-md-9
.panel.panel-default
.panel-heading
Users (#{@users.total_count})
@@ -36,22 +38,27 @@
%a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"}
%span.light sort:
- if @sort.present?
- = @sort.humanize
+ = sort_options_hash[@sort]
- else
- Name
+ = sort_title_name
%b.caret
%ul.dropdown-menu
%li
- = link_to admin_users_path(sort: nil) do
- Name
- = link_to admin_users_path(sort: 'recent_sign_in') do
- Recent sign in
- = link_to admin_users_path(sort: 'oldest_sign_in') do
- Oldest sign in
- = link_to admin_users_path(sort: 'recently_created') do
- Recently created
- = link_to admin_users_path(sort: 'late_created') do
- Late created
+ = link_to admin_users_path(sort: sort_value_name) do
+ = sort_title_name
+ = link_to admin_users_path(sort: sort_value_recently_signin) do
+ = sort_title_recently_signin
+ = link_to admin_users_path(sort: sort_value_oldest_signin) do
+ = sort_title_oldest_signin
+ = link_to admin_users_path(sort: sort_value_recently_created) do
+ = sort_title_recently_created
+ = link_to admin_users_path(sort: sort_value_oldest_created) do
+ = sort_title_oldest_created
+ = link_to admin_users_path(sort: sort_value_recently_updated) do
+ = sort_title_recently_updated
+ = link_to admin_users_path(sort: sort_value_oldest_updated) do
+ = sort_title_oldest_updated
+
= link_to 'New User', new_admin_user_path, class: "btn btn-new"
%ul.well-list
- @users.each do |user|
diff --git a/app/views/admin/users/show.html.haml b/app/views/admin/users/show.html.haml
index 211d77d5185..90267897503 100644
--- a/app/views/admin/users/show.html.haml
+++ b/app/views/admin/users/show.html.haml
@@ -20,6 +20,8 @@
%a{"data-toggle" => "tab", href: "#groups"} Groups
%li
%a{"data-toggle" => "tab", href: "#projects"} Projects
+ %li
+ %a{"data-toggle" => "tab", href: "#ssh-keys"} SSH keys
.tab-content
#account.tab-pane.active
@@ -95,7 +97,7 @@
%li
%span.light LDAP uid:
%strong
- = @user.extern_uid
+ = @user.ldap_identity.extern_uid
- if @user.created_by
%li
@@ -204,7 +206,7 @@
- tm = project.team.find_tm(@user.id)
%li.project_member
.list-item-name
- = link_to admin_project_path(project), class: dom_class(project) do
+ = link_to admin_namespace_project_path(project.namespace, project), class: dom_class(project) do
= project.name_with_namespace
- if tm
@@ -215,5 +217,7 @@
%span.light= tm.human_access
- if tm.respond_to? :project
- = 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
+ = link_to namespace_project_team_member_path(project.namespace, 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.fa.fa-times
+ #ssh-keys.tab-pane
+ = render 'profiles/keys/key_table', admin: true
diff --git a/app/views/dashboard/_activities.html.haml b/app/views/dashboard/_activities.html.haml
index fdf96dd6f56..c1fc1602d0a 100644
--- a/app/views/dashboard/_activities.html.haml
+++ b/app/views/dashboard/_activities.html.haml
@@ -1,9 +1,4 @@
= render "events/event_last_push", event: @last_push
= render 'shared/event_filter'
-
-- if @events.any?
- .content_list
-- else
- .nothing-here-block Projects activity will be displayed here
-
+.content_list
= spinner
diff --git a/app/views/dashboard/_groups.html.haml b/app/views/dashboard/_groups.html.haml
index 5460cf56f22..e3df43d8892 100644
--- a/app/views/dashboard/_groups.html.haml
+++ b/app/views/dashboard/_groups.html.haml
@@ -1,15 +1,17 @@
.panel.panel-default
.panel-heading.clearfix
- = search_field_tag :filter_group, nil, placeholder: 'Filter by name', class: 'dash-filter form-control'
- - if current_user.can_create_group?
- = link_to new_group_path, class: "btn btn-new pull-right" do
- %i.fa.fa-plus
- New group
+ .input-group
+ = search_field_tag :filter_group, nil, placeholder: 'Filter by name', class: 'dash-filter form-control'
+ - if current_user.can_create_group?
+ .input-group-addon.dash-new-group
+ = link_to new_group_path, class: "" do
+ %strong New group
%ul.well-list.dash-list
- groups.each do |group|
%li.group-row
= link_to group_path(id: group.path), class: dom_class(group) do
- = image_tag group_icon(group.path), class: "avatar s24"
+ .dash-project-avatar
+ = image_tag group_icon(group.path), class: "avatar s40"
%span.group-name.filter-title
= truncate(group.name, length: 35)
%span.arrow
diff --git a/app/views/dashboard/_project.html.haml b/app/views/dashboard/_project.html.haml
index 89ed5102754..fa9179cb249 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-avatar
+ = project_icon(project, alt: '', class: 'avatar project-avatar s40')
.dash-project-access-icon
= visibility_level_icon(project.visibility_level)
%span.str-truncated
diff --git a/app/views/dashboard/_projects.html.haml b/app/views/dashboard/_projects.html.haml
index 3598425777f..0596738342f 100644
--- a/app/views/dashboard/_projects.html.haml
+++ b/app/views/dashboard/_projects.html.haml
@@ -1,10 +1,11 @@
.panel.panel-default
.panel-heading.clearfix
- = search_field_tag :filter_projects, nil, placeholder: 'Filter by name', class: 'dash-filter form-control'
- - if current_user.can_create_project?
- = link_to new_project_path, class: "btn btn-new pull-right" do
- %i.fa.fa-plus
- New project
+ .input-group
+ = search_field_tag :filter_projects, nil, placeholder: 'Filter by name', class: 'dash-filter form-control'
+ - if current_user.can_create_project?
+ .input-group-addon.dash-new-project
+ = link_to new_project_path do
+ %strong New project
%ul.well-list.dash-list
- projects.each do |project|
diff --git a/app/views/dashboard/_projects_filter.html.haml b/app/views/dashboard/_projects_filter.html.haml
index b65e882e693..d87ca861aed 100644
--- a/app/views/dashboard/_projects_filter.html.haml
+++ b/app/views/dashboard/_projects_filter.html.haml
@@ -1,55 +1,100 @@
-%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
+.dash-projects-filters.append-bottom-20
+ .append-right-20
+ %ul.nav.nav-tabs
+ = nav_tab :scope, nil do
+ = link_to projects_dashboard_filter_path(scope: nil) do
+ All
+ = nav_tab :scope, 'personal' do
+ = link_to projects_dashboard_filter_path(scope: 'personal') do
+ Personal
+ = nav_tab :scope, 'joined' do
+ = link_to projects_dashboard_filter_path(scope: 'joined') do
+ Joined
+ = nav_tab :scope, 'owned' do
+ = link_to projects_dashboard_filter_path(scope: 'owned') do
+ Owned
-%fieldset
- %legend Visibility
- %ul.nav.nav-pills.nav-stacked.nav-small.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)
+ .dropdown.inline.append-right-10
+ %a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"}
+ %i.fa.fa-globe
+ %span.light Visibility:
+ - if params[:visibility_level].present?
+ = visibility_level_label(params[:visibility_level].to_i)
+ - else
+ Any
+ %b.caret
+ %ul.dropdown-menu
+ %li
+ = link_to projects_dashboard_filter_path(visibility_level: nil) do
+ Any
+ - 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
- %legend Groups
- %ul.nav.nav-pills.nav-stacked.nav-small
- - @groups.each do |group|
- %li{ class: (group.name == params[:group]) ? 'active' : 'light' }
- = link_to projects_dashboard_filter_path(group: group.name) do
- %i.fa.fa-folder-o
- = group.name
- %small.pull-right
- = group.projects.count
+ - if @groups.present?
+ .dropdown.inline.append-right-10
+ %a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"}
+ %i.fa.fa-group
+ %span.light Group:
+ - if params[:group].present?
+ = Group.find_by(name: params[:group]).name
+ - else
+ Any
+ %b.caret
+ %ul.dropdown-menu
+ %li
+ = link_to projects_dashboard_filter_path(group: nil) do
+ Any
+ - @groups.each do |group|
+ %li{ class: (group.name == params[:group]) ? 'active' : 'light' }
+ = link_to projects_dashboard_filter_path(group: group.name) do
+ = group.name
+ %small.pull-right
+ = group.projects.count
-- if @tags.present?
- %fieldset
- %legend Tags
- %ul.nav.nav-pills.nav-stacked.nav-small
- - @tags.each do |tag|
- %li{ class: (tag.name == params[:tag]) ? 'active' : 'light' }
- = link_to projects_dashboard_filter_path(scope: params[:scope], tag: tag.name) do
- %i.fa.fa-tag
- = tag.name
+ - if @tags.present?
+ .dropdown.inline.append-right-10
+ %a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"}
+ %i.fa.fa-tags
+ %span.light Tags:
+ - if params[:tag].present?
+ = params[:tag]
+ - else
+ Any
+ %b.caret
+ %ul.dropdown-menu
+ %li
+ = link_to projects_dashboard_filter_path(tag: nil) do
+ Any
+
+ - @tags.each do |tag|
+ %li{ class: (tag.name == params[:tag]) ? 'active' : 'light' }
+ = link_to projects_dashboard_filter_path(tag: tag.name) do
+ %i.fa.fa-tag
+ = tag.name
+
+ .pull-right
+ .dropdown.inline
+ %a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"}
+ %span.light sort:
+ - if @sort.present?
+ = sort_options_hash[@sort]
+ - else
+ = sort_title_recently_created
+ %b.caret
+ %ul.dropdown-menu
+ %li
+ = link_to projects_dashboard_filter_path(sort: sort_value_recently_created) do
+ = sort_title_recently_created
+ = link_to projects_dashboard_filter_path(sort: sort_value_oldest_created) do
+ = sort_title_oldest_created
+ = link_to projects_dashboard_filter_path(sort: sort_value_recently_updated) do
+ = sort_title_recently_updated
+ = link_to projects_dashboard_filter_path(sort: sort_value_oldest_updated) do
+ = sort_title_oldest_updated
+ = link_to projects_dashboard_filter_path(sort: sort_value_name) do
+ = sort_title_name
diff --git a/app/views/dashboard/_sidebar.html.haml b/app/views/dashboard/_sidebar.html.haml
index add9eb7fa29..a980f495427 100644
--- a/app/views/dashboard/_sidebar.html.haml
+++ b/app/views/dashboard/_sidebar.html.haml
@@ -15,11 +15,4 @@
= render "groups", groups: @groups
.prepend-top-20
- %span.rss-icon
- = link_to dashboard_path(:atom, { private_token: current_user.private_token }) do
- %strong
- %i.fa.fa-rss
- News Feed
-
-%hr
-= render 'shared/promo'
+ = render 'shared/promo'
diff --git a/app/views/dashboard/_zero_authorized_projects.html.haml b/app/views/dashboard/_zero_authorized_projects.html.haml
index 5d133cd8285..6e76f95b34e 100644
--- a/app/views/dashboard/_zero_authorized_projects.html.haml
+++ b/app/views/dashboard/_zero_authorized_projects.html.haml
@@ -4,7 +4,7 @@
%div
.dashboard-intro-icon
%i.fa.fa-bookmark-o
- %div
+ .dashboard-intro-text
%p.slead
You don't have access to any projects right now.
%br
@@ -17,28 +17,30 @@
- if current_user.can_create_project?
.link_holder
= link_to new_project_path, class: "btn btn-new" do
- New project »
+ %i.fa.fa-plus
+ New Project
- if current_user.can_create_group?
%hr
%div
.dashboard-intro-icon
%i.fa.fa-users
- %div
+ .dashboard-intro-text
%p.slead
You can create a group for several dependent projects.
%br
Groups are the best way to manage projects and members.
.link_holder
= link_to new_group_path, class: "btn btn-new" do
- New group »
+ %i.fa.fa-plus
+ New Group
-if @publicish_project_count > 0
%hr
%div
.dashboard-intro-icon
%i.fa.fa-globe
- %div
+ .dashboard-intro-text
%p.slead
There are
%strong= @publicish_project_count
@@ -47,4 +49,4 @@
Public projects are an easy way to allow everyone to have read-only access.
.link_holder
= link_to trending_explore_projects_path, class: "btn btn-new" do
- Browse public projects »
+ Browse public projects
diff --git a/app/views/dashboard/issues.atom.builder b/app/views/dashboard/issues.atom.builder
index f5413557783..72e9e361dc3 100644
--- a/app/views/dashboard/issues.atom.builder
+++ b/app/views/dashboard/issues.atom.builder
@@ -1,24 +1,13 @@
xml.instruct!
xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://search.yahoo.com/mrss/" do
xml.title "#{current_user.name} issues"
- xml.link :href => issues_dashboard_url(:atom, :private_token => current_user.private_token), :rel => "self", :type => "application/atom+xml"
- xml.link :href => issues_dashboard_url(:private_token => current_user.private_token), :rel => "alternate", :type => "text/html"
- xml.id issues_dashboard_url(:private_token => current_user.private_token)
+ xml.link href: issues_dashboard_url(:atom, private_token: current_user.private_token), rel: "self", type: "application/atom+xml"
+ xml.link href: issues_dashboard_url(private_token: current_user.private_token), rel: "alternate", type: "text/html"
+ xml.id issues_dashboard_url(private_token: current_user.private_token)
xml.updated @issues.first.created_at.strftime("%Y-%m-%dT%H:%M:%SZ") if @issues.any?
@issues.each do |issue|
- xml.entry do
- xml.id project_issue_url(issue.project, issue)
- xml.link :href => project_issue_url(issue.project, issue)
- xml.title truncate(issue.title, :length => 80)
- xml.updated issue.created_at.strftime("%Y-%m-%dT%H:%M:%SZ")
- xml.media :thumbnail, :width => "40", :height => "40", :url => avatar_icon(issue.author_email)
- xml.author do |author|
- xml.name issue.author_name
- xml.email issue.author_email
- end
- xml.summary issue.title
- end
+ issue_to_atom(xml, issue)
end
end
diff --git a/app/views/dashboard/issues.html.haml b/app/views/dashboard/issues.html.haml
index 7c1f1ddbb80..db19a46cb26 100644
--- a/app/views/dashboard/issues.html.haml
+++ b/app/views/dashboard/issues.html.haml
@@ -5,10 +5,6 @@
List all issues from all projects you have access to.
%hr
-.row
- .fixed.sidebar-expand-button.hidden-lg.hidden-md
- %i.fa.fa-list.fa-2x
- .col-md-3.responsive-side
- = render 'shared/filter', entity: 'issue'
- .col-md-9
- = render 'shared/issues'
+.append-bottom-20
+ = render 'shared/issuable_filter'
+= render 'shared/issues'
diff --git a/app/views/dashboard/merge_requests.html.haml b/app/views/dashboard/merge_requests.html.haml
index c96584c7b6b..97a42461b4e 100644
--- a/app/views/dashboard/merge_requests.html.haml
+++ b/app/views/dashboard/merge_requests.html.haml
@@ -5,10 +5,6 @@
%p.light
List all merge requests from all projects you have access to.
%hr
-.row
- .fixed.sidebar-expand-button.hidden-lg.hidden-md
- %i.fa.fa-list.fa-2x
- .col-md-3.responsive-side
- = render 'shared/filter', entity: 'merge_request'
- .col-md-9
- = render 'shared/merge_requests'
+.append-bottom-20
+ = render 'shared/issuable_filter'
+= render 'shared/merge_requests'
diff --git a/app/views/dashboard/projects.html.haml b/app/views/dashboard/projects.html.haml
index f124c688be1..15db8592547 100644
--- a/app/views/dashboard/projects.html.haml
+++ b/app/views/dashboard/projects.html.haml
@@ -1,73 +1,60 @@
%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
+
+ = link_to new_project_path, class: "btn btn-new pull-right" do
+ %i.fa.fa-plus
+ New Project
+
%p.light
All projects you have access to are listed here. Public projects are not included here unless you are a member
%hr
-.row
- .col-md-3.hidden-sm.hidden-xs.side-filters
- = render "projects_filter"
- .col-md-9
- %ul.bordered-list.my-projects.top-list
- - @projects.each do |project|
- %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
+.side-filters
+ = render "projects_filter"
+.dash-projects
+ %ul.bordered-list.my-projects.top-list
+ - @projects.each do |project|
+ %li.my-project-row
+ %h4.project-title
+ .pull-left
+ = project_icon("#{project.namespace.to_param}/#{project.to_param}", alt: '', class: 'avatar project-avatar s60')
+ .project-access-icon
+ = visibility_level_icon(project.visibility_level)
+ = link_to project_path(project), class: dom_class(project) do
+ %strong= project.name_with_namespace
- - 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.fa.fa-sign-out
- Leave
+ - if project.forked_from_project
+ &nbsp;
+ %small
+ %i.fa.fa-code-fork
+ Forked from:
+ = link_to project.forked_from_project.name_with_namespace, namespace_project_path(project.namespace, project.forked_from_project)
- - if project.forked_from_project
- %small.pull-right
- %i.fa.fa-code-fork
- Forked from:
- = link_to project.forked_from_project.name_with_namespace, project_path(project.forked_from_project)
- .project-info
+ - if current_user.can_leave_project?(project)
.pull-right
- - if project.archived?
- %span.label
- %i.fa.fa-archive
- Archived
- - project.tags.each do |tag|
- %span.label.label-info
- %i.fa.fa-tag
- = tag.name
- - if project.description.present?
- %p= truncate project.description, length: 100
- .last-activity
- %span.light Last activity:
- %span.date= project_last_activity(project)
+ = link_to leave_namespace_project_team_members_path(project.namespace, project), data: { confirm: "Leave project?"}, method: :delete, remote: true, class: "btn-tiny btn remove-row", title: 'Leave project' do
+ %i.fa.fa-sign-out
+ Leave
+
+ .project-info
+ .pull-right
+ - if project.archived?
+ %span.label
+ %i.fa.fa-archive
+ Archived
+ - project.tags.each do |tag|
+ %span.label.label-info
+ %i.fa.fa-tag
+ = tag.name
+ - if project.description.present?
+ %p= truncate project.description, length: 100
+ .last-activity
+ %span.light Last activity:
+ %span.date= project_last_activity(project)
- - if @projects.blank?
- %li
- .nothing-here-block There are no projects here.
- .bottom
- = paginate @projects, theme: "gitlab"
+ - if @projects.blank?
+ %li
+ .nothing-here-block There are no projects here.
+ .bottom
+ = paginate @projects, theme: "gitlab"
diff --git a/app/views/dashboard/show.atom.builder b/app/views/dashboard/show.atom.builder
index f4cf24ccd99..da631ecb33e 100644
--- a/app/views/dashboard/show.atom.builder
+++ b/app/views/dashboard/show.atom.builder
@@ -1,29 +1,12 @@
xml.instruct!
xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://search.yahoo.com/mrss/" do
xml.title "Dashboard feed#{" - #{current_user.name}" if current_user.name.present?}"
- xml.link :href => dashboard_url(:atom), :rel => "self", :type => "application/atom+xml"
- xml.link :href => dashboard_url, :rel => "alternate", :type => "text/html"
+ xml.link href: dashboard_url(:atom), rel: "self", type: "application/atom+xml"
+ xml.link href: dashboard_url, rel: "alternate", type: "text/html"
xml.id projects_url
xml.updated @events.maximum(:updated_at).strftime("%Y-%m-%dT%H:%M:%SZ") if @events.any?
@events.each do |event|
- if event.proper?
- xml.entry do
- event_link = event_feed_url(event)
- event_title = event_feed_title(event)
- event_summary = event_feed_summary(event)
-
- xml.id "tag:#{request.host},#{event.created_at.strftime("%Y-%m-%d")}:#{event.id}"
- xml.link :href => event_link
- xml.title truncate(event_title, :length => 80)
- xml.updated event.created_at.strftime("%Y-%m-%dT%H:%M:%SZ")
- xml.media :thumbnail, :width => "40", :height => "40", :url => avatar_icon(event.author_email)
- xml.author do |author|
- xml.name event.author_name
- xml.email event.author_email
- end
- xml.summary(:type => "xhtml") { |x| x << event_summary unless event_summary.nil? }
- end
- end
+ event_to_atom(xml, event)
end
end
diff --git a/app/views/dashboard/show.html.haml b/app/views/dashboard/show.html.haml
index 10951af6a09..f973f4829a0 100644
--- a/app/views/dashboard/show.html.haml
+++ b/app/views/dashboard/show.html.haml
@@ -2,11 +2,10 @@
.dashboard.row
%section.activities.col-md-8
= render 'activities'
- %aside.side.col-md-4.left.responsive-side
+ %aside.col-md-4
= render 'sidebar'
-
- .fixed.sidebar-expand-button.hidden-lg.hidden-md
- %i.fa.fa-list.fa-2x
+ = link_to '#aside', class: 'show-aside' do
+ %i.fa.fa-angle-left
- else
= render "zero_authorized_projects"
diff --git a/app/views/devise/confirmations/new.html.haml b/app/views/devise/confirmations/new.html.haml
index 8d17f39eba2..970ba147111 100755..100644
--- a/app/views/devise/confirmations/new.html.haml
+++ b/app/views/devise/confirmations/new.html.haml
@@ -7,7 +7,8 @@
= devise_error_messages!
.clearfix.append-bottom-20
= f.email_field :email, placeholder: 'Email', class: "form-control", required: true
- .clearfix.append-bottom-10
+ .clearfix
= f.submit "Resend confirmation instructions", class: 'btn btn-success'
- .login-footer
- = render 'devise/shared/sign_in_link'
+
+.clearfix.prepend-top-20
+ = render 'devise/shared/sign_in_link'
diff --git a/app/views/devise/passwords/edit.html.haml b/app/views/devise/passwords/edit.html.haml
index f6cbf9b82ba..0640739b5d7 100644
--- a/app/views/devise/passwords/edit.html.haml
+++ b/app/views/devise/passwords/edit.html.haml
@@ -6,13 +6,14 @@
.devise-errors
= devise_error_messages!
= f.hidden_field :reset_password_token
- .form-group#password-strength
- = f.password_field :password, class: "form-control top", id: "user_password_recover", placeholder: "New password", required: true
+ %div
+ = f.password_field :password, class: "form-control top", placeholder: "New password", required: true
%div
= f.password_field :password_confirmation, class: "form-control bottom", placeholder: "Confirm new password", required: true
- .clearfix.append-bottom-10
+ .clearfix
= f.submit "Change my password", class: "btn btn-primary"
- .login-footer
- %p
- = link_to "Didn't receive confirmation instructions?", new_confirmation_path(resource_name)
- = render 'devise/shared/sign_in_link'
+
+.clearfix.prepend-top-20
+ %p
+ = link_to "Didn't receive confirmation instructions?", new_confirmation_path(resource_name)
+ = render 'devise/shared/sign_in_link'
diff --git a/app/views/devise/passwords/new.html.haml b/app/views/devise/passwords/new.html.haml
index b8af1b8693a..e8820daf58f 100755..100644
--- a/app/views/devise/passwords/new.html.haml
+++ b/app/views/devise/passwords/new.html.haml
@@ -7,7 +7,8 @@
= devise_error_messages!
.clearfix.append-bottom-20
= f.email_field :email, placeholder: "Email", class: "form-control", required: true
- .clearfix.append-bottom-10
+ .clearfix
= f.submit "Reset password", class: "btn-primary btn"
- .login-footer
- = render 'devise/shared/sign_in_link'
+
+.clearfix.prepend-top-20
+ = render 'devise/shared/sign_in_link'
diff --git a/app/views/devise/registrations/new.html.haml b/app/views/devise/registrations/new.html.haml
index 123de881f59..d3e37f7494c 100644
--- a/app/views/devise/registrations/new.html.haml
+++ b/app/views/devise/registrations/new.html.haml
@@ -1,27 +1,3 @@
-.login-box
- .login-heading
- %h3 Sign up
- .login-body
- = form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f|
- .devise-errors
- = devise_error_messages!
- %div
- = f.text_field :name, class: "form-control top", placeholder: "Name", required: true
- %div
- = f.text_field :username, class: "form-control middle", placeholder: "Username", required: true
- %div
- = f.email_field :email, class: "form-control middle", placeholder: "Email", required: true
- .form-group#password-strength
- = f.password_field :password, class: "form-control middle", id: "user_password_sign_up", placeholder: "Password", required: true
- %div
- = f.password_field :password_confirmation, class: "form-control bottom", placeholder: "Confirm password", required: true
- %div
- = f.submit "Sign up", class: "btn-create btn"
- .login-footer
- %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)
+= render 'devise/shared/signup_box'
+
+= render 'devise/shared/sign_in_link' \ No newline at end of file
diff --git a/app/views/devise/sessions/_new_base.html.haml b/app/views/devise/sessions/_new_base.html.haml
index e819847e5ea..54a39726771 100644
--- a/app/views/devise/sessions/_new_base.html.haml
+++ b/app/views/devise/sessions/_new_base.html.haml
@@ -2,11 +2,11 @@
= 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"}
+ .remember-me.checkbox
+ %label{for: "user_remember_me"}
= f.check_box :remember_me
%span Remember me
- .pull-right
- = link_to "Forgot your password?", new_password_path(resource_name)
+ .pull-right
+ = link_to "Forgot your password?", new_password_path(resource_name)
%div
= f.submit "Sign in", class: "btn btn-save"
diff --git a/app/views/devise/sessions/_new_ldap.html.haml b/app/views/devise/sessions/_new_ldap.html.haml
index bf8a593c254..e986989a728 100644
--- a/app/views/devise/sessions/_new_ldap.html.haml
+++ b/app/views/devise/sessions/_new_ldap.html.haml
@@ -1,5 +1,4 @@
= form_tag(user_omniauth_callback_path(provider), id: 'new_ldap_user' ) do
= 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/
= button_tag "LDAP Sign in", class: "btn-save btn"
diff --git a/app/views/devise/sessions/_oauth_providers.html.haml b/app/views/devise/sessions/_oauth_providers.html.haml
deleted file mode 100644
index 15048a78063..00000000000
--- a/app/views/devise/sessions/_oauth_providers.html.haml
+++ /dev/null
@@ -1,10 +0,0 @@
-- providers = (enabled_oauth_providers - [:ldap])
-- if providers.present?
- .bs-callout.bs-callout-info{:'data-no-turbolink' => 'data-no-turbolink'}
- %span Sign in with: &nbsp;
- - providers.each do |provider|
- %span
- - if default_providers.include?(provider)
- = link_to authbutton(provider, 32), omniauth_authorize_path(resource_name, provider)
- - else
- = link_to provider.to_s.titleize, omniauth_authorize_path(resource_name, provider), class: "btn"
diff --git a/app/views/devise/sessions/new.html.haml b/app/views/devise/sessions/new.html.haml
index ca7e9570b43..fa2460518fc 100644
--- a/app/views/devise/sessions/new.html.haml
+++ b/app/views/devise/sessions/new.html.haml
@@ -1,43 +1,6 @@
-.login-box
- .login-heading
- %h3 Sign in
- .login-body
- - if ldap_enabled?
- %ul.nav.nav-tabs
- - @ldap_servers.each_with_index do |server, i|
- %li{class: (:active if i.zero?)}
- = link_to server['label'], "#tab-#{server['provider_name']}", 'data-toggle' => 'tab'
- - if gitlab_config.signin_enabled
- %li
- = link_to 'Standard', '#tab-signin', 'data-toggle' => 'tab'
- .tab-content
- - @ldap_servers.each_with_index do |server, i|
- %div.tab-pane{id: "tab-#{server['provider_name']}", class: (:active if i.zero?)}
- = render 'devise/sessions/new_ldap', provider: server['provider_name']
- - if gitlab_config.signin_enabled
- %div#tab-signin.tab-pane
- = render 'devise/sessions/new_base'
+%div
+ = render 'devise/shared/signin_box'
- - elsif gitlab_config.signin_enabled
- = render 'devise/sessions/new_base'
- - else
- %div
- No authentication methods configured.
-
- = render 'devise/sessions/oauth_providers' if Gitlab.config.omniauth.enabled && devise_mapping.omniauthable?
-
- .login-footer
- - if gitlab_config.signup_enabled
- %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)
+ - if signup_enabled?
+ .prepend-top-20
+ = render 'devise/shared/signup_box'
diff --git a/app/views/devise/shared/_signin_box.html.haml b/app/views/devise/shared/_signin_box.html.haml
new file mode 100644
index 00000000000..805cf816231
--- /dev/null
+++ b/app/views/devise/shared/_signin_box.html.haml
@@ -0,0 +1,42 @@
+.login-box
+ - if signup_enabled?
+ .login-heading
+ %h3 Existing user? Sign in
+ - else
+ .login-heading
+ %h3 Sign in
+ .login-body
+ - if ldap_enabled?
+ %ul.nav.nav-tabs
+ - @ldap_servers.each_with_index do |server, i|
+ %li{class: (:active if i.zero?)}
+ = link_to server['label'], "#tab-#{server['provider_name']}", 'data-toggle' => 'tab'
+ - if signin_enabled?
+ %li
+ = link_to 'Standard', '#tab-signin', 'data-toggle' => 'tab'
+ .tab-content
+ - @ldap_servers.each_with_index do |server, i|
+ %div.tab-pane{id: "tab-#{server['provider_name']}", class: (:active if i.zero?)}
+ = render 'devise/sessions/new_ldap', provider: server['provider_name']
+ - if signin_enabled?
+ %div#tab-signin.tab-pane
+ = render 'devise/sessions/new_base'
+
+ - elsif signin_enabled?
+ = render 'devise/sessions/new_base'
+ - else
+ %div
+ No authentication methods configured.
+
+- if Gitlab.config.omniauth.enabled && devise_mapping.omniauthable?
+ .clearfix.prepend-top-20
+ %p
+ %span.light
+ Sign in with &nbsp;
+ - providers = additional_providers
+ - providers.each do |provider|
+ %span.light
+ - if default_providers.include?(provider)
+ = link_to authbutton(provider, 32), omniauth_authorize_path(resource_name, provider)
+ - else
+ = link_to provider.to_s.titleize, omniauth_authorize_path(resource_name, provider), class: "btn" \ No newline at end of file
diff --git a/app/views/devise/shared/_signup_box.html.haml b/app/views/devise/shared/_signup_box.html.haml
new file mode 100644
index 00000000000..dcf60c90430
--- /dev/null
+++ b/app/views/devise/shared/_signup_box.html.haml
@@ -0,0 +1,26 @@
+.login-box
+ - if signin_enabled?
+ .login-heading
+ %h3 New user? Create an account
+ - else
+ .login-heading
+ %h3 Create an account
+ .login-body
+ = form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f|
+ .devise-errors
+ = devise_error_messages!
+ %div
+ = f.text_field :name, class: "form-control top", placeholder: "Name", required: true
+ %div
+ = f.text_field :username, class: "form-control middle", placeholder: "Username", required: true
+ %div
+ = f.email_field :email, class: "form-control middle", placeholder: "Email", required: true
+ .form-group.append-bottom-20#password-strength
+ = f.password_field :password, class: "form-control bottom", id: "user_password_sign_up", placeholder: "Password", required: true
+ %div
+ = f.submit "Sign up", class: "btn-create btn"
+
+.clearfix.prepend-top-20
+ %p
+ %span.light Did not receive confirmation email?
+ = link_to "Send again", new_confirmation_path(resource_name) \ No newline at end of file
diff --git a/app/views/doorkeeper/applications/_delete_form.html.haml b/app/views/doorkeeper/applications/_delete_form.html.haml
new file mode 100644
index 00000000000..bf8098f38d0
--- /dev/null
+++ b/app/views/doorkeeper/applications/_delete_form.html.haml
@@ -0,0 +1,4 @@
+- submit_btn_css ||= 'btn btn-link btn-remove btn-small'
+= form_tag oauth_application_path(application) do
+ %input{:name => "_method", :type => "hidden", :value => "delete"}/
+ = submit_tag 'Destroy', onclick: "return confirm('Are you sure?')", class: submit_btn_css \ No newline at end of file
diff --git a/app/views/doorkeeper/applications/_form.html.haml b/app/views/doorkeeper/applications/_form.html.haml
new file mode 100644
index 00000000000..a5fec2fabdb
--- /dev/null
+++ b/app/views/doorkeeper/applications/_form.html.haml
@@ -0,0 +1,24 @@
+= form_for application, url: doorkeeper_submit_path(application), html: {class: 'form-horizontal', role: 'form'} do |f|
+ - if application.errors.any?
+ .alert.alert-danger{"data-alert" => ""}
+ %p Whoops! Check your form for possible errors
+ = content_tag :div, class: "form-group#{' has-error' if application.errors[:name].present?}" do
+ = f.label :name, class: 'col-sm-2 control-label'
+ .col-sm-10
+ = f.text_field :name, class: 'form-control'
+ = doorkeeper_errors_for application, :name
+ = content_tag :div, class: "form-group#{' has-error' if application.errors[:redirect_uri].present?}" do
+ = f.label :redirect_uri, class: 'col-sm-2 control-label'
+ .col-sm-10
+ = f.text_area :redirect_uri, class: 'form-control'
+ = doorkeeper_errors_for application, :redirect_uri
+ %span.help-block
+ Use one line per URI
+ - if Doorkeeper.configuration.native_redirect_uri
+ %span.help-block
+ Use
+ %code= Doorkeeper.configuration.native_redirect_uri
+ for local tests
+ .form-actions
+ = f.submit 'Submit', class: "btn btn-primary wide"
+ = link_to "Cancel", applications_profile_path, class: "btn btn-default"
diff --git a/app/views/doorkeeper/applications/edit.html.haml b/app/views/doorkeeper/applications/edit.html.haml
new file mode 100644
index 00000000000..61584eb9c49
--- /dev/null
+++ b/app/views/doorkeeper/applications/edit.html.haml
@@ -0,0 +1,2 @@
+%h3.page-title Edit application
+= render 'form', application: @application \ No newline at end of file
diff --git a/app/views/doorkeeper/applications/index.html.haml b/app/views/doorkeeper/applications/index.html.haml
new file mode 100644
index 00000000000..e5be4b4bcac
--- /dev/null
+++ b/app/views/doorkeeper/applications/index.html.haml
@@ -0,0 +1,16 @@
+%h3.page-title Your applications
+%p= link_to 'New Application', new_oauth_application_path, class: 'btn btn-success'
+%table.table.table-striped
+ %thead
+ %tr
+ %th Name
+ %th Callback URL
+ %th
+ %th
+ %tbody
+ - @applications.each do |application|
+ %tr{:id => "application_#{application.id}"}
+ %td= link_to application.name, oauth_application_path(application)
+ %td= application.redirect_uri
+ %td= link_to 'Edit', edit_oauth_application_path(application), class: 'btn btn-link'
+ %td= render 'delete_form', application: application \ No newline at end of file
diff --git a/app/views/doorkeeper/applications/new.html.haml b/app/views/doorkeeper/applications/new.html.haml
new file mode 100644
index 00000000000..655845e4af5
--- /dev/null
+++ b/app/views/doorkeeper/applications/new.html.haml
@@ -0,0 +1,2 @@
+%h3.page-title New application
+= render 'form', application: @application \ No newline at end of file
diff --git a/app/views/doorkeeper/applications/show.html.haml b/app/views/doorkeeper/applications/show.html.haml
new file mode 100644
index 00000000000..82e78b4af13
--- /dev/null
+++ b/app/views/doorkeeper/applications/show.html.haml
@@ -0,0 +1,26 @@
+%h3.page-title
+ Application: #{@application.name}
+
+
+%table.table
+ %tr
+ %td
+ Application Id
+ %td
+ %code#application_id= @application.uid
+ %tr
+ %td
+ Secret:
+ %td
+ %code#secret= @application.secret
+
+ %tr
+ %td
+ Callback url
+ %td
+ - @application.redirect_uri.split.each do |uri|
+ %div
+ %span.monospace= uri
+.form-actions
+ = link_to 'Edit', edit_oauth_application_path(@application), class: 'btn btn-primary wide pull-left'
+ = render 'delete_form', application: @application, submit_btn_css: 'btn btn-danger prepend-left-10'
diff --git a/app/views/doorkeeper/authorizations/error.html.haml b/app/views/doorkeeper/authorizations/error.html.haml
new file mode 100644
index 00000000000..7561ec85ed9
--- /dev/null
+++ b/app/views/doorkeeper/authorizations/error.html.haml
@@ -0,0 +1,3 @@
+%h3.page-title An error has occurred
+%main{:role => "main"}
+ %pre= @pre_auth.error_response.body[:error_description] \ No newline at end of file
diff --git a/app/views/doorkeeper/authorizations/new.html.haml b/app/views/doorkeeper/authorizations/new.html.haml
new file mode 100644
index 00000000000..15f9ee266c1
--- /dev/null
+++ b/app/views/doorkeeper/authorizations/new.html.haml
@@ -0,0 +1,28 @@
+%h3.page-title Authorize required
+%main{:role => "main"}
+ %p.h4
+ Authorize
+ %strong.text-info= @pre_auth.client.name
+ to use your account?
+ - if @pre_auth.scopes
+ #oauth-permissions
+ %p This application will be able to:
+ %ul.text-info
+ - @pre_auth.scopes.each do |scope|
+ %li= t scope, scope: [:doorkeeper, :scopes]
+ %hr/
+ .actions
+ = form_tag oauth_authorization_path, method: :post do
+ = hidden_field_tag :client_id, @pre_auth.client.uid
+ = hidden_field_tag :redirect_uri, @pre_auth.redirect_uri
+ = hidden_field_tag :state, @pre_auth.state
+ = hidden_field_tag :response_type, @pre_auth.response_type
+ = hidden_field_tag :scope, @pre_auth.scope
+ = submit_tag "Authorize", class: "btn btn-success wide pull-left"
+ = form_tag oauth_authorization_path, method: :delete do
+ = hidden_field_tag :client_id, @pre_auth.client.uid
+ = hidden_field_tag :redirect_uri, @pre_auth.redirect_uri
+ = hidden_field_tag :state, @pre_auth.state
+ = hidden_field_tag :response_type, @pre_auth.response_type
+ = hidden_field_tag :scope, @pre_auth.scope
+ = submit_tag "Deny", class: "btn btn-danger prepend-left-10" \ No newline at end of file
diff --git a/app/views/doorkeeper/authorizations/show.html.haml b/app/views/doorkeeper/authorizations/show.html.haml
new file mode 100644
index 00000000000..9a402007194
--- /dev/null
+++ b/app/views/doorkeeper/authorizations/show.html.haml
@@ -0,0 +1,3 @@
+%h3.page-title Authorization code:
+%main{:role => "main"}
+ %code#authorization_code= params[:code] \ No newline at end of file
diff --git a/app/views/doorkeeper/authorized_applications/_delete_form.html.haml b/app/views/doorkeeper/authorized_applications/_delete_form.html.haml
new file mode 100644
index 00000000000..5cbb4a70c19
--- /dev/null
+++ b/app/views/doorkeeper/authorized_applications/_delete_form.html.haml
@@ -0,0 +1,4 @@
+- submit_btn_css ||= 'btn btn-link btn-remove'
+= form_tag oauth_authorized_application_path(application) do
+ %input{:name => "_method", :type => "hidden", :value => "delete"}/
+ = submit_tag 'Revoke', onclick: "return confirm('Are you sure?')", class: 'btn btn-link btn-remove btn-small' \ No newline at end of file
diff --git a/app/views/doorkeeper/authorized_applications/index.html.haml b/app/views/doorkeeper/authorized_applications/index.html.haml
new file mode 100644
index 00000000000..814cdc987ef
--- /dev/null
+++ b/app/views/doorkeeper/authorized_applications/index.html.haml
@@ -0,0 +1,16 @@
+%header.page-header
+ %h1 Your authorized applications
+%main{:role => "main"}
+ %table.table.table-striped
+ %thead
+ %tr
+ %th Application
+ %th Created At
+ %th
+ %th
+ %tbody
+ - @applications.each do |application|
+ %tr
+ %td= application.name
+ %td= application.created_at.strftime('%Y-%m-%d %H:%M:%S')
+ %td= render 'delete_form', application: application \ No newline at end of file
diff --git a/app/views/events/_commit.html.haml b/app/views/events/_commit.html.haml
index f0c34def145..c86ce9ae651 100644
--- a/app/views/events/_commit.html.haml
+++ b/app/views/events/_commit.html.haml
@@ -1,5 +1,5 @@
%li.commit
.commit-row-title
- = link_to truncate_sha(commit[:id]), project_commit_path(project, commit[:id]), class: "commit_short_id", alt: ''
+ = link_to truncate_sha(commit[:id]), namespace_project_commit_path(project.namespace, project, commit[:id]), class: "commit_short_id", alt: ''
&nbsp;
= gfm event_commit_title(commit[:message]), project
diff --git a/app/views/events/_event.html.haml b/app/views/events/_event.html.haml
index 61383315373..02b1dec753c 100644
--- a/app/views/events/_event.html.haml
+++ b/app/views/events/_event.html.haml
@@ -3,13 +3,14 @@
.event-item-timestamp
#{time_ago_with_tooltip(event.created_at)}
- = cache event do
+ = cache [event, current_user] do
= image_tag avatar_icon(event.author_email, 24), class: "avatar s24", alt:''
- if event.push?
= render "events/event/push", event: event
- - elsif event.note?
+ - elsif event.commented?
= render "events/event/note", event: event
+ - elsif event.created_project?
+ = render "events/event/created_project", event: event
- else
- = render "events/event/common", event: event
-
+ = render "events/event/common", event: event \ No newline at end of file
diff --git a/app/views/events/_event_last_push.html.haml b/app/views/events/_event_last_push.html.haml
index 4c9a39bcc27..cb40aa9970b 100644
--- a/app/views/events/_event_last_push.html.haml
+++ b/app/views/events/_event_last_push.html.haml
@@ -2,7 +2,7 @@
.event-last-push
.event-last-push-text
%span You pushed to
- = link_to project_commits_path(event.project, event.ref_name) do
+ = link_to namespace_project_commits_path(event.project.namespace, event.project, event.ref_name) do
%strong= event.ref_name
at
%strong= link_to_project event.project
diff --git a/app/views/events/_event_push.atom.haml b/app/views/events/_event_push.atom.haml
index 2b63519edac..0ffd2aa0b98 100644
--- a/app/views/events/_event_push.atom.haml
+++ b/app/views/events/_event_push.atom.haml
@@ -2,7 +2,7 @@
- event.commits.first(15).each do |commit|
%p
%strong= commit[:author][:name]
- = link_to "(##{truncate_sha(commit[:id])})", project_commit_path(event.project, id: commit[:id])
+ = link_to "(##{truncate_sha(commit[:id])})", namespace_project_commit_path(event.project.namespace, event.project, id: commit[:id])
%i
at
= commit[:timestamp].to_time.to_s(:short)
diff --git a/app/views/events/event/_common.html.haml b/app/views/events/event/_common.html.haml
index a9d3adf41df..a39e62e9dac 100644
--- a/app/views/events/event/_common.html.haml
+++ b/app/views/events/event/_common.html.haml
@@ -1,15 +1,17 @@
.event-title
%span.author_name= link_to_author event
- %span.event_label{class: event.action_name}= event_action_name(event)
+ %span.event_label{class: event.action_name}
+ = event_action_name(event)
+
- if event.target
- %strong= link_to "##{event.target_iid}", [event.project, event.target]
- - else
- %strong= gfm event.target_title
- at
+ %strong= link_to "##{event.target_iid}", [event.project.namespace.becomes(Namespace), event.project, event.target]
+ at
+
- if event.project
= link_to_project event.project
- else
= event.project_name
+
- if event.target.respond_to?(:title)
.event-body
.event-note
diff --git a/app/views/events/event/_created_project.html.haml b/app/views/events/event/_created_project.html.haml
new file mode 100644
index 00000000000..3c7153d235f
--- /dev/null
+++ b/app/views/events/event/_created_project.html.haml
@@ -0,0 +1,27 @@
+.event-title
+ %span.author_name= link_to_author event
+ %span.event_label{class: event.action_name}
+ = event_action_name(event)
+
+ - if event.project
+ = link_to_project event.project
+ - else
+ = event.project_name
+
+- if current_user == event.author && !event.project.private? && twitter_sharing_enabled?
+ .event-body
+ .event-note
+ .md
+ %p
+ Congratulations! Why not share your accomplishment with the world?
+
+ %a.twitter-share-button{ |
+ href: "https://twitter.com/share", |
+ "data-url" => event.project.web_url, |
+ "data-text" => "I just created a new project in GitLab! GitLab is version control on your server.", |
+ "data-size" => "medium", |
+ "data-related" => "gitlab", |
+ "data-hashtags" => "gitlab", |
+ "data-count" => "none"}
+ Tweet
+ %script{src: "//platform.twitter.com/widgets.js"} \ No newline at end of file
diff --git a/app/views/events/event/_note.html.haml b/app/views/events/event/_note.html.haml
index 6ec8e54fba5..4ef18c09060 100644
--- a/app/views/events/event/_note.html.haml
+++ b/app/views/events/event/_note.html.haml
@@ -1,6 +1,10 @@
.event-title
%span.author_name= link_to_author event
- %span.event_label commented on #{event_note_title_html(event)} at
+ %span.event_label
+ = event.action_name
+ = event_note_title_html(event)
+ at
+
- if event.project
= link_to_project event.project
- else
@@ -14,9 +18,9 @@
- note = event.target
- if note.attachment.url
- if note.attachment.image?
- = link_to note.attachment.secure_url, target: '_blank' do
- = image_tag note.attachment.secure_url, class: 'note-image-attach'
+ = link_to note.attachment.url, target: '_blank' do
+ = image_tag note.attachment.url, class: 'note-image-attach'
- else
- = link_to note.attachment.secure_url, target: "_blank", class: 'note-file-attach' do
+ = link_to note.attachment.url, target: "_blank", class: 'note-file-attach' do
%i.fa.fa-paperclip
= note.attachment_identifier
diff --git a/app/views/events/event/_push.html.haml b/app/views/events/event/_push.html.haml
index b912b5e092f..489138887ae 100644
--- a/app/views/events/event/_push.html.haml
+++ b/app/views/events/event/_push.html.haml
@@ -1,10 +1,10 @@
.event-title
%span.author_name= link_to_author event
- %span.event_label.pushed #{event.push_action_name} #{event.ref_type}
+ %span.event_label.pushed #{event.action_name} #{event.ref_type}
- if event.rm_ref?
%strong= event.ref_name
- else
- = link_to project_commits_path(event.project, event.ref_name) do
+ = link_to namespace_project_commits_path(event.project.namespace, event.project, event.ref_name) do
%strong= event.ref_name
at
= link_to_project event.project
@@ -21,5 +21,5 @@
%li.commits-stat
- if event.commits_count > 2
%span ... and #{event.commits_count - 2} more commits.
- = link_to project_compare_path(event.project, from: event.commit_from, to: event.commit_to) do
+ = link_to namespace_project_compare_path(event.project.namespace, event.project, from: event.commit_from, to: event.commit_to) do
%strong Compare &rarr; #{truncate_sha(event.commit_from)}...#{truncate_sha(event.commit_to)}
diff --git a/app/views/explore/groups/index.html.haml b/app/views/explore/groups/index.html.haml
index 709d062df83..5cf514927af 100644
--- a/app/views/explore/groups/index.html.haml
+++ b/app/views/explore/groups/index.html.haml
@@ -1,6 +1,7 @@
.clearfix
.pull-left
= form_tag explore_groups_path, method: :get, class: 'form-inline form-tiny' do |f|
+ = hidden_field_tag :sort, @sort
.form-group
= search_field_tag :search, params[:search], placeholder: "Filter by name", class: "form-control search-text-input input-mn-300", id: "groups_search"
.form-group
@@ -11,22 +12,20 @@
%a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"}
%span.light sort:
- if @sort.present?
- = @sort.humanize
+ = sort_options_hash[@sort]
- else
- Name
+ = sort_title_recently_created
%b.caret
%ul.dropdown-menu
%li
- = link_to explore_groups_path(sort: nil) do
- Name
- = link_to explore_groups_path(sort: 'newest') do
- Newest
- = link_to explore_groups_path(sort: 'oldest') do
- Oldest
- = link_to explore_groups_path(sort: 'recently_updated') do
- Recently updated
- = link_to explore_groups_path(sort: 'last_updated') do
- Last updated
+ = link_to explore_groups_path(sort: sort_value_recently_created) do
+ = sort_title_recently_created
+ = link_to explore_groups_path(sort: sort_value_oldest_created) do
+ = sort_title_oldest_created
+ = link_to explore_groups_path(sort: sort_value_recently_updated) do
+ = sort_title_recently_updated
+ = link_to explore_groups_path(sort: sort_value_oldest_updated) do
+ = sort_title_oldest_updated
%hr
diff --git a/app/views/explore/projects/_project.html.haml b/app/views/explore/projects/_project.html.haml
index ffbddbae4d6..d65fb529373 100644
--- a/app/views/explore/projects/_project.html.haml
+++ b/app/views/explore/projects/_project.html.haml
@@ -2,12 +2,10 @@
%h4.project-title
.project-access-icon
= visibility_level_icon(project.visibility_level)
- = link_to project.name_with_namespace, project
-
- - if current_page?(starred_explore_projects_path)
- %strong.pull-right
- %i.fa.fa-star
- = pluralize project.star_count, 'star'
+ = link_to project.name_with_namespace, [project.namespace.becomes(Namespace), project]
+ %span.pull-right
+ %i.fa.fa-star
+ = project.star_count
.project-info
- if project.description.present?
@@ -16,11 +14,11 @@
.repo-info
- unless project.empty_repo?
- = link_to pluralize(project.repository.round_commit_count, 'commit'), project_commits_path(project, project.default_branch)
+ = link_to pluralize(project.repository.round_commit_count, 'commit'), namespace_project_commits_path(project.namespace, project, project.default_branch)
&middot;
- = link_to pluralize(project.repository.branch_names.count, 'branch'), project_branches_path(project)
+ = link_to pluralize(project.repository.branch_names.count, 'branch'), namespace_project_branches_path(project.namespace, project)
&middot;
- = link_to pluralize(project.repository.tag_names.count, 'tag'), project_tags_path(project)
+ = link_to pluralize(project.repository.tag_names.count, 'tag'), namespace_project_tags_path(project.namespace, project)
- else
%i.fa.fa-exclamation-triangle
Empty repository
diff --git a/app/views/explore/projects/index.html.haml b/app/views/explore/projects/index.html.haml
index f797c4e3830..02d02912791 100644
--- a/app/views/explore/projects/index.html.haml
+++ b/app/views/explore/projects/index.html.haml
@@ -11,22 +11,20 @@
%a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"}
%span.light sort:
- if @sort.present?
- = @sort.humanize
+ = sort_options_hash[@sort]
- else
- Name
+ = sort_title_recently_created
%b.caret
%ul.dropdown-menu
%li
- = link_to explore_projects_path(sort: nil) do
- Name
- = link_to explore_projects_path(sort: 'newest') do
- Newest
- = link_to explore_projects_path(sort: 'oldest') do
- Oldest
- = link_to explore_projects_path(sort: 'recently_updated') do
- Recently updated
- = link_to explore_projects_path(sort: 'last_updated') do
- Last updated
+ = link_to explore_projects_path(sort: sort_value_recently_created) do
+ = sort_title_recently_created
+ = link_to explore_projects_path(sort: sort_value_oldest_created) do
+ = sort_title_oldest_created
+ = link_to explore_projects_path(sort: sort_value_recently_updated) do
+ = sort_title_recently_updated
+ = link_to explore_projects_path(sort: sort_value_oldest_updated) do
+ = sort_title_oldest_updated
%hr
.public-projects
diff --git a/app/views/groups/_filter.html.haml b/app/views/groups/_filter.html.haml
deleted file mode 100644
index 393be3f1d12..00000000000
--- a/app/views/groups/_filter.html.haml
+++ /dev/null
@@ -1,12 +0,0 @@
-= form_tag group_filter_path(entity), method: 'get' do
- %fieldset
- %ul.nav.nav-pills.nav-stacked
- %li{class: ("active" if (params[:status] == 'active' || !params[:status]))}
- = link_to group_filter_path(entity, status: 'active') do
- Active
- %li{class: ("active" if params[:status] == 'closed')}
- = link_to group_filter_path(entity, status: 'closed') do
- Closed
- %li{class: ("active" if params[:status] == 'all')}
- = link_to group_filter_path(entity, status: 'all') do
- All
diff --git a/app/views/groups/_new_group_member.html.haml b/app/views/groups/_new_group_member.html.haml
index e590ddbf931..345c0555a36 100644
--- a/app/views/groups/_new_group_member.html.haml
+++ b/app/views/groups/_new_group_member.html.haml
@@ -5,7 +5,11 @@
.form-group
= f.label :access_level, "Group Access", class: 'control-label'
- .col-sm-10= select_tag :access_level, options_for_select(GroupMember.access_level_roles, @users_group.access_level), class: "project-access-select select2"
+ .col-sm-10
+ = select_tag :access_level, options_for_select(GroupMember.access_level_roles, @users_group.access_level), class: "project-access-select select2"
+ .help-block
+ Read more about role permissions
+ %strong= link_to "here", help_page_path("permissions", "permissions"), class: "vlink"
.form-actions
- = f.submit 'Add users into group', class: "btn btn-create"
+ = f.submit 'Add users to group', class: "btn btn-create"
diff --git a/app/views/groups/_projects.html.haml b/app/views/groups/_projects.html.haml
index 2c65b3049e3..b505760fa8f 100644
--- a/app/views/groups/_projects.html.haml
+++ b/app/views/groups/_projects.html.haml
@@ -12,6 +12,8 @@
- projects.each do |project|
%li.project-row
= link_to project_path(project), class: dom_class(project) do
+ .dash-project-avatar
+ = project_icon(project, alt: '', class: 'avatar s40')
.dash-project-access-icon
= visibility_level_icon(project.visibility_level)
%span.str-truncated
diff --git a/app/views/groups/_settings_nav.html.haml b/app/views/groups/_settings_nav.html.haml
index ec1fb4a2c00..e6aee22e529 100644
--- a/app/views/groups/_settings_nav.html.haml
+++ b/app/views/groups/_settings_nav.html.haml
@@ -1,10 +1,11 @@
-%ul.nav.nav-pills.nav-stacked.nav-stacked-menu
+%ul.sidebar-subnav
= nav_link(path: 'groups#edit') do
- = link_to edit_group_path(@group) do
+ = link_to edit_group_path(@group), title: 'Group' do
%i.fa.fa-pencil-square-o
- Group
+ %span
+ Group
= nav_link(path: 'groups#projects') do
- = link_to projects_group_path(@group) do
+ = link_to projects_group_path(@group), title: 'Projects' do
%i.fa.fa-folder
- Projects
-
+ %span
+ Projects
diff --git a/app/views/groups/edit.html.haml b/app/views/groups/edit.html.haml
index eb24fd65d9e..c4eb00e8925 100644
--- a/app/views/groups/edit.html.haml
+++ b/app/views/groups/edit.html.haml
@@ -1,41 +1,37 @@
-.row
- .col-md-2
- = render 'settings_nav'
- .col-md-10
- .panel.panel-default
- .panel-heading
- %strong= @group.name
- group settings:
- .panel-body
- = form_for @group, html: { multipart: true, class: "form-horizontal" }, authenticity_token: true do |f|
- - if @group.errors.any?
- .alert.alert-danger
- %span= @group.errors.full_messages.first
- = render 'shared/group_form', f: f
+.panel.panel-default
+ .panel-heading
+ %strong= @group.name
+ group settings:
+ .panel-body
+ = form_for @group, html: { multipart: true, class: "form-horizontal" }, authenticity_token: true do |f|
+ - if @group.errors.any?
+ .alert.alert-danger
+ %span= @group.errors.full_messages.first
+ = render 'shared/group_form', f: f
- .form-group
- .col-sm-2
- .col-sm-10
- = image_tag group_icon(@group.to_param), alt: '', class: 'avatar s160'
- %p.light
- - if @group.avatar?
- You can change your group avatar here
- - else
- You can upload a group avatar here
- = render 'shared/choose_group_avatar_button', f: f
- - if @group.avatar?
- %hr
- = link_to 'Remove avatar', group_avatar_path(@group.to_param), data: { confirm: "Group avatar will be removed. Are you sure?"}, method: :delete, class: "btn btn-remove btn-small remove-avatar"
+ .form-group
+ .col-sm-2
+ .col-sm-10
+ = image_tag group_icon(@group.to_param), alt: '', class: 'avatar group-avatar s160'
+ %p.light
+ - if @group.avatar?
+ You can change your group avatar here
+ - else
+ You can upload a group avatar here
+ = render 'shared/choose_group_avatar_button', f: f
+ - if @group.avatar?
+ %hr
+ = link_to 'Remove avatar', group_avatar_path(@group.to_param), data: { confirm: "Group avatar will be removed. Are you sure?"}, method: :delete, class: "btn btn-remove btn-small remove-avatar"
- .form-actions
- = f.submit 'Save group', class: "btn btn-save"
+ .form-actions
+ = f.submit 'Save group', class: "btn btn-save"
- .panel.panel-danger
- .panel-heading Remove group
- .panel-body
- %p
- Removing group will cause all child projects and resources to be removed.
- %br
- %strong Removed group can not be restored!
+.panel.panel-danger
+ .panel-heading Remove group
+ .panel-body
+ %p
+ Removing group will cause all child projects and resources to be removed.
+ %br
+ %strong Removed group can not be restored!
- = link_to 'Remove Group', @group, data: {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.atom.builder b/app/views/groups/issues.atom.builder
index f2005193f83..240001967f3 100644
--- a/app/views/groups/issues.atom.builder
+++ b/app/views/groups/issues.atom.builder
@@ -7,18 +7,7 @@ xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://sear
xml.updated @issues.first.created_at.strftime("%Y-%m-%dT%H:%M:%SZ") if @issues.any?
@issues.each do |issue|
- xml.entry do
- xml.id project_issue_url(issue.project, issue)
- xml.link :href => project_issue_url(issue.project, issue)
- xml.title truncate(issue.title, :length => 80)
- xml.updated issue.created_at.strftime("%Y-%m-%dT%H:%M:%SZ")
- xml.media :thumbnail, :width => "40", :height => "40", :url => avatar_icon(issue.author_email)
- xml.author do |author|
- xml.name issue.author_name
- xml.email issue.author_email
- end
- xml.summary issue.title
- end
+ issue_to_atom(xml, issue)
end
end
diff --git a/app/views/groups/issues.html.haml b/app/views/groups/issues.html.haml
index 1932ba2f644..6c0d89c4e7c 100644
--- a/app/views/groups/issues.html.haml
+++ b/app/views/groups/issues.html.haml
@@ -9,10 +9,6 @@
To see all issues you should visit #{link_to 'dashboard', issues_dashboard_path} page.
%hr
-.row
- .fixed.sidebar-expand-button.hidden-lg.hidden-md
- %i.fa.fa-list.fa-2x
- .col-md-3.responsive-side
- = render 'shared/filter', entity: 'issue'
- .col-md-9
- = render 'shared/issues'
+.append-bottom-20
+ = render 'shared/issuable_filter'
+= render 'shared/issues'
diff --git a/app/views/groups/merge_requests.html.haml b/app/views/groups/merge_requests.html.haml
index 86d5acdaa32..1ad74905636 100644
--- a/app/views/groups/merge_requests.html.haml
+++ b/app/views/groups/merge_requests.html.haml
@@ -8,10 +8,6 @@
- if current_user
To see all merge requests you should visit #{link_to 'dashboard', merge_requests_dashboard_path} page.
%hr
-.row
- .fixed.sidebar-expand-button.hidden-lg.hidden-md
- %i.fa.fa-list.fa-2x
- .col-md-3.responsive-side
- = render 'shared/filter', entity: 'merge_request'
- .col-md-9
- = render 'shared/merge_requests'
+.append-bottom-20
+ = render 'shared/issuable_filter'
+= render 'shared/merge_requests'
diff --git a/app/views/groups/milestones/_issue.html.haml b/app/views/groups/milestones/_issue.html.haml
index c95c2e89670..27d0c62df8c 100644
--- a/app/views/groups/milestones/_issue.html.haml
+++ b/app/views/groups/milestones/_issue.html.haml
@@ -2,9 +2,9 @@
%span.milestone-row
- project = issue.project
%strong #{project.name} &middot;
- = link_to [project, issue] do
+ = link_to [project.namespace.becomes(Namespace), project, issue] do
%span.cgray ##{issue.iid}
- = link_to_gfm issue.title, [project, issue], title: issue.title
+ = link_to_gfm issue.title, [project.namespace.becomes(Namespace), project, issue], title: issue.title
.pull-right.assignee-icon
- if issue.assignee
= image_tag avatar_icon(issue.assignee.email, 16), class: "avatar s16"
diff --git a/app/views/groups/milestones/_merge_request.html.haml b/app/views/groups/milestones/_merge_request.html.haml
index e0c903bfdb2..b2d2097dfab 100644
--- a/app/views/groups/milestones/_merge_request.html.haml
+++ b/app/views/groups/milestones/_merge_request.html.haml
@@ -2,9 +2,9 @@
%span.milestone-row
- project = merge_request.project
%strong #{project.name} &middot;
- = link_to [project, merge_request] do
+ = link_to [project.namespace.becomes(Namespace), project, merge_request] do
%span.cgray ##{merge_request.iid}
- = link_to_gfm merge_request.title, [project, merge_request], title: merge_request.title
+ = link_to_gfm merge_request.title, [project.namespace.becomes(Namespace), project, merge_request], title: merge_request.title
.pull-right.assignee-icon
- if merge_request.assignee
= image_tag avatar_icon(merge_request.assignee.email, 16), class: "avatar s16"
diff --git a/app/views/groups/milestones/index.html.haml b/app/views/groups/milestones/index.html.haml
index 2727525f070..7f0b2832cac 100644
--- a/app/views/groups/milestones/index.html.haml
+++ b/app/views/groups/milestones/index.html.haml
@@ -9,42 +9,38 @@
%hr
-.row
- .fixed.sidebar-expand-button.hidden-lg.hidden-md
- %i.fa.fa-list.fa-2x
- .col-md-3.responsive-side
- = render 'groups/filter', entity: 'milestone'
- .col-md-9
- .panel.panel-default
- %ul.well-list
- - if @group_milestones.blank?
- %li
- .nothing-here-block No milestones to show
- - else
- - @group_milestones.each do |milestone|
- %li{class: "milestone milestone-#{milestone.closed? ? 'closed' : 'open'}", id: dom_id(milestone.milestones.first) }
- .pull-right
- - if can?(current_user, :manage_group, @group)
- - if milestone.closed?
- = link_to 'Reopen Milestone', group_milestone_path(@group, milestone.safe_title, title: milestone.title, milestone: {state_event: :activate }), method: :put, class: "btn btn-small btn-grouped btn-reopen"
- - else
- = link_to 'Close Milestone', group_milestone_path(@group, milestone.safe_title, title: milestone.title, milestone: {state_event: :close }), method: :put, class: "btn btn-small btn-close"
- %h4
- = link_to_gfm truncate(milestone.title, length: 100), group_milestone_path(@group, milestone.safe_title, title: milestone.title)
+= render 'shared/milestones_filter'
+.milestones
+ .panel.panel-default
+ %ul.well-list
+ - if @group_milestones.blank?
+ %li
+ .nothing-here-block No milestones to show
+ - else
+ - @group_milestones.each do |milestone|
+ %li{class: "milestone milestone-#{milestone.closed? ? 'closed' : 'open'}", id: dom_id(milestone.milestones.first) }
+ .pull-right
+ - if can?(current_user, :manage_group, @group)
+ - if milestone.closed?
+ = link_to 'Reopen Milestone', group_milestone_path(@group, milestone.safe_title, title: milestone.title, milestone: {state_event: :activate }), method: :put, class: "btn btn-small btn-grouped btn-reopen"
+ - else
+ = link_to 'Close Milestone', group_milestone_path(@group, milestone.safe_title, title: milestone.title, milestone: {state_event: :close }), method: :put, class: "btn btn-small btn-close"
+ %h4
+ = link_to_gfm truncate(milestone.title, length: 100), group_milestone_path(@group, milestone.safe_title, title: milestone.title)
+ %div
%div
- %div
- = link_to group_milestone_path(@group, milestone.safe_title, title: milestone.title) do
- = pluralize milestone.issue_count, 'Issue'
- &nbsp;
- = link_to group_milestone_path(@group, milestone.safe_title, title: milestone.title) do
- = pluralize milestone.merge_requests_count, 'Merge Request'
- &nbsp;
- %span.light #{milestone.percent_complete}% complete
- .progress.progress-info
- .progress-bar{style: "width: #{milestone.percent_complete}%;"}
- %div
- %br
- - milestone.projects.each do |project|
- %span.label.label-default
- = project.name
- = paginate @group_milestones, theme: "gitlab"
+ = link_to group_milestone_path(@group, milestone.safe_title, title: milestone.title) do
+ = pluralize milestone.issue_count, 'Issue'
+ &nbsp;
+ = link_to group_milestone_path(@group, milestone.safe_title, title: milestone.title) do
+ = pluralize milestone.merge_requests_count, 'Merge Request'
+ &nbsp;
+ %span.light #{milestone.percent_complete}% complete
+ .progress.progress-info
+ .progress-bar{style: "width: #{milestone.percent_complete}%;"}
+ %div
+ %br
+ - milestone.projects.each do |project|
+ %span.label.label-default
+ = project.name
+ = paginate @group_milestones, theme: "gitlab"
diff --git a/app/views/groups/milestones/show.html.haml b/app/views/groups/milestones/show.html.haml
index 411d1822be0..e3606d167ad 100644
--- a/app/views/groups/milestones/show.html.haml
+++ b/app/views/groups/milestones/show.html.haml
@@ -1,4 +1,9 @@
-%h3.page-title
+%h4.page-title
+ .issue-box{ class: "issue-box-#{@group_milestone.closed? ? 'closed' : 'open'}" }
+ - if @group_milestone.closed?
+ Closed
+ - else
+ Open
Milestone #{@group_milestone.title}
.pull-right
- if can?(current_user, :manage_group, @group)
@@ -7,46 +12,41 @@
- else
= link_to 'Reopen Milestone', group_milestone_path(@group, @group_milestone.safe_title, title: @group_milestone.title, milestone: {state_event: :activate }), method: :put, class: "btn btn-small btn-grouped btn-reopen"
+%hr
- if (@group_milestone.total_items_count == @group_milestone.closed_items_count) && @group_milestone.active?
.alert.alert-success
%span All issues for this milestone are closed. You may close the milestone now.
-.back-link
- = link_to group_milestones_path(@group) do
- &larr; To milestones list
-
-.issue-box{ class: "issue-box-#{@group_milestone.closed? ? 'closed' : 'open'}" }
- .state.clearfix
- .state-label
- - if @group_milestone.closed?
- Closed
- - else
- Open
-
- %h4.title
- = gfm escape_once(@group_milestone.title)
-
- .description
- - @group_milestone.milestones.each do |milestone|
- %hr
- %h4
- = link_to "#{milestone.project.name} - #{milestone.title}", project_milestone_path(milestone.project, milestone)
- %span.pull-right= milestone.expires_at
+.description
+%table.table
+ %thead
+ %tr
+ %th Project
+ %th Open issues
+ %th State
+ %th Due date
+ - @group_milestone.milestones.each do |milestone|
+ %tr
+ %td
+ = link_to "#{milestone.project.name}", namespace_project_milestone_path(milestone.project.namespace, milestone.project, milestone)
+ %td
+ = milestone.issues.opened.count
+ %td
- if milestone.closed?
- %span.label.label-danger #{milestone.state}
- = preserve do
- - if milestone.description.present?
- = milestone.description
-
- .context
- %p
- Progress:
- #{@group_milestone.closed_items_count} closed
- &ndash;
- #{@group_milestone.open_items_count} open
+ Closed
+ - else
+ Open
+ %td
+ = milestone.expires_at
- .progress.progress-info
- .progress-bar{style: "width: #{@group_milestone.percent_complete}%;"}
+.context
+ %p.lead
+ Progress:
+ #{@group_milestone.closed_items_count} closed
+ &ndash;
+ #{@group_milestone.open_items_count} open
+ .progress.progress-info
+ .progress-bar{style: "width: #{@group_milestone.percent_complete}%;"}
%ul.nav.nav-tabs
%li.active
diff --git a/app/views/groups/projects.html.haml b/app/views/groups/projects.html.haml
index 65a66355c56..8c829654fb0 100644
--- a/app/views/groups/projects.html.haml
+++ b/app/views/groups/projects.html.haml
@@ -1,29 +1,25 @@
-.row
- .col-md-2
- = render 'settings_nav'
- .col-md-10
- .panel.panel-default
- .panel-heading
- %strong= @group.name
- projects:
- - if can? current_user, :manage_group, @group
- .panel-head-actions
- = link_to new_project_path(namespace_id: @group.id), class: "btn btn-new" do
- %i.fa.fa-plus
- New Project
- %ul.well-list
- - @projects.each do |project|
- %li
- .list-item-name
- = visibility_level_icon(project.visibility_level)
- %strong= link_to project.name_with_namespace, project
- %span.label.label-gray
- = repository_size(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, data: { confirm: remove_project_message(project)}, method: :delete, class: "btn btn-small btn-remove"
- - if @projects.blank?
- .nothing-here-block This group has no projects yet
+.panel.panel-default
+ .panel-heading
+ %strong= @group.name
+ projects:
+ - if can? current_user, :manage_group, @group
+ .panel-head-actions
+ = link_to new_project_path(namespace_id: @group.id), class: "btn btn-new" do
+ %i.fa.fa-plus
+ New Project
+ %ul.well-list
+ - @projects.each do |project|
+ %li
+ .list-item-name
+ = visibility_level_icon(project.visibility_level)
+ %strong= link_to project.name_with_namespace, project
+ %span.label.label-gray
+ = repository_size(project)
+ .pull-right
+ = link_to 'Members', namespace_project_team_index_path(project.namespace, project), id: "edit_#{dom_id(project)}", class: "btn btn-small"
+ = link_to 'Edit', edit_namespace_project_path(project.namespace, project), id: "edit_#{dom_id(project)}", class: "btn btn-small"
+ = link_to 'Remove', project, data: { confirm: remove_project_message(project)}, method: :delete, class: "btn btn-small btn-remove"
+ - if @projects.blank?
+ .nothing-here-block This group has no projects yet
- = paginate @projects, theme: "gitlab"
+= paginate @projects, theme: "gitlab"
diff --git a/app/views/groups/show.atom.builder b/app/views/groups/show.atom.builder
index e07bb7d2fb7..c78bd1bd263 100644
--- a/app/views/groups/show.atom.builder
+++ b/app/views/groups/show.atom.builder
@@ -1,28 +1,12 @@
xml.instruct!
xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://search.yahoo.com/mrss/" do
xml.title "Group feed - #{@group.name}"
- xml.link :href => group_path(@group, :atom), :rel => "self", :type => "application/atom+xml"
- xml.link :href => group_path(@group), :rel => "alternate", :type => "text/html"
+ xml.link href: group_path(@group, :atom), rel: "self", type: "application/atom+xml"
+ xml.link href: group_path(@group), rel: "alternate", type: "text/html"
xml.id projects_url
xml.updated @events.maximum(:updated_at).strftime("%Y-%m-%dT%H:%M:%SZ") if @events.any?
@events.each do |event|
- if event.proper?
- xml.entry do
- event_link = event_feed_url(event)
- event_title = event_feed_title(event)
-
- xml.id "tag:#{request.host},#{event.created_at.strftime("%Y-%m-%d")}:#{event.id}"
- xml.link :href => event_link
- xml.title truncate(event_title, :length => 80)
- xml.updated event.created_at.strftime("%Y-%m-%dT%H:%M:%SZ")
- xml.media :thumbnail, :width => "40", :height => "40", :url => avatar_icon(event.author_email)
- xml.author do |author|
- xml.name event.author_name
- xml.email event.author_email
- end
- xml.summary event_title
- end
- end
+ event_to_atom(xml, event)
end
end
diff --git a/app/views/groups/show.html.haml b/app/views/groups/show.html.haml
index d876e87852c..a453889f744 100644
--- a/app/views/groups/show.html.haml
+++ b/app/views/groups/show.html.haml
@@ -1,37 +1,21 @@
.dashboard
- %section.activities.col-md-8.hidden-sm.hidden-xs
- - if current_user
- = render "events/event_last_push", event: @last_push
- = link_to dashboard_path, class: 'btn btn-tiny' do
- &larr; To dashboard
- &nbsp;
- %span.cgray
- Currently you are only seeing events from the
- = @group.name
- group
- %hr
- = render 'shared/event_filter'
- - if @events.any?
+ %div
+ = image_tag group_icon(@group.path), class: "avatar group-avatar s90"
+ .clearfix
+ %h2
+ = @group.name
+ - if @group.description.present?
+ %p
+ = escaped_autolink(@group.description)
+ %hr
+ .row
+ %section.activities.col-md-8
+ - if current_user
+ = render "events/event_last_push", event: @last_push
+ = render 'shared/event_filter'
.content_list
- - else
- .nothing-here-block Project activity will be displayed here
- = spinner
- %aside.side.col-md-4
- .light-well.append-bottom-20
- = image_tag group_icon(@group.path), class: "avatar s90"
- .clearfix.light
- %h3.page-title
- = @group.name
- - if @group.description.present?
- %p
- = escaped_autolink(@group.description)
- = render "projects", projects: @projects
- - if current_user
- .prepend-top-20
- = link_to group_path(@group, { format: :atom, private_token: current_user.private_token }), title: "Feed" do
- %strong
- %i.fa.fa-rss
- News Feed
-
- %hr
- = render 'shared/promo'
+ = spinner
+ %aside.side.col-md-4
+ = render "projects", projects: @projects
+ = link_to '#aside', class: 'show-aside' do
+ %i.fa.fa-angle-left
diff --git a/app/views/help/index.html.haml b/app/views/help/index.html.haml
index 7b8193abfdf..af39dfeac5b 100644
--- a/app/views/help/index.html.haml
+++ b/app/views/help/index.html.haml
@@ -42,3 +42,9 @@
%li
Use
= link_to 'shortcuts', '#', onclick: 'Shortcuts.showHelp(event)'
+ %li
+ Get a support
+ = link_to 'subscription', 'https://about.gitlab.com/pricing/'
+ %li
+ = link_to 'Compare', 'https://about.gitlab.com/features/#compare'
+ GitLab editions
diff --git a/app/views/help/show.html.haml b/app/views/help/show.html.haml
index 67f9cc41cf3..eca34dbff06 100644
--- a/app/views/help/show.html.haml
+++ b/app/views/help/show.html.haml
@@ -1,2 +1,2 @@
.documentation.wiki
- = markdown File.read(Rails.root.join('doc', @category, @file + '.md'))
+ = markdown File.read(Rails.root.join('doc', @category, @file + '.md')).gsub("$your_email", current_user.email)
diff --git a/app/views/import/base/create.js.haml b/app/views/import/base/create.js.haml
new file mode 100644
index 00000000000..8d10722628f
--- /dev/null
+++ b/app/views/import/base/create.js.haml
@@ -0,0 +1,25 @@
+- if @already_been_taken
+ :plain
+ target_field = $("tr#repo_#{@repo_id} .import-target")
+ origin_target = target_field.text()
+ project_name = "#{@project_name}"
+ origin_namespace = "#{@target_namespace}"
+ target_field.empty()
+ target_field.append("<p class='alert alert-danger'>This namespace already been taken! Please choose another one</p>")
+ target_field.append("<input type='text' name='target_namespace' />")
+ target_field.append("/" + project_name)
+ target_field.data("project_name", project_name)
+ target_field.find('input').prop("value", origin_namespace)
+- elsif @access_denied
+ :plain
+ job = $("tr#repo_#{@repo_id}")
+ job.find(".import-actions").html("<p class='alert alert-danger'>Access denied! Please verify you can add deploy keys to this repository.</p>"")
+- else
+ :plain
+ job = $("tr#repo_#{@repo_id}")
+ job.attr("id", "project_#{@project.id}")
+ target_field = job.find(".import-target")
+ target_field.empty()
+ target_field.append('<strong>#{link_to @project.path_with_namespace, [@project.namespace.becomes(Namespace), @project]}</strong>')
+ $("table.import-jobs tbody").prepend(job)
+ job.addClass("active").find(".import-actions").html("<i class='fa fa-spinner fa-spin'></i> started")
diff --git a/app/views/import/bitbucket/status.html.haml b/app/views/import/bitbucket/status.html.haml
new file mode 100644
index 00000000000..bcbbaadf3e0
--- /dev/null
+++ b/app/views/import/bitbucket/status.html.haml
@@ -0,0 +1,46 @@
+%h3.page-title
+ %i.fa.fa-bitbucket
+ Import projects from Bitbucket
+
+%p.light
+ Select projects you want to import.
+%hr
+%p
+ = button_tag 'Import all projects', class: "btn btn-success js-import-all"
+
+%table.table.import-jobs
+ %thead
+ %tr
+ %th From Bitbucket
+ %th To GitLab
+ %th Status
+ %tbody
+ - @already_added_projects.each do |project|
+ %tr{id: "project_#{project.id}", class: "#{project_status_css_class(project.import_status)}"}
+ %td
+ = link_to project.import_source, "https://bitbucket.org/#{project.import_source}", target: "_blank"
+ %td
+ %strong= link_to project.path_with_namespace, [project.namespace.becomes(Namespace), project]
+ %td.job-status
+ - if project.import_status == 'finished'
+ %span.cgreen
+ %i.fa.fa-check
+ done
+ - elsif project.import_status == 'started'
+ %i.fa.fa-spinner.fa-spin
+ started
+ - else
+ = project.human_import_status_name
+
+ - @repos.each do |repo|
+ %tr{id: "repo_#{repo["owner"]}___#{repo["slug"]}"}
+ %td
+ = link_to "#{repo["owner"]}/#{repo["slug"]}", "https://bitbucket.org/#{repo["owner"]}/#{repo["slug"]}", target: "_blank"
+ %td.import-target
+ = "#{repo["owner"]}/#{repo["slug"]}"
+ %td.import-actions.job-status
+ = button_tag "Import", class: "btn js-add-to-import"
+
+:coffeescript
+ $ ->
+ new ImporterStatus("#{jobs_import_bitbucket_path}", "#{import_bitbucket_path}")
diff --git a/app/views/import/github/status.html.haml b/app/views/import/github/status.html.haml
new file mode 100644
index 00000000000..883090a3026
--- /dev/null
+++ b/app/views/import/github/status.html.haml
@@ -0,0 +1,46 @@
+%h3.page-title
+ %i.fa.fa-github
+ Import projects from GitHub
+
+%p.light
+ Select projects you want to import.
+%hr
+%p
+ = button_tag 'Import all projects', class: "btn btn-success js-import-all"
+
+%table.table.import-jobs
+ %thead
+ %tr
+ %th From GitHub
+ %th To GitLab
+ %th Status
+ %tbody
+ - @already_added_projects.each do |project|
+ %tr{id: "project_#{project.id}", class: "#{project_status_css_class(project.import_status)}"}
+ %td
+ = link_to project.import_source, "https://github.com/#{project.import_source}", target: "_blank"
+ %td
+ %strong= link_to project.path_with_namespace, [project.namespace.becomes(Namespace), project]
+ %td.job-status
+ - if project.import_status == 'finished'
+ %span.cgreen
+ %i.fa.fa-check
+ done
+ - elsif project.import_status == 'started'
+ %i.fa.fa-spinner.fa-spin
+ started
+ - else
+ = project.human_import_status_name
+
+ - @repos.each do |repo|
+ %tr{id: "repo_#{repo.id}"}
+ %td
+ = link_to repo.full_name, "https://github.com/#{repo.full_name}", target: "_blank"
+ %td.import-target
+ = repo.full_name
+ %td.import-actions.job-status
+ = button_tag "Import", class: "btn js-add-to-import"
+
+:coffeescript
+ $ ->
+ new ImporterStatus("#{jobs_import_github_path}", "#{import_github_path}")
diff --git a/app/views/import/gitlab/status.html.haml b/app/views/import/gitlab/status.html.haml
new file mode 100644
index 00000000000..41ac073eae1
--- /dev/null
+++ b/app/views/import/gitlab/status.html.haml
@@ -0,0 +1,46 @@
+%h3.page-title
+ %i.fa.fa-heart
+ Import projects from GitLab.com
+
+%p.light
+ Select projects you want to import.
+%hr
+%p
+ = button_tag 'Import all projects', class: "btn btn-success js-import-all"
+
+%table.table.import-jobs
+ %thead
+ %tr
+ %th From GitLab.com
+ %th To this GitLab instance
+ %th Status
+ %tbody
+ - @already_added_projects.each do |project|
+ %tr{id: "project_#{project.id}", class: "#{project_status_css_class(project.import_status)}"}
+ %td
+ = link_to project.import_source, "https://gitlab.com/#{project.import_source}", target: "_blank"
+ %td
+ %strong= link_to project.path_with_namespace, [project.namespace.becomes(Namespace), project]
+ %td.job-status
+ - if project.import_status == 'finished'
+ %span.cgreen
+ %i.fa.fa-check
+ done
+ - elsif project.import_status == 'started'
+ %i.fa.fa-spinner.fa-spin
+ started
+ - else
+ = project.human_import_status_name
+
+ - @repos.each do |repo|
+ %tr{id: "repo_#{repo["id"]}"}
+ %td
+ = link_to repo["path_with_namespace"], "https://gitlab.com/#{repo["path_with_namespace"]}", target: "_blank"
+ %td.import-target
+ = repo["path_with_namespace"]
+ %td.import-actions.job-status
+ = button_tag "Import", class: "btn js-add-to-import"
+
+:coffeescript
+ $ ->
+ new ImporterStatus("#{jobs_import_gitlab_path}", "#{import_gitlab_path}")
diff --git a/app/views/import/gitorious/status.html.haml b/app/views/import/gitorious/status.html.haml
new file mode 100644
index 00000000000..ebe24747a05
--- /dev/null
+++ b/app/views/import/gitorious/status.html.haml
@@ -0,0 +1,46 @@
+%h3.page-title
+ %i.icon-gitorious.icon-gitorious-big
+ Import projects from Gitorious.org
+
+%p.light
+ Select projects you want to import.
+%hr
+%p
+ = button_tag 'Import all projects', class: "btn btn-success js-import-all"
+
+%table.table.import-jobs
+ %thead
+ %tr
+ %th From Gitorious.org
+ %th To GitLab
+ %th Status
+ %tbody
+ - @already_added_projects.each do |project|
+ %tr{id: "project_#{project.id}", class: "#{project_status_css_class(project.import_status)}"}
+ %td
+ = link_to project.import_source, "https://gitorious.org/#{project.import_source}", target: "_blank"
+ %td
+ %strong= link_to project.path_with_namespace, [project.namespace.becomes(Namespace), project]
+ %td.job-status
+ - if project.import_status == 'finished'
+ %span.cgreen
+ %i.fa.fa-check
+ done
+ - elsif project.import_status == 'started'
+ %i.fa.fa-spinner.fa-spin
+ started
+ - else
+ = project.human_import_status_name
+
+ - @repos.each do |repo|
+ %tr{id: "repo_#{repo.id}"}
+ %td
+ = link_to repo.full_name, "https://gitorious.org/#{repo.full_name}", target: "_blank"
+ %td.import-target
+ = repo.full_name
+ %td.import-actions.job-status
+ = button_tag "Import", class: "btn js-add-to-import"
+
+:coffeescript
+ $ ->
+ new ImporterStatus("#{jobs_import_gitorious_path}", "#{import_gitorious_path}")
diff --git a/app/views/layouts/_collapse_button.html.haml b/app/views/layouts/_collapse_button.html.haml
new file mode 100644
index 00000000000..2ed51d87ca1
--- /dev/null
+++ b/app/views/layouts/_collapse_button.html.haml
@@ -0,0 +1,4 @@
+- if nav_menu_collapsed?
+ = link_to icon('angle-right'), '#', class: 'toggle-nav-collapse', title: "Open/Close"
+- else
+ = link_to icon('angle-left'), '#', class: 'toggle-nav-collapse', title: "Open/Close"
diff --git a/app/views/layouts/_head.html.haml b/app/views/layouts/_head.html.haml
index fa6aecb6661..d12145651af 100644
--- a/app/views/layouts/_head.html.haml
+++ b/app/views/layouts/_head.html.haml
@@ -1,12 +1,5 @@
%head
%meta{charset: "utf-8"}
-
- -# Go repository retrieval support
- -# Need to be the fist thing in the head
- -# Since Go is using an XML parser to process HTML5
- -# https://github.com/gitlabhq/gitlabhq/pull/5958#issuecomment-45397555
- - if controller_name == 'projects' && action_name == 'show'
- %meta{name: "go-import", content: "#{@project.web_url_without_protocol} git #{@project.web_url}.git"}
%meta{content: "GitLab Community Edition", name: "description"}
%title
@@ -18,7 +11,8 @@
= javascript_include_tag "application"
= csrf_meta_tags
= include_gon
- %meta{name: 'viewport', content: 'width=device-width, initial-scale=1.0'}
+ %meta{name: 'viewport', content: 'width=device-width, initial-scale=1, maximum-scale=1'}
+ %meta{name: 'theme-color', content: '#474D57'}
= render 'layouts/google_analytics' if extra_config.has_key?('google_analytics_id')
= render 'layouts/piwik' if extra_config.has_key?('piwik_url') && extra_config.has_key?('piwik_site_id')
@@ -29,6 +23,6 @@
= auto_discovery_link_tag :atom, projects_url(:atom, private_token: current_user.private_token), title: "Dashboard feed"
- if @project && !@project.new_record?
- if current_controller?(:tree, :commits)
- = auto_discovery_link_tag(:atom, project_commits_url(@project, @ref, format: :atom, private_token: current_user.private_token), title: "Recent commits to #{@project.name}:#{@ref}")
+ = auto_discovery_link_tag(:atom, namespace_project_commits_url(@project.namespace, @project, @ref, format: :atom, private_token: current_user.private_token), title: "Recent commits to #{@project.name}:#{@ref}")
- if current_controller?(:issues)
- = auto_discovery_link_tag(:atom, project_issues_url(@project, :atom, private_token: current_user.private_token), title: "#{@project.name} issues")
+ = auto_discovery_link_tag(:atom, namespace_project_issues_url(@project.namespace, @project, :atom, private_token: current_user.private_token), title: "#{@project.name} issues")
diff --git a/app/views/layouts/_head_panel.html.haml b/app/views/layouts/_head_panel.html.haml
index 5dcaee2fa02..fc8a487ece7 100644
--- a/app/views/layouts/_head_panel.html.haml
+++ b/app/views/layouts/_head_panel.html.haml
@@ -1,11 +1,9 @@
-%header.navbar.navbar-static-top.navbar-gitlab
+%header.navbar.navbar-fixed-top.navbar-gitlab
.navbar-inner
.container
%div.app_logo
- %span.separator
= link_to root_path, class: "home has_bottom_tooltip", title: "Dashboard" do
- %h1 GITLAB
- %span.separator
+ = brand_header_logo
%h1.title= title
%button.navbar-toggle{"data-target" => ".navbar-collapse", "data-toggle" => "collapse", type: "button"}
@@ -44,5 +42,7 @@
= link_to destroy_user_session_path, class: "logout", method: :delete, title: "Logout", class: 'has_bottom_tooltip', 'data-original-title' => 'Logout' do
%i.fa.fa-sign-out
%li.hidden-xs
- = link_to current_user, class: "profile-pic", id: 'profile-pic' do
- = image_tag avatar_icon(current_user.email, 26), alt: 'User activity'
+ = link_to current_user, class: "profile-pic has_bottom_tooltip", id: 'profile-pic', 'data-original-title' => 'Your profile' do
+ = image_tag avatar_icon(current_user.email, 60), alt: 'User activity'
+
+= render 'shared/outdated_browser'
diff --git a/app/views/layouts/_init_auto_complete.html.haml b/app/views/layouts/_init_auto_complete.html.haml
index 353f7ce34f1..3c58f10e759 100644
--- a/app/views/layouts/_init_auto_complete.html.haml
+++ b/app/views/layouts/_init_auto_complete.html.haml
@@ -1,3 +1,3 @@
:javascript
- GitLab.GfmAutoComplete.dataSource = "#{autocomplete_sources_project_path(@project, type: @noteable.class, type_id: params[:id])}"
+ GitLab.GfmAutoComplete.dataSource = "#{autocomplete_sources_namespace_project_path(@project.namespace, @project, type: @noteable.class, type_id: params[:id])}"
GitLab.GfmAutoComplete.setup();
diff --git a/app/views/layouts/_page.html.haml b/app/views/layouts/_page.html.haml
new file mode 100644
index 00000000000..422966cdc55
--- /dev/null
+++ b/app/views/layouts/_page.html.haml
@@ -0,0 +1,23 @@
+- if defined?(sidebar)
+ .page-with-sidebar{ class: nav_sidebar_class }
+ = render "layouts/broadcast"
+ .sidebar-wrapper
+ = render(sidebar)
+ .collapse-nav
+ = render partial: 'layouts/collapse_button'
+ .content-wrapper
+ .container-fluid
+ .content
+ = render "layouts/flash"
+ .clearfix
+ = yield
+- else
+ .container.navless-container
+ .content
+ = yield
+
+= yield :embedded_scripts
+
+:coffeescript
+ $('.page-sidebar-collapsed .nav-sidebar a').tooltip placement: "right"
+
diff --git a/app/views/layouts/_public_head_panel.html.haml b/app/views/layouts/_public_head_panel.html.haml
index 9bfc14d16c1..bd6bb3c720d 100644
--- a/app/views/layouts/_public_head_panel.html.haml
+++ b/app/views/layouts/_public_head_panel.html.haml
@@ -1,22 +1,22 @@
-%header.navbar.navbar-static-top.navbar-gitlab
+%header.navbar.navbar-fixed-top.navbar-gitlab
.navbar-inner
.container
%div.app_logo
- %span.separator
= link_to explore_root_path, class: "home" do
- %h1 GITLAB
- %span.separator
+ = brand_header_logo
%h1.title= title
%button.navbar-toggle{"data-target" => ".navbar-collapse", "data-toggle" => "collapse", type: "button"}
%span.sr-only Toggle navigation
%i.fa.fa-bars
- .pull-right.hidden-xs
- = link_to "Sign in", new_session_path(:user, redirect_to_referer: 'yes'), class: 'btn btn-sign-in btn-new'
+ - unless current_controller?('sessions')
+ .pull-right.hidden-xs
+ = link_to "Sign in", new_session_path(:user, redirect_to_referer: 'yes'), class: 'btn btn-sign-in btn-new'
- .navbar-collapse.collapse
- %ul.nav.navbar-nav
- %li.visible-xs
- = link_to "Sign in", new_session_path(:user, redirect_to_referer: 'yes')
+ .navbar-collapse.collapse
+ %ul.nav.navbar-nav
+ %li.visible-xs
+ = link_to "Sign in", new_session_path(:user, redirect_to_referer: 'yes')
+= render 'shared/outdated_browser'
diff --git a/app/views/layouts/_search.html.haml b/app/views/layouts/_search.html.haml
index 2460a6a014d..04f79846858 100644
--- a/app/views/layouts/_search.html.haml
+++ b/app/views/layouts/_search.html.haml
@@ -4,7 +4,16 @@
= hidden_field_tag :group_id, @group.try(:id)
- if @project && @project.persisted?
= hidden_field_tag :project_id, @project.id
- = hidden_field_tag :search_code, true
+
+ - if current_controller?(:issues)
+ = hidden_field_tag :scope, 'issues'
+ - elsif current_controller?(:merge_requests)
+ = hidden_field_tag :scope, 'merge_requests'
+ - elsif current_controller?(:wikis)
+ = hidden_field_tag :scope, 'wiki_blobs'
+ - else
+ = hidden_field_tag :search_code, true
+
- if @snippet || @snippets
= hidden_field_tag :snippets, true
= hidden_field_tag :repository_ref, @ref
diff --git a/app/views/layouts/admin.html.haml b/app/views/layouts/admin.html.haml
index 207ab22f4c7..ab84e87c300 100644
--- a/app/views/layouts/admin.html.haml
+++ b/app/views/layouts/admin.html.haml
@@ -1,13 +1,6 @@
!!! 5
%html{ lang: "en"}
= render "layouts/head", title: "Admin area"
- %body{class: "#{app_theme} admin", :'data-page' => body_data_page}
- = render "layouts/broadcast"
- = render "layouts/head_panel", title: "Admin area"
- %nav.main-nav.navbar-collapse.collapse
- .container= render 'layouts/nav/admin'
- .container
- .content
- = render "layouts/flash"
- = yield
- = yield :embedded_scripts
+ %body{class: "#{app_theme} admin", :'data-page' => body_data_page}
+ = render "layouts/head_panel", title: link_to("Admin area", admin_root_path)
+ = render 'layouts/page', sidebar: 'layouts/nav/admin'
diff --git a/app/views/layouts/application.html.haml b/app/views/layouts/application.html.haml
index 7d0819aa93e..6bd8ac4adb8 100644
--- a/app/views/layouts/application.html.haml
+++ b/app/views/layouts/application.html.haml
@@ -1,12 +1,6 @@
!!! 5
%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"
- %nav.main-nav.navbar-collapse.collapse
- .container= render 'layouts/nav/dashboard'
- .container
- .content
- = render "layouts/flash"
- = yield
+ %body{class: "#{app_theme} application", :'data-page' => body_data_page }
+ = render "layouts/head_panel", title: link_to("Dashboard", root_path)
+ = render 'layouts/page', sidebar: 'layouts/nav/dashboard'
diff --git a/app/views/layouts/devise.html.haml b/app/views/layouts/devise.html.haml
index 06de03eadad..6f805f1c9d1 100644
--- a/app/views/layouts/devise.html.haml
+++ b/app/views/layouts/devise.html.haml
@@ -1,35 +1,32 @@
!!! 5
%html{ lang: "en"}
= render "layouts/head"
- %body.ui_basic.login-page
- .container
- .content
- .login-title
- %h1= brand_title
- %hr
- .container
+ %body.ui_mars.login-page.application
+ = render "layouts/broadcast"
+ = render "layouts/public_head_panel", title: ''
+ .container.navless-container
.content
= render "layouts/flash"
- .row
- .col-md-7.brand-holder
+ .row.prepend-top-20
+ .col-sm-5.pull-right
+ = yield
+ .col-sm-7.brand-holder.pull-left
+ %h1
+ = brand_title
- if brand_item
- .brand-image
- = brand_image
- .brand_text
- = brand_text
+ = brand_image
+ = brand_text
- else
- .brand-image.default-brand-image.hidden-sm.hidden-xs
- = image_tag 'brand_logo.png'
- .brand_text.hidden-xs
- %h2 Open source software to collaborate on code
+ %h3 Open source software to collaborate on code
- %p.lead
- Manage git repositories with fine grained access controls that keep your code secure.
- Perform code reviews and enhance collaboration with merge requests.
- Each project can also have an issue tracker and a wiki.
+ %p
+ Manage git repositories with fine grained access controls that keep your code secure.
+ Perform code reviews and enhance collaboration with merge requests.
+ Each project can also have an issue tracker and a wiki.
+
+ - if extra_sign_in_text.present?
+ = markdown(extra_sign_in_text)
- .col-md-5
- = yield
%hr
.container
.footer-links
diff --git a/app/views/layouts/doorkeeper/admin.html.haml b/app/views/layouts/doorkeeper/admin.html.haml
new file mode 100644
index 00000000000..bd9adfab66d
--- /dev/null
+++ b/app/views/layouts/doorkeeper/admin.html.haml
@@ -0,0 +1,22 @@
+!!!
+%html
+ %head
+ %meta{:charset => "utf-8"}
+ %meta{:content => "IE=edge", "http-equiv" => "X-UA-Compatible"}
+ %meta{:content => "width=device-width, initial-scale=1.0", :name => "viewport"}
+ %title Doorkeeper
+ = stylesheet_link_tag "doorkeeper/admin/application"
+ = csrf_meta_tags
+ %body
+ .navbar.navbar-inverse.navbar-fixed-top{:role => "navigation"}
+ .container
+ .navbar-header
+ = link_to 'OAuth2 Provider', oauth_applications_path, class: 'navbar-brand'
+ %ul.nav.navbar-nav
+ = content_tag :li, class: "#{'active' if request.path == oauth_applications_path}" do
+ = link_to 'Applications', oauth_applications_path
+ .container
+ - if flash[:notice].present?
+ .alert.alert-info
+ = flash[:notice]
+ = yield \ No newline at end of file
diff --git a/app/views/layouts/doorkeeper/application.html.haml b/app/views/layouts/doorkeeper/application.html.haml
new file mode 100644
index 00000000000..e5f37fad1f4
--- /dev/null
+++ b/app/views/layouts/doorkeeper/application.html.haml
@@ -0,0 +1,15 @@
+!!!
+%html
+ %head
+ %title OAuth authorize required
+ %meta{:charset => "utf-8"}
+ %meta{:content => "IE=edge", "http-equiv" => "X-UA-Compatible"}
+ %meta{:content => "width=device-width, initial-scale=1.0", :name => "viewport"}
+ = stylesheet_link_tag "doorkeeper/application"
+ = csrf_meta_tags
+ %body
+ #container
+ - if flash[:notice].present?
+ .alert.alert-info
+ = flash[:notice]
+ = yield \ No newline at end of file
diff --git a/app/views/layouts/errors.html.haml b/app/views/layouts/errors.html.haml
index 16df9c10fbb..e51fd4cb820 100644
--- a/app/views/layouts/errors.html.haml
+++ b/app/views/layouts/errors.html.haml
@@ -1,7 +1,7 @@
!!! 5
%html{ lang: "en"}
= render "layouts/head", title: "Error"
- %body{class: "#{app_theme} application"}
+ %body{class: "#{app_theme} application"}
= render "layouts/head_panel", title: "" if current_user
.container.navless-container
= render "layouts/flash"
diff --git a/app/views/layouts/explore.html.haml b/app/views/layouts/explore.html.haml
index d023846c5eb..2bd0b8d85c9 100644
--- a/app/views/layouts/explore.html.haml
+++ b/app/views/layouts/explore.html.haml
@@ -2,12 +2,12 @@
!!! 5
%html{ lang: "en"}
= render "layouts/head", title: page_title
- %body{class: "#{app_theme} application", :'data-page' => body_data_page}
+ %body{class: "#{app_theme} application", :'data-page' => body_data_page}
= render "layouts/broadcast"
- if current_user
- = render "layouts/head_panel", title: page_title
+ = render "layouts/head_panel", title: link_to(page_title, explore_root_path)
- else
- = render "layouts/public_head_panel", title: page_title
+ = render "layouts/public_head_panel", title: link_to(page_title, explore_root_path)
.container.navless-container
.content
.explore-title
diff --git a/app/views/layouts/group.html.haml b/app/views/layouts/group.html.haml
index f22fb236cb5..f4a6bee15f6 100644
--- a/app/views/layouts/group.html.haml
+++ b/app/views/layouts/group.html.haml
@@ -1,12 +1,6 @@
!!! 5
%html{ lang: "en"}
= 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}"
- %nav.main-nav.navbar-collapse.collapse
- .container= render 'layouts/nav/group'
- .container
- .content
- = render "layouts/flash"
- = yield
+ %body{class: "#{app_theme} application", :'data-page' => body_data_page}
+ = render "layouts/head_panel", title: link_to(@group.name, group_path(@group))
+ = render 'layouts/page', sidebar: 'layouts/nav/group'
diff --git a/app/views/layouts/nav/_admin.html.haml b/app/views/layouts/nav/_admin.html.haml
index c57216f01c8..2f38d596c65 100644
--- a/app/views/layouts/nav/_admin.html.haml
+++ b/app/views/layouts/nav/_admin.html.haml
@@ -1,19 +1,60 @@
-%ul
+%ul.nav.nav-sidebar
= nav_link(controller: :dashboard, html_options: {class: 'home'}) do
= link_to admin_root_path, title: "Stats" do
- Overview
+ %i.fa.fa-dashboard
+ %span
+ Overview
= nav_link(controller: :projects) do
- = link_to "Projects", admin_projects_path
+ = link_to admin_namespaces_projects_path, title: 'Projects' do
+ %i.fa.fa-cube
+ %span
+ Projects
= nav_link(controller: :users) do
- = link_to "Users", admin_users_path
+ = link_to admin_users_path, title: 'Users' do
+ %i.fa.fa-user
+ %span
+ Users
= nav_link(controller: :groups) do
- = link_to "Groups", admin_groups_path
+ = link_to admin_groups_path, title: 'Groups' do
+ %i.fa.fa-group
+ %span
+ Groups
= nav_link(controller: :logs) do
- = link_to "Logs", admin_logs_path
+ = link_to admin_logs_path, title: 'Logs' do
+ %i.fa.fa-file-text
+ %span
+ Logs
= nav_link(controller: :broadcast_messages) do
- = link_to "Messages", admin_broadcast_messages_path
+ = link_to admin_broadcast_messages_path, title: 'Broadcast Messages' do
+ %i.fa.fa-bullhorn
+ %span
+ Messages
= nav_link(controller: :hooks) do
- = link_to "Hooks", admin_hooks_path
+ = link_to admin_hooks_path, title: 'Hooks' do
+ %i.fa.fa-external-link
+ %span
+ Hooks
= nav_link(controller: :background_jobs) do
- = link_to "Background Jobs", admin_background_jobs_path
+ = link_to admin_background_jobs_path, title: 'Background Jobs' do
+ %i.fa.fa-cog
+ %span
+ Background Jobs
+
+ = nav_link(controller: :applications) do
+ = link_to admin_applications_path, title: 'Applications' do
+ %i.fa.fa-cloud
+ %span
+ Applications
+
+ = nav_link(controller: :services) do
+ = link_to admin_application_settings_services_path, title: 'Service Templates' do
+ %i.fa.fa-copy
+ %span
+ Service Templates
+
+ = nav_link(controller: :application_settings, html_options: { class: 'separate-item'}) do
+ = link_to admin_application_settings_path, title: 'Settings' do
+ %i.fa.fa-cogs
+ %span
+ Settings
diff --git a/app/views/layouts/nav/_dashboard.html.haml b/app/views/layouts/nav/_dashboard.html.haml
index a6e9772d93f..48c7c999427 100644
--- a/app/views/layouts/nav/_dashboard.html.haml
+++ b/app/views/layouts/nav/_dashboard.html.haml
@@ -1,18 +1,29 @@
-%ul
+%ul.nav.nav-sidebar
= nav_link(path: 'dashboard#show', html_options: {class: 'home'}) do
= link_to root_path, title: 'Home', class: 'shortcuts-activity' do
- Activity
+ %i.fa.fa-dashboard
+ %span
+ Activity
= nav_link(path: 'dashboard#projects') do
- = link_to projects_dashboard_path, class: 'shortcuts-projects' do
- Projects
+ = link_to projects_dashboard_path, title: 'Projects', class: 'shortcuts-projects' do
+ %i.fa.fa-cube
+ %span
+ Projects
= nav_link(path: 'dashboard#issues') do
- = link_to issues_dashboard_path, class: 'shortcuts-issues' do
- Issues
- %span.count= current_user.assigned_issues.opened.count
+ = link_to assigned_issues_dashboard_path, title: 'Issues', class: 'shortcuts-issues' do
+ %i.fa.fa-exclamation-circle
+ %span
+ Issues
+ %span.count= current_user.assigned_issues.opened.count
= nav_link(path: 'dashboard#merge_requests') do
- = link_to merge_requests_dashboard_path, class: 'shortcuts-merge_requests' do
- Merge Requests
- %span.count= current_user.assigned_merge_requests.opened.count
+ = link_to assigned_mrs_dashboard_path, title: 'Merge Requests', class: 'shortcuts-merge_requests' do
+ %i.fa.fa-tasks
+ %span
+ Merge Requests
+ %span.count= current_user.assigned_merge_requests.opened.count
= nav_link(controller: :help) do
- = link_to "Help", help_path
+ = link_to help_path, title: 'Help' do
+ %i.fa.fa-question-circle
+ %span
+ Help
diff --git a/app/views/layouts/nav/_group.html.haml b/app/views/layouts/nav/_group.html.haml
index 9095a843c9f..ddd3df19eec 100644
--- a/app/views/layouts/nav/_group.html.haml
+++ b/app/views/layouts/nav/_group.html.haml
@@ -1,25 +1,42 @@
-%ul
+%ul.nav.nav-sidebar
= nav_link(path: 'groups#show', html_options: {class: 'home'}) do
= link_to group_path(@group), title: "Home" do
- Activity
- = nav_link(controller: [:group, :milestones]) do
- = link_to group_milestones_path(@group) do
- Milestones
+ %i.fa.fa-dashboard
+ %span
+ Activity
+ - if current_user
+ = nav_link(controller: [:group, :milestones]) do
+ = link_to group_milestones_path(@group), title: 'Milestones' do
+ %i.fa.fa-clock-o
+ %span
+ Milestones
= nav_link(path: 'groups#issues') do
- = link_to issues_group_path(@group) do
- Issues
- - if current_user
- %span.count= current_user.assigned_issues.opened.of_group(@group).count
+ = link_to issues_group_path(@group), title: 'Issues' do
+ %i.fa.fa-exclamation-circle
+ %span
+ Issues
+ - if current_user
+ %span.count= Issue.opened.of_group(@group).count
= nav_link(path: 'groups#merge_requests') do
- = link_to merge_requests_group_path(@group) do
- Merge Requests
- - if current_user
- %span.count= current_user.cared_merge_requests.opened.of_group(@group).count
+ = link_to merge_requests_group_path(@group), title: 'Merge Requests' do
+ %i.fa.fa-tasks
+ %span
+ Merge Requests
+ - if current_user
+ %span.count= MergeRequest.opened.of_group(@group).count
= nav_link(path: 'groups#members') do
- = link_to "Members", members_group_path(@group)
+ = link_to members_group_path(@group), title: 'Members' do
+ %i.fa.fa-users
+ %span
+ Members
- if can?(current_user, :manage_group, @group)
- = nav_link(path: 'groups#edit') do
- = link_to edit_group_path(@group), class: "tab " do
- Settings
+ = nav_link(html_options: { class: "#{"active" if group_settings_page?} separate-item" }) do
+ = link_to edit_group_path(@group), title: 'Settings', class: "tab no-highlight" do
+ %i.fa.fa-cogs
+ %span
+ Settings
+ %i.fa.fa-angle-down
+ - if group_settings_page?
+ = render 'groups/settings_nav'
diff --git a/app/views/layouts/nav/_profile.html.haml b/app/views/layouts/nav/_profile.html.haml
index 1de5ee99cf4..0914d2a167a 100644
--- a/app/views/layouts/nav/_profile.html.haml
+++ b/app/views/layouts/nav/_profile.html.haml
@@ -1,26 +1,55 @@
-%ul
+%ul.nav.nav-sidebar
= nav_link(path: 'profiles#show', html_options: {class: 'home'}) do
= link_to profile_path, title: "Profile" do
- Profile
+ %i.fa.fa-user
+ %span
+ Profile
= nav_link(controller: :accounts) do
- = link_to "Account", profile_account_path
+ = link_to profile_account_path, title: 'Account' do
+ %i.fa.fa-gear
+ %span
+ Account
+ = nav_link(path: ['profiles#applications', 'applications#edit', 'applications#show', 'applications#new']) do
+ = link_to applications_profile_path, title: 'Applications' do
+ %i.fa.fa-cloud
+ %span
+ Applications
= nav_link(controller: :emails) do
- = link_to profile_emails_path do
- Emails
- %span.count= current_user.emails.count + 1
+ = link_to profile_emails_path, title: 'Emails' do
+ %i.fa.fa-envelope-o
+ %span
+ Emails
+ %span.count= current_user.emails.count + 1
- unless current_user.ldap_user?
= nav_link(controller: :passwords) do
- = link_to "Password", edit_profile_password_path
+ = link_to edit_profile_password_path, title: 'Password' do
+ %i.fa.fa-lock
+ %span
+ Password
= nav_link(controller: :notifications) do
- = link_to "Notifications", profile_notifications_path
+ = link_to profile_notifications_path, title: 'Notifications' do
+ %i.fa.fa-inbox
+ %span
+ Notifications
+
= nav_link(controller: :keys) do
- = link_to profile_keys_path do
- SSH Keys
- %span.count= current_user.keys.count
+ = link_to profile_keys_path, title: 'SSH Keys' do
+ %i.fa.fa-key
+ %span
+ SSH Keys
+ %span.count= current_user.keys.count
= nav_link(path: 'profiles#design') do
- = link_to "Design", design_profile_path
+ = link_to design_profile_path, title: 'Design' do
+ %i.fa.fa-image
+ %span
+ Design
= nav_link(controller: :groups) do
- = link_to "Groups", profile_groups_path
+ = link_to profile_groups_path, title: 'Groups' do
+ %i.fa.fa-group
+ %span
+ Groups
= nav_link(path: 'profiles#history') do
- = link_to "History", history_profile_path
-
+ = link_to history_profile_path, title: 'History' do
+ %i.fa.fa-history
+ %span
+ History
diff --git a/app/views/layouts/nav/_project.html.haml b/app/views/layouts/nav/_project.html.haml
index 6cb2a82bac8..d340ab1796a 100644
--- a/app/views/layouts/nav/_project.html.haml
+++ b/app/views/layouts/nav/_project.html.haml
@@ -1,45 +1,95 @@
-%ul.project-navigation
- = nav_link(path: 'projects#show', html_options: {class: "home"}) do
- = link_to project_path(@project), title: 'Project', class: 'shortcuts-project' do
- Project
- - if project_nav_tab? :files
- = nav_link(controller: %w(tree blob blame edit_tree new_tree)) do
- = link_to 'Files', project_tree_path(@project, @ref || @repository.root_ref), class: 'shortcuts-tree'
-
- - if project_nav_tab? :commits
- = nav_link(controller: %w(commit commits compare repositories tags branches)) do
- = link_to "Commits", project_commits_path(@project, @ref || @repository.root_ref), class: 'shortcuts-commits'
-
- - if project_nav_tab? :network
- = nav_link(controller: %w(network)) do
- = link_to "Network", project_network_path(@project, @ref || @repository.root_ref), class: 'shortcuts-network'
-
- - if project_nav_tab? :graphs
- = nav_link(controller: %w(graphs)) do
- = link_to "Graphs", project_graph_path(@project, @ref || @repository.root_ref), class: 'shortcuts-graphs'
-
- - if project_nav_tab? :issues
- = nav_link(controller: %w(issues milestones labels)) do
- = link_to url_for_project_issues, class: 'shortcuts-issues' do
- Issues
- - if @project.used_default_issues_tracker?
- %span.count.issue_counter= @project.issues.opened.count
-
- - if project_nav_tab? :merge_requests
- = nav_link(controller: :merge_requests) do
- = link_to project_merge_requests_path(@project), class: 'shortcuts-merge_requests' do
- Merge Requests
- %span.count.merge_counter= @project.merge_requests.opened.count
-
- - if project_nav_tab? :wiki
- = nav_link(controller: :wikis) do
- = link_to 'Wiki', project_wiki_path(@project, :home), class: 'shortcuts-wiki'
-
- - if project_nav_tab? :snippets
- = nav_link(controller: :snippets) do
- = link_to 'Snippets', project_snippets_path(@project), class: 'shortcuts-snippets'
-
- - if project_nav_tab? :settings
- = nav_link(html_options: {class: "#{project_tab_class}"}) do
- = link_to edit_project_path(@project), class: "stat-tab tab " do
- Settings
+%ul.project-navigation.nav.nav-sidebar
+ - if @project_settings_nav
+ = nav_link do
+ = link_to project_path(@project), title: 'Back to project', class: "" do
+ %i.fa.fa-caret-square-o-left
+ %span
+ Back to project
+
+ %li.separate-item
+
+ = render 'projects/settings_nav'
+
+ - else
+ = nav_link(path: 'projects#show', html_options: {class: "home"}) do
+ = link_to project_path(@project), title: 'Project', class: 'shortcuts-project' do
+ %i.fa.fa-dashboard
+ %span
+ Project
+ - if project_nav_tab? :files
+ = nav_link(controller: %w(tree blob blame edit_tree new_tree)) do
+ = link_to namespace_project_tree_path(@project.namespace, @project, @ref || @repository.root_ref), title: 'Files', class: 'shortcuts-tree' do
+ %i.fa.fa-files-o
+ %span
+ Files
+
+ - if project_nav_tab? :commits
+ = nav_link(controller: %w(commit commits compare repositories tags branches)) do
+ = link_to namespace_project_commits_path(@project.namespace, @project, @ref || @repository.root_ref), title: 'Commits', class: 'shortcuts-commits' do
+ %i.fa.fa-history
+ %span
+ Commits
+
+ - if project_nav_tab? :network
+ = nav_link(controller: %w(network)) do
+ = link_to namespace_project_network_path(@project.namespace, @project, @ref || @repository.root_ref), title: 'Network', class: 'shortcuts-network' do
+ %i.fa.fa-code-fork
+ %span
+ Network
+
+ - if project_nav_tab? :graphs
+ = nav_link(controller: %w(graphs)) do
+ = link_to namespace_project_graph_path(@project.namespace, @project, @ref || @repository.root_ref), title: 'Graphs', class: 'shortcuts-graphs' do
+ %i.fa.fa-area-chart
+ %span
+ Graphs
+
+ = nav_link(controller: :milestones) do
+ = link_to namespace_project_milestones_path(@project.namespace, @project), title: 'Milestones' do
+ %i.fa.fa-clock-o
+ %span
+ Milestones
+
+ - if project_nav_tab? :issues
+ = nav_link(controller: :issues) do
+ = link_to url_for_project_issues, title: 'Issues', class: 'shortcuts-issues' do
+ %i.fa.fa-exclamation-circle
+ %span
+ Issues
+ - if @project.default_issues_tracker?
+ %span.count.issue_counter= @project.issues.opened.count
+
+ - if project_nav_tab? :merge_requests
+ = nav_link(controller: :merge_requests) do
+ = link_to namespace_project_merge_requests_path(@project.namespace, @project), title: 'Merge Requests', class: 'shortcuts-merge_requests' do
+ %i.fa.fa-tasks
+ %span
+ Merge Requests
+ %span.count.merge_counter= @project.merge_requests.opened.count
+
+ = nav_link(controller: :labels) do
+ = link_to namespace_project_labels_path(@project.namespace, @project), title: 'Labels' do
+ %i.fa.fa-tags
+ %span
+ Labels
+
+ - if project_nav_tab? :wiki
+ = nav_link(controller: :wikis) do
+ = link_to namespace_project_wiki_path(@project.namespace, @project, :home), title: 'Wiki', class: 'shortcuts-wiki' do
+ %i.fa.fa-book
+ %span
+ Wiki
+
+ - if project_nav_tab? :snippets
+ = nav_link(controller: :snippets) do
+ = link_to namespace_project_snippets_path(@project.namespace, @project), title: 'Snippets', class: 'shortcuts-snippets' do
+ %i.fa.fa-file-text-o
+ %span
+ Snippets
+
+ - if project_nav_tab? :settings
+ = nav_link(html_options: {class: "#{project_tab_class} separate-item"}) do
+ = link_to edit_project_path(@project), title: 'Settings', class: "stat-tab tab no-highlight" do
+ %i.fa.fa-cogs
+ %span
+ Settings
diff --git a/app/views/layouts/navless.html.haml b/app/views/layouts/navless.html.haml
index 2c5fffe384f..4d0278251a6 100644
--- a/app/views/layouts/navless.html.haml
+++ b/app/views/layouts/navless.html.haml
@@ -1,9 +1,9 @@
!!! 5
%html{ lang: "en"}
= render "layouts/head", title: @title
- %body{class: "#{app_theme} application", :'data-page' => body_data_page}
+ %body{class: "#{app_theme} application", :'data-page' => body_data_page}
= render "layouts/broadcast"
- = render "layouts/head_panel", title: @title
+ = render "layouts/head_panel", title: defined?(@title_url) ? link_to(@title, @title_url) : @title
.container.navless-container
.content
= render "layouts/flash"
diff --git a/app/views/layouts/notify.html.haml b/app/views/layouts/notify.html.haml
index ab421d63f1a..8cca80e5248 100644
--- a/app/views/layouts/notify.html.haml
+++ b/app/views/layouts/notify.html.haml
@@ -16,7 +16,7 @@
font-size:small;
color:#777
}
-
+ #{add_email_highlight_css}
%body
%div.content
= yield
@@ -24,7 +24,8 @@
%p
\—
%br
- - if @project
- You're receiving this notification because you are a member of the #{link_to_unless @target_url, @project.name_with_namespace, project_url(@project)} project team.
- if @target_url
#{link_to "View it on GitLab", @target_url}
+ = email_action @target_url
+ - if @project
+ You're receiving this notification because you are a member of the #{link_to_unless @target_url, @project.name_with_namespace, namespace_project_url(@project.namespace, @project)} project team.
diff --git a/app/views/layouts/profile.html.haml b/app/views/layouts/profile.html.haml
index 1d0ab84d26f..2b5be7fc372 100644
--- a/app/views/layouts/profile.html.haml
+++ b/app/views/layouts/profile.html.haml
@@ -1,12 +1,6 @@
!!! 5
%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"
- %nav.main-nav.navbar-collapse.collapse
- .container= render 'layouts/nav/profile'
- .container
- .content
- = render "layouts/flash"
- = yield
+ %body{class: "#{app_theme} profile", :'data-page' => body_data_page}
+ = render "layouts/head_panel", title: link_to("Profile", profile_path)
+ = render 'layouts/page', sidebar: 'layouts/nav/profile'
diff --git a/app/views/layouts/project_settings.html.haml b/app/views/layouts/project_settings.html.haml
index c8b8f4ba971..0a0039dec16 100644
--- a/app/views/layouts/project_settings.html.haml
+++ b/app/views/layouts/project_settings.html.haml
@@ -1,19 +1,8 @@
!!! 5
%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"
+ %body{class: "#{app_theme} project", :'data-page' => body_data_page, :'data-project-id' => @project.id }
= render "layouts/head_panel", title: project_title(@project)
= render "layouts/init_auto_complete"
- - if can?(current_user, :download_code, @project)
- = render 'shared/no_ssh'
- %nav.main-nav.navbar-collapse.collapse
- .container= render 'layouts/nav/project'
- .container
- .content
- = render "layouts/flash"
- .row
- .col-md-2
- = render "projects/settings_nav"
- .col-md-10
- = yield
+ - @project_settings_nav = true
+ = render 'layouts/page', sidebar: 'layouts/nav/project'
diff --git a/app/views/layouts/projects.html.haml b/app/views/layouts/projects.html.haml
index 8ad2f165946..dde0964f47f 100644
--- a/app/views/layouts/projects.html.haml
+++ b/app/views/layouts/projects.html.haml
@@ -1,16 +1,7 @@
!!! 5
%html{ lang: "en"}
= 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"
+ %body{class: "#{app_theme} project", :'data-page' => body_data_page, :'data-project-id' => @project.id }
= render "layouts/head_panel", title: project_title(@project)
= render "layouts/init_auto_complete"
- - if can?(current_user, :download_code, @project)
- = render 'shared/no_ssh'
- %nav.main-nav.navbar-collapse.collapse
- .container= render 'layouts/nav/project'
- .container
- .content
- = render "layouts/flash"
- = yield
- = yield :embedded_scripts
+ = render 'layouts/page', sidebar: 'layouts/nav/project'
diff --git a/app/views/layouts/public_group.html.haml b/app/views/layouts/public_group.html.haml
index a289b784725..b9b1d03e08e 100644
--- a/app/views/layouts/public_group.html.haml
+++ b/app/views/layouts/public_group.html.haml
@@ -1,10 +1,6 @@
!!! 5
%html{ lang: "en"}
= render "layouts/head", title: group_head_title
- %body{class: "#{app_theme} application", :'data-page' => body_data_page}
- = render "layouts/broadcast"
- = render "layouts/public_head_panel", title: "group: #{@group.name}"
- %nav.main-nav.navbar-collapse.collapse
- .container= render 'layouts/nav/group'
- .container
- .content= yield
+ %body{class: "#{app_theme} application", :'data-page' => body_data_page}
+ = render "layouts/public_head_panel", title: link_to(@group.name, group_path(@group))
+ = render 'layouts/page', sidebar: 'layouts/nav/group'
diff --git a/app/views/layouts/public_projects.html.haml b/app/views/layouts/public_projects.html.haml
index 2a9230244f8..04fa7c84e73 100644
--- a/app/views/layouts/public_projects.html.haml
+++ b/app/views/layouts/public_projects.html.haml
@@ -1,10 +1,6 @@
!!! 5
%html{ lang: "en"}
= render "layouts/head", title: @project.name_with_namespace
- %body{class: "#{app_theme} application", :'data-page' => body_data_page}
- = render "layouts/broadcast"
+ %body{class: "#{app_theme} application", :'data-page' => body_data_page}
= render "layouts/public_head_panel", title: project_title(@project)
- %nav.main-nav.navbar-collapse.collapse
- .container= render 'layouts/nav/project'
- .container
- .content= yield
+ = render 'layouts/page', sidebar: 'layouts/nav/project'
diff --git a/app/views/layouts/public_users.html.haml b/app/views/layouts/public_users.html.haml
index 4aa258fea0d..71c16bd1684 100644
--- a/app/views/layouts/public_users.html.haml
+++ b/app/views/layouts/public_users.html.haml
@@ -1,8 +1,6 @@
!!! 5
%html{ lang: "en"}
= render "layouts/head", title: @title
- %body{class: "#{app_theme} application", :'data-page' => body_data_page}
- = render "layouts/broadcast"
- = render "layouts/public_head_panel", title: @title
- .container.navless-container
- .content= yield
+ %body{class: "#{app_theme} application", :'data-page' => body_data_page}
+ = render "layouts/public_head_panel", title: defined?(@title_url) ? link_to(@title, @title_url) : @title
+ = render 'layouts/page'
diff --git a/app/views/layouts/search.html.haml b/app/views/layouts/search.html.haml
index 084ff7ec830..f9d8db06e10 100644
--- a/app/views/layouts/search.html.haml
+++ b/app/views/layouts/search.html.haml
@@ -1,9 +1,9 @@
!!! 5
%html{ lang: "en"}
= render "layouts/head", title: "Search"
- %body{class: "#{app_theme} application", :'data-page' => body_data_page}
+ %body{class: "#{app_theme} application", :'data-page' => body_data_page}
= render "layouts/broadcast"
- = render "layouts/head_panel", title: "Search"
+ = render "layouts/head_panel", title: link_to("Search", search_path)
.container.navless-container
.content
= render "layouts/flash"
diff --git a/app/views/notify/_reassigned_issuable_email.text.erb b/app/views/notify/_reassigned_issuable_email.text.erb
index 817d030c362..855d37429d9 100644
--- a/app/views/notify/_reassigned_issuable_email.text.erb
+++ b/app/views/notify/_reassigned_issuable_email.text.erb
@@ -1,6 +1,6 @@
Reassigned <%= issuable.class.model_name.human.titleize %> <%= issuable.iid %>
-<%= url_for([issuable.project, issuable, {only_path: false}]) %>
+<%= url_for([issuable.project.namespace.becomes(Namespace), issuable.project, issuable, {only_path: false}]) %>
Assignee changed <%= "from #{@previous_assignee.name}" if @previous_assignee -%>
to <%= "#{issuable.assignee_id ? issuable.assignee_name : 'Unassigned'}" %>
diff --git a/app/views/notify/closed_issue_email.text.haml b/app/views/notify/closed_issue_email.text.haml
index 49f160a0d5f..ac703b31edd 100644
--- a/app/views/notify/closed_issue_email.text.haml
+++ b/app/views/notify/closed_issue_email.text.haml
@@ -1,3 +1,3 @@
= "Issue was closed by #{@updated_by.name}"
-Issue ##{@issue.iid}: #{project_issue_url(@issue.project, @issue)}
+Issue ##{@issue.iid}: #{namespace_project_issue_url(@issue.project.namespace, @issue.project, @issue)}
diff --git a/app/views/notify/closed_merge_request_email.text.haml b/app/views/notify/closed_merge_request_email.text.haml
index d6b76e906c5..59db86b08bc 100644
--- a/app/views/notify/closed_merge_request_email.text.haml
+++ b/app/views/notify/closed_merge_request_email.text.haml
@@ -1,6 +1,6 @@
= "Merge Request ##{@merge_request.iid} was closed by #{@updated_by.name}"
-Merge Request url: #{project_merge_request_url(@merge_request.target_project, @merge_request)}
+Merge Request url: #{namespace_project_merge_request_url(@merge_request.target_project.namespace, @merge_request.target_project, @merge_request)}
= merge_path_description(@merge_request, 'to')
diff --git a/app/views/notify/issue_status_changed_email.text.erb b/app/views/notify/issue_status_changed_email.text.erb
index 4200881f7e8..e6ab3fcde77 100644
--- a/app/views/notify/issue_status_changed_email.text.erb
+++ b/app/views/notify/issue_status_changed_email.text.erb
@@ -1,4 +1,4 @@
Issue was <%= @issue_status %> by <%= @updated_by.name %>
-Issue <%= @issue.iid %>: <%= url_for(project_issue_url(@issue.project, @issue)) %>
+Issue <%= @issue.iid %>: <%= url_for(namespace_project_issue_url(@issue.project.namespace, @issue.project, @issue)) %>
diff --git a/app/views/notify/merge_request_status_email.text.haml b/app/views/notify/merge_request_status_email.text.haml
index 8750bf86e2c..b96dd0fd8ab 100644
--- a/app/views/notify/merge_request_status_email.text.haml
+++ b/app/views/notify/merge_request_status_email.text.haml
@@ -1,6 +1,6 @@
= "Merge Request ##{@merge_request.iid} was #{@mr_status} by #{@updated_by.name}"
-Merge Request url: #{project_merge_request_url(@merge_request.target_project, @merge_request)}
+Merge Request url: #{namespace_project_merge_request_url(@merge_request.target_project.namespace, @merge_request.target_project, @merge_request)}
= merge_path_description(@merge_request, 'to')
diff --git a/app/views/notify/merged_merge_request_email.text.haml b/app/views/notify/merged_merge_request_email.text.haml
index 360da60bc3f..9db75bdb19e 100644
--- a/app/views/notify/merged_merge_request_email.text.haml
+++ b/app/views/notify/merged_merge_request_email.text.haml
@@ -1,6 +1,6 @@
= "Merge Request ##{@merge_request.iid} was merged"
-Merge Request Url: #{project_merge_request_url(@merge_request.target_project, @merge_request)}
+Merge Request Url: #{namespace_project_merge_request_url(@merge_request.target_project.namespace, @merge_request.target_project, @merge_request)}
= merge_path_description(@merge_request, 'to')
diff --git a/app/views/notify/new_issue_email.text.erb b/app/views/notify/new_issue_email.text.erb
index d36f54eb1ca..0cc62935498 100644
--- a/app/views/notify/new_issue_email.text.erb
+++ b/app/views/notify/new_issue_email.text.erb
@@ -1,5 +1,5 @@
New Issue was created.
-Issue <%= @issue.iid %>: <%= url_for(project_issue_url(@issue.project, @issue)) %>
+Issue <%= @issue.iid %>: <%= url_for(namespace_project_issue_url(@issue.project.namespace, @issue.project, @issue)) %>
Author: <%= @issue.author_name %>
Asignee: <%= @issue.assignee_name %>
diff --git a/app/views/notify/new_merge_request_email.text.erb b/app/views/notify/new_merge_request_email.text.erb
index 16be4bb619f..f08039ad045 100644
--- a/app/views/notify/new_merge_request_email.text.erb
+++ b/app/views/notify/new_merge_request_email.text.erb
@@ -1,6 +1,6 @@
New Merge Request #<%= @merge_request.iid %>
-<%= url_for(project_merge_request_url(@merge_request.target_project, @merge_request)) %>
+<%= url_for(namespace_project_merge_request_url(@merge_request.target_project.namespace, @merge_request.target_project, @merge_request)) %>
<%= merge_path_description(@merge_request, 'to') %>
Author: <%= @merge_request.author_name %>
diff --git a/app/views/notify/new_ssh_key_email.html.haml b/app/views/notify/new_ssh_key_email.html.haml
index deb0822d8f2..63b0cbbd205 100644
--- a/app/views/notify/new_ssh_key_email.html.haml
+++ b/app/views/notify/new_ssh_key_email.html.haml
@@ -6,5 +6,5 @@
title:
%code= @key.title
%p
- If this key was added in error, you can remove it here:
+ If this key was added in error, you can remove it under
= link_to "SSH Keys", profile_keys_url
diff --git a/app/views/notify/new_ssh_key_email.text.erb b/app/views/notify/new_ssh_key_email.text.erb
index 5f0080c2b76..05b551c89a0 100644
--- a/app/views/notify/new_ssh_key_email.text.erb
+++ b/app/views/notify/new_ssh_key_email.text.erb
@@ -2,6 +2,6 @@ Hi <%= @user.name %>!
A new public key was added to your account:
-title.................. <%= @key.title %>
+Title: <%= @key.title %>
-If this key was added in error, you can remove it here: <%= profile_keys_url %>
+If this key was added in error, you can remove it at <%= profile_keys_url %>
diff --git a/app/views/notify/note_commit_email.text.erb b/app/views/notify/note_commit_email.text.erb
index aab8e5cfb6c..aaeaf5fdf73 100644
--- a/app/views/notify/note_commit_email.text.erb
+++ b/app/views/notify/note_commit_email.text.erb
@@ -1,6 +1,6 @@
New comment for Commit <%= @commit.short_id %>
-<%= url_for(project_commit_url(@note.project, id: @commit.id, anchor: "note_#{@note.id}")) %>
+<%= url_for(namespace_project_commit_url(@note.project.namespace, @note.project, id: @commit.id, anchor: "note_#{@note.id}")) %>
Author: <%= @note.author_name %>
diff --git a/app/views/notify/note_issue_email.text.erb b/app/views/notify/note_issue_email.text.erb
index 8a61f54a337..e33cbcd70f2 100644
--- a/app/views/notify/note_issue_email.text.erb
+++ b/app/views/notify/note_issue_email.text.erb
@@ -1,6 +1,6 @@
New comment for Issue <%= @issue.iid %>
-<%= url_for(project_issue_url(@issue.project, @issue, anchor: "note_#{@note.id}")) %>
+<%= url_for(namespace_project_issue_url(@issue.project.namespace, @issue.project, @issue, anchor: "note_#{@note.id}")) %>
Author: <%= @note.author_name %>
diff --git a/app/views/notify/note_merge_request_email.text.erb b/app/views/notify/note_merge_request_email.text.erb
index 79e72ca16c6..1d1411992a6 100644
--- a/app/views/notify/note_merge_request_email.text.erb
+++ b/app/views/notify/note_merge_request_email.text.erb
@@ -1,6 +1,6 @@
New comment for Merge Request <%= @merge_request.iid %>
-<%= url_for(project_merge_request_url(@merge_request.target_project, @merge_request, anchor: "note_#{@note.id}")) %>
+<%= url_for(namespace_project_merge_request_url(@merge_request.target_project.namespace, @merge_request.target_project, @merge_request, anchor: "note_#{@note.id}")) %>
<%= @note.author_name %>
diff --git a/app/views/notify/project_access_granted_email.html.haml b/app/views/notify/project_access_granted_email.html.haml
index 4596205f39b..dfc30a2d360 100644
--- a/app/views/notify/project_access_granted_email.html.haml
+++ b/app/views/notify/project_access_granted_email.html.haml
@@ -1,5 +1,5 @@
%p
= "You have been granted #{@project_member.human_access} access to project"
%p
- = link_to project_url(@project) do
+ = link_to namespace_project_url(@project.namespace, @project) do
= @project.name_with_namespace
diff --git a/app/views/notify/project_access_granted_email.text.erb b/app/views/notify/project_access_granted_email.text.erb
index de24feb802f..68eb1611ba7 100644
--- a/app/views/notify/project_access_granted_email.text.erb
+++ b/app/views/notify/project_access_granted_email.text.erb
@@ -1,4 +1,4 @@
You have been granted <%= @project_member.human_access %> access to project <%= @project.name_with_namespace %>
-<%= url_for(project_url(@project)) %>
+<%= url_for(namespace_project_url(@project.namespace, @project)) %>
diff --git a/app/views/notify/project_was_moved_email.html.haml b/app/views/notify/project_was_moved_email.html.haml
index fe248584e55..f53de2de287 100644
--- a/app/views/notify/project_was_moved_email.html.haml
+++ b/app/views/notify/project_was_moved_email.html.haml
@@ -2,7 +2,7 @@
Project was moved to another location
%p
The project is now located under
- = link_to project_url(@project) do
+ = link_to namespace_project_url(@project.namespace, @project) do
= @project.name_with_namespace
%p
To update the remote url in your local repository run (for ssh):
diff --git a/app/views/notify/project_was_moved_email.text.erb b/app/views/notify/project_was_moved_email.text.erb
index 664148fb3ba..b3f18b35a4d 100644
--- a/app/views/notify/project_was_moved_email.text.erb
+++ b/app/views/notify/project_was_moved_email.text.erb
@@ -1,7 +1,7 @@
Project was moved to another location
The project is now located under
-<%= project_url(@project) %>
+<%= namespace_project_url(@project.namespace, @project) %>
To update the remote url in your local repository run (for ssh):
diff --git a/app/views/notify/repository_push_email.html.haml b/app/views/notify/repository_push_email.html.haml
index 3cf50bf0826..a45d1dedcd1 100644
--- a/app/views/notify/repository_push_email.html.haml
+++ b/app/views/notify/repository_push_email.html.haml
@@ -1,12 +1,14 @@
-%h3 #{@author.name} pushed to #{@branch} at #{link_to @project.name_with_namespace, project_url(@project)}
+%h3 #{@author.name} pushed to #{@branch} at #{link_to @project.name_with_namespace, namespace_project_url(@project.namespace, @project)}
%h4 Commits:
%ul
- @commits.each do |commit|
%li
- %strong #{link_to commit.short_id, project_commit_url(@project, commit)}
- %span by #{commit.author_name}
+ %strong #{link_to commit.short_id, namespace_project_commit_url(@project.namespace, @project, commit)}
+ %div
+ %span by #{commit.author_name}
+ %i at #{commit.committed_date.strftime("%Y-%m-%dT%H:%M:%SZ")}
%pre #{commit.safe_message}
%h4 Changes:
@@ -21,7 +23,7 @@
= diff.new_path || diff.old_path
%hr
%pre
- = diff.diff
+ = color_email_diff(diff.diff)
%br
- if @compare.timeout
diff --git a/app/views/notify/repository_push_email.text.haml b/app/views/notify/repository_push_email.text.haml
index 6f5f9eda2c5..fa355cb5269 100644
--- a/app/views/notify/repository_push_email.text.haml
+++ b/app/views/notify/repository_push_email.text.haml
@@ -1,9 +1,9 @@
-#{@author.name} pushed to #{@branch} at #{link_to @project.name_with_namespace, project_url(@project)}
+#{@author.name} pushed to #{@branch} at #{link_to @project.name_with_namespace, namespace_project_url(@project.namespace, @project)}
\
Commits:
- @commits.each do |commit|
- #{link_to commit.short_id, project_commit_url(@project, commit)} by #{commit.author_name}
+ #{link_to commit.short_id, namespace_project_commit_url(@project.namespace, @project, commit)} by #{commit.author_name}
#{commit.safe_message}
\- - - - -
\
diff --git a/app/views/profiles/accounts/show.html.haml b/app/views/profiles/accounts/show.html.haml
index a21dcff41c0..f124637c07b 100644
--- a/app/views/profiles/accounts/show.html.haml
+++ b/app/views/profiles/accounts/show.html.haml
@@ -1,5 +1,5 @@
%h3.page-title
- Account settings
+ Account Settings
%p.light
You can change your username and private token here.
- if current_user.ldap_user?
@@ -10,7 +10,7 @@
.account-page
%fieldset.update-token
%legend
- Private token
+ Reset Private token
%div
= form_for @user, url: reset_private_token_profile_path, method: :put do |f|
.data
@@ -25,7 +25,7 @@
- if current_user.private_token
= 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"
+ = f.submit 'Reset private token', 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"
@@ -43,7 +43,7 @@
- if show_profile_username_tab?
%fieldset.update-username
%legend
- Username
+ Change Username
= form_for @user, url: update_username_profile_path, method: :put, remote: true do |f|
%p
Changing your username will change path to all personal projects!
@@ -75,3 +75,4 @@
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, data: { confirm: "REMOVE #{current_user.name}? Are you sure?" }, method: :delete, class: "btn btn-remove"
+
diff --git a/app/views/profiles/applications.html.haml b/app/views/profiles/applications.html.haml
new file mode 100644
index 00000000000..c8c522e9812
--- /dev/null
+++ b/app/views/profiles/applications.html.haml
@@ -0,0 +1,49 @@
+%h3.page-title
+ Application Settings
+%p.light
+ OAuth2 protocol settings below.
+
+%fieldset.oauth-applications
+ %legend Your applications
+ %p= link_to 'New Application', new_oauth_application_path, class: 'btn btn-success'
+ - if @applications.any?
+ %table.table.table-striped
+ %thead
+ %tr
+ %th Name
+ %th Callback URL
+ %th Clients
+ %th
+ %th
+ %tbody
+ - @applications.each do |application|
+ %tr{:id => "application_#{application.id}"}
+ %td= link_to application.name, oauth_application_path(application)
+ %td
+ - application.redirect_uri.split.each do |uri|
+ %div= uri
+ %td= application.access_tokens.count
+ %td= link_to 'Edit', edit_oauth_application_path(application), class: 'btn btn-link btn-small'
+ %td= render 'doorkeeper/applications/delete_form', application: application
+
+%fieldset.oauth-authorized-applications.prepend-top-20
+ %legend Authorized applications
+
+ - if @authorized_tokens.any?
+ %table.table.table-striped
+ %thead
+ %tr
+ %th Name
+ %th Authorized At
+ %th Scope
+ %th
+ %tbody
+ - @authorized_apps.each do |app|
+ - token = app.authorized_tokens.order('created_at desc').first
+ %tr{:id => "application_#{app.id}"}
+ %td= app.name
+ %td= token.created_at
+ %td= token.scopes
+ %td= render 'doorkeeper/authorized_applications/delete_form', application: app
+ - else
+ %p.light You dont have any authorized applications
diff --git a/app/views/profiles/design.html.haml b/app/views/profiles/design.html.haml
index 0d8075b7d43..8d09595fd4f 100644
--- a/app/views/profiles/design.html.haml
+++ b/app/views/profiles/design.html.haml
@@ -1,7 +1,7 @@
%h3.page-title
- My appearance settings
+ Design Settings
%p.light
- Appearance settings saved to your profile and available across all devices
+ Appearance settings will be saved to your profile and made available across all devices.
%hr
= form_for @user, url: profile_path, remote: true, method: :put do |f|
diff --git a/app/views/profiles/emails/index.html.haml b/app/views/profiles/emails/index.html.haml
index ca980db2f3c..3bbad6fdf7b 100644
--- a/app/views/profiles/emails/index.html.haml
+++ b/app/views/profiles/emails/index.html.haml
@@ -1,9 +1,13 @@
%h3.page-title
- My email addresses
+ Email Settings
%p.light
Your
%b Primary Email
- will be used for account notifications, avatar detection and web based operations, such as edits and merges.
+ will be used for avatar detection and web based operations, such as edits and merges.
+ %br
+ Your
+ %b Notification Email
+ will be used for account notifications.
%br
All email addresses will be used to identify your commits.
@@ -30,4 +34,4 @@
.col-sm-10
= f.text_field :email, class: 'form-control'
.form-actions
- = f.submit 'Add', class: 'btn btn-create'
+ = f.submit 'Add email address', class: 'btn btn-create'
diff --git a/app/views/profiles/groups/index.html.haml b/app/views/profiles/groups/index.html.haml
index e9ffca8faf4..daf76636ff2 100644
--- a/app/views/profiles/groups/index.html.haml
+++ b/app/views/profiles/groups/index.html.haml
@@ -1,12 +1,12 @@
%h3.page-title
- Group membership
+ Group Membership
- if current_user.can_create_group?
%span.pull-right
= link_to new_group_path, class: "btn btn-new" do
%i.fa.fa-plus
New Group
%p.light
- Group members have access to all a group's projects
+ Group members have access to all group projects.
%hr
.panel.panel-default
.panel-heading
diff --git a/app/views/profiles/history.html.haml b/app/views/profiles/history.html.haml
index 3951c47b5f2..9cafe03b8b3 100644
--- a/app/views/profiles/history.html.haml
+++ b/app/views/profiles/history.html.haml
@@ -1,7 +1,7 @@
%h3.page-title
- Account history
+ My Account History
%p.light
- All events created by your account are listed here
+ All events created by your account are listed below.
%hr
.profile_history
= render @events
diff --git a/app/views/profiles/keys/_key.html.haml b/app/views/profiles/keys/_key.html.haml
index 81411a7565e..8892302e25d 100644
--- a/app/views/profiles/keys/_key.html.haml
+++ b/app/views/profiles/keys/_key.html.haml
@@ -1,9 +1,12 @@
-%li
- = link_to profile_key_path(key) do
- %strong= key.title
- %span
- (#{key.fingerprint})
- %span.cgray
- added #{time_ago_with_tooltip(key.created_at)}
-
- = link_to 'Remove', profile_key_path(key), data: { confirm: 'Are you sure?'}, method: :delete, class: "btn btn-small btn-remove delete-key pull-right"
+%tr
+ %td
+ = link_to path_to_key(key, is_admin) do
+ %strong= key.title
+ %td
+ %span
+ (#{key.fingerprint})
+ %td
+ %span.cgray
+ added #{time_ago_with_tooltip(key.created_at)}
+ %td
+ = link_to 'Remove', path_to_key(key, is_admin), data: { confirm: 'Are you sure?'}, method: :delete, class: "btn btn-small btn-remove delete-key pull-right"
diff --git a/app/views/profiles/keys/_key_details.html.haml b/app/views/profiles/keys/_key_details.html.haml
new file mode 100644
index 00000000000..8bac22a2e1a
--- /dev/null
+++ b/app/views/profiles/keys/_key_details.html.haml
@@ -0,0 +1,22 @@
+- is_admin = defined?(admin) ? true : false
+.row
+ .col-md-4
+ .panel.panel-default
+ .panel-heading
+ SSH Key
+ %ul.well-list
+ %li
+ %span.light Title:
+ %strong= @key.title
+ %li
+ %span.light Created on:
+ %strong= @key.created_at.stamp("Aug 21, 2011")
+
+ .col-md-8
+ %p
+ %span.light Fingerprint:
+ %strong= @key.fingerprint
+ %pre.well-pre
+ = @key.key
+ .pull-right
+ = link_to 'Remove', path_to_key(@key, is_admin), data: {confirm: 'Are you sure?'}, method: :delete, class: "btn btn-remove delete-key"
diff --git a/app/views/profiles/keys/_key_table.html.haml b/app/views/profiles/keys/_key_table.html.haml
new file mode 100644
index 00000000000..ef0075aad3b
--- /dev/null
+++ b/app/views/profiles/keys/_key_table.html.haml
@@ -0,0 +1,19 @@
+- is_admin = defined?(admin) ? true : false
+.panel.panel-default
+ - if @keys.any?
+ %table.table
+ %thead.panel-heading
+ %tr
+ %th Title
+ %th Fingerprint
+ %th Added at
+ %th
+ %tbody
+ - @keys.each do |key|
+ = render 'profiles/keys/key', key: key, is_admin: is_admin
+ - else
+ .nothing-here-block
+ - if is_admin
+ User has no ssh keys
+ - else
+ There are no SSH keys with access to your account.
diff --git a/app/views/profiles/keys/index.html.haml b/app/views/profiles/keys/index.html.haml
index a322f82f236..965d5e032f9 100644
--- a/app/views/profiles/keys/index.html.haml
+++ b/app/views/profiles/keys/index.html.haml
@@ -1,22 +1,12 @@
%h3.page-title
- My SSH keys
+ SSH Keys Settings
.pull-right
= link_to "Add SSH Key", new_profile_key_path, class: "btn btn-new"
%p.light
- SSH keys allow you to establish a secure connection between your computer and GitLab
+ My SSH keys: #{@keys.count}
%br
Before you can add an SSH key you need to
- = link_to "generate it", help_page_path("ssh", "ssh")
+ = link_to "generate it.", help_page_path("ssh", "README")
%hr
-
-.panel.panel-default
- .panel-heading
- SSH Keys (#{@keys.count})
- %ul.well-list#keys-table
- = render @keys
- - if @keys.blank?
- %li
- .nothing-here-block There are no SSH keys with access to your account.
-
-
+= render 'key_table'
diff --git a/app/views/profiles/keys/new.html.haml b/app/views/profiles/keys/new.html.haml
index c02b47b0ad5..ccec716d0c6 100644
--- a/app/views/profiles/keys/new.html.haml
+++ b/app/views/profiles/keys/new.html.haml
@@ -8,9 +8,9 @@
$('#key_key').on('focusout', function(){
var title = $('#key_title'),
val = $('#key_key').val(),
- key_mail = val.match(/([a-zA-Z0-9._-]+@[a-zA-Z0-9._-]+|\.[a-zA-Z0-9._-]+)/gi);
+ comment = val.match(/^\S+ \S+ (.+)$/);
- if( key_mail && key_mail.length > 0 && title.val() == '' ){
- $('#key_title').val( key_mail );
+ if( comment && comment.length > 1 && title.val() == '' ){
+ $('#key_title').val( comment[1] );
}
});
diff --git a/app/views/profiles/keys/show.html.haml b/app/views/profiles/keys/show.html.haml
index c4fc1bb269c..cfd53298962 100644
--- a/app/views/profiles/keys/show.html.haml
+++ b/app/views/profiles/keys/show.html.haml
@@ -1,22 +1 @@
-.row
- .col-md-4
- .panel.panel-default
- .panel-heading
- SSH Key
- %ul.well-list
- %li
- %span.light Title:
- %strong= @key.title
- %li
- %span.light Created on:
- %strong= @key.created_at.stamp("Aug 21, 2011")
-
- .col-md-8
- %p
- %span.light Fingerprint:
- %strong= @key.fingerprint
- %pre.well-pre
- = @key.key
-
-.pull-right
- = link_to 'Remove', profile_key_path(@key), data: {confirm: 'Are you sure?'}, method: :delete, class: "btn btn-remove delete-key"
+= render "key_details"
diff --git a/app/views/profiles/notifications/show.html.haml b/app/views/profiles/notifications/show.html.haml
index a044fad8fa3..e3cd323927e 100644
--- a/app/views/profiles/notifications/show.html.haml
+++ b/app/views/profiles/notifications/show.html.haml
@@ -1,33 +1,56 @@
%h3.page-title
- Notifications settings
+ Notifications Settings
%p.light
- GitLab uses the email specified in your profile for notifications
+ These are your global notification settings.
%hr
-= form_tag profile_notifications_path, method: :put, remote: true, class: 'update-notifications form-horizontal global-notifications-form' do
+
+= form_for @user, url: profile_notifications_path, method: :put, html: { class: 'update-notifications form-horizontal global-notifications-form' } do |f|
+ -if @user.errors.any?
+ %div.alert.alert-danger
+ %ul
+ - @user.errors.full_messages.each do |msg|
+ %li= msg
+
= hidden_field_tag :notification_type, 'global'
- = label_tag :notification_level, 'Notification level', class: 'control-label'
- .col-sm-10
- .radio
- = label_tag nil, class: '' do
- = radio_button_tag :notification_level, Notification::N_DISABLED, @notification.disabled?, class: 'trigger-submit'
- .level-title
- Disabled
- %p You will not get any notifications via email
-
- .radio
- = label_tag nil, class: '' do
- = radio_button_tag :notification_level, Notification::N_PARTICIPATING, @notification.participating?, class: 'trigger-submit'
- .level-title
- Participating
- %p You will only receive notifications from related resources (e.g. from your commits or assigned issues)
-
- .radio
- = label_tag nil, class: '' do
- = radio_button_tag :notification_level, Notification::N_WATCH, @notification.watch?, class: 'trigger-submit'
- .level-title
- Watch
- %p You will receive all notifications from projects in which you participate
+ .form-group
+ = f.label :notification_email, class: "control-label"
+ .col-sm-10
+ = f.select :notification_email, @user.all_emails, { include_blank: false }, class: "form-control"
+
+ .form-group
+ = f.label :notification_level, class: 'control-label'
+ .col-sm-10
+ .radio
+ = f.label :notification_level, value: Notification::N_DISABLED do
+ = f.radio_button :notification_level, Notification::N_DISABLED
+ .level-title
+ Disabled
+ %p You will not get any notifications via email
+
+ .radio
+ = f.label :notification_level, value: Notification::N_MENTION do
+ = f.radio_button :notification_level, Notification::N_MENTION
+ .level-title
+ Mention
+ %p You will receive notifications only for comments in which you were @mentioned
+
+ .radio
+ = f.label :notification_level, value: Notification::N_PARTICIPATING do
+ = f.radio_button :notification_level, Notification::N_PARTICIPATING
+ .level-title
+ Participating
+ %p You will only receive notifications from related resources (e.g. from your commits or assigned issues)
+
+ .radio
+ = f.label :notification_level, value: Notification::N_WATCH do
+ = f.radio_button :notification_level, Notification::N_WATCH
+ .level-title
+ Watch
+ %p You will receive all notifications from projects in which you participate
+
+ .form-actions
+ = f.submit 'Save changes', class: "btn btn-save"
.clearfix
%hr
@@ -36,7 +59,7 @@
%p
You can also specify notification level per group or per project.
%br
- By default all projects and groups uses notification level set above.
+ By default, all projects and groups will use the notification level set above.
%h4 Groups:
%ul.bordered-list
- @group_members.each do |users_group|
@@ -45,7 +68,7 @@
.col-md-6
%p
- To specify notification level per project of a group you belong to,
+ To specify the notification level per project of a group you belong to,
%br
you need to be a member of the project itself, not only its group.
%h4 Projects:
diff --git a/app/views/profiles/passwords/edit.html.haml b/app/views/profiles/passwords/edit.html.haml
index 425200ff523..3b1ebbfaf59 100644
--- a/app/views/profiles/passwords/edit.html.haml
+++ b/app/views/profiles/passwords/edit.html.haml
@@ -1,30 +1,35 @@
-%h3.page-title Password
+%h3.page-title Password Settings
%p.light
- Change your password or recover your current one.
+ - if @user.password_automatically_set?
+ Set your password.
+ - else
+ Change your password or recover your current one.
%hr
.update-password
= 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
+ - unless @user.password_automatically_set?
+ You must provide current password in order to change it.
+ %br
+ After a successful password update, you will be redirected to the login page where you can log in with your new password.
-if @user.errors.any?
.alert.alert-danger
%ul
- @user.errors.full_messages.each do |msg|
%li= msg
- .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
+ - unless @user.password_automatically_set?
+ .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
.form-group
= f.label :password, 'New password', class: 'control-label'
.col-sm-10
- = f.password_field :password, required: true, class: 'form-control', id: 'user_password_profile'
+ = f.password_field :password, required: true, class: 'form-control'
.form-group
= f.label :password_confirmation, class: 'control-label'
.col-sm-10
diff --git a/app/views/profiles/passwords/new.html.haml b/app/views/profiles/passwords/new.html.haml
index 42d2d0db29c..8bed6e0dbee 100644
--- a/app/views/profiles/passwords/new.html.haml
+++ b/app/views/profiles/passwords/new.html.haml
@@ -10,13 +10,14 @@
%ul
- @user.errors.full_messages.each do |msg|
%li= msg
-
- .form-group
- = f.label :current_password, class: 'control-label'
- .col-sm-10= f.password_field :current_password, required: true, class: 'form-control'
+
+ - unless @user.password_automatically_set?
+ .form-group
+ = f.label :current_password, class: 'control-label'
+ .col-sm-10= f.password_field :current_password, required: true, class: 'form-control'
.form-group
= f.label :password, class: 'control-label'
- .col-sm-10= f.password_field :password, required: true, class: 'form-control', id: 'user_password_profile'
+ .col-sm-10= f.password_field :password, required: true, class: 'form-control'
.form-group
= f.label :password_confirmation, class: 'control-label'
.col-sm-10
diff --git a/app/views/profiles/show.html.haml b/app/views/profiles/show.html.haml
index d6b52f86154..b2808c46c00 100644
--- a/app/views/profiles/show.html.haml
+++ b/app/views/profiles/show.html.haml
@@ -1,7 +1,7 @@
%h3.page-title
- Profile settings
+ Profile Settings
%p.light
- This information appears on your profile.
+ This information will appear on your profile.
- if current_user.ldap_user?
Some options are unavailable for LDAP accounts
%hr
@@ -83,7 +83,7 @@
&nbsp;
%span.file_name.js-avatar-filename File name...
= f.file_field :avatar, class: "js-user-avatar-input hidden"
- .light The maximum file size allowed is 100KB.
+ .light The maximum file size allowed is 200KB.
- 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"
diff --git a/app/views/profiles/update.js.erb b/app/views/profiles/update.js.erb
index 04b5cf4827d..e664ac2a52a 100644
--- a/app/views/profiles/update.js.erb
+++ b/app/views/profiles/update.js.erb
@@ -1,6 +1,6 @@
// Remove body class for any previous theme, re-add current one
-$('body').removeClass('ui_basic ui_mars ui_modern ui_gray ui_color')
-$('body').addClass('<%= app_theme %>')
+$('body').removeClass('ui_basic ui_mars ui_modern ui_gray ui_color light_theme dark_theme')
+$('body').addClass('<%= app_theme %> <%= theme_type %>')
// Re-render the header to reflect the new theme
$('header').html('<%= escape_javascript(render("layouts/head_panel", title: "Profile")) %>')
diff --git a/app/views/projects/_bitbucket_import_modal.html.haml b/app/views/projects/_bitbucket_import_modal.html.haml
new file mode 100644
index 00000000000..5c52f91927d
--- /dev/null
+++ b/app/views/projects/_bitbucket_import_modal.html.haml
@@ -0,0 +1,13 @@
+%div#bitbucket_import_modal.modal.hide
+ .modal-dialog
+ .modal-content
+ .modal-header
+ %a.close{href: "#", "data-dismiss" => "modal"} ×
+ %h3 Import projects from Bitbucket
+ .modal-body
+ To enable importing projects from Bitbucket,
+ - if current_user.admin?
+ you need to
+ - else
+ your GitLab administrator needs to
+ == #{link_to 'setup OAuth integration', 'https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/integration/butbucket.md'}. \ No newline at end of file
diff --git a/app/views/projects/_dropdown.html.haml b/app/views/projects/_dropdown.html.haml
index 6ff46970336..2d5120f283b 100644
--- a/app/views/projects/_dropdown.html.haml
+++ b/app/views/projects/_dropdown.html.haml
@@ -9,24 +9,24 @@
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
+ = link_to new_namespace_project_merge_request_path(@project.namespace, @project), title: "New Merge Request" do
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
+ = link_to new_namespace_project_snippet_path(@project.namespace, @project), title: "New Snippet" do
New snippet
- if can?(current_user, :admin_team_member, @project)
%li
- = link_to new_project_team_member_path(@project), title: "New project member" do
+ = link_to new_namespace_project_team_member_path(@project.namespace, @project), title: "New project member" do
New project member
- if can? current_user, :push_code, @project
%li.divider
%li
- = link_to new_project_branch_path(@project) do
+ = link_to new_namespace_project_branch_path(@project.namespace, @project) do
%i.fa.fa-code-fork
Git branch
%li
- = link_to new_project_tag_path(@project) do
+ = link_to new_namespace_project_tag_path(@project.namespace, @project) do
%i.fa.fa-tag
Git tag
diff --git a/app/views/projects/_github_import_modal.html.haml b/app/views/projects/_github_import_modal.html.haml
new file mode 100644
index 00000000000..e88a0f7d689
--- /dev/null
+++ b/app/views/projects/_github_import_modal.html.haml
@@ -0,0 +1,13 @@
+%div#github_import_modal.modal.hide
+ .modal-dialog
+ .modal-content
+ .modal-header
+ %a.close{href: "#", "data-dismiss" => "modal"} ×
+ %h3 Import projects from GitHub
+ .modal-body
+ To enable importing projects from GitHub,
+ - if current_user.admin?
+ you need to
+ - else
+ your GitLab administrator needs to
+ == #{link_to 'setup OAuth integration', 'https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/integration/github.md'}. \ No newline at end of file
diff --git a/app/views/projects/_gitlab_import_modal.html.haml b/app/views/projects/_gitlab_import_modal.html.haml
new file mode 100644
index 00000000000..52212b6ae02
--- /dev/null
+++ b/app/views/projects/_gitlab_import_modal.html.haml
@@ -0,0 +1,13 @@
+%div#gitlab_import_modal.modal.hide
+ .modal-dialog
+ .modal-content
+ .modal-header
+ %a.close{href: "#", "data-dismiss" => "modal"} ×
+ %h3 Import projects from GitLab.com
+ .modal-body
+ To enable importing projects from GitLab.com,
+ - if current_user.admin?
+ you need to
+ - else
+ your GitLab administrator needs to
+ == #{link_to 'setup OAuth integration', 'https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/integration/gitlab.md'}. \ No newline at end of file
diff --git a/app/views/projects/_home_panel.html.haml b/app/views/projects/_home_panel.html.haml
index 672a91e0eef..c0e13e67be3 100644
--- a/app/views/projects/_home_panel.html.haml
+++ b/app/views/projects/_home_panel.html.haml
@@ -1,26 +1,28 @@
- empty_repo = @project.empty_repo?
.project-home-panel{:class => ("empty-project" if empty_repo)}
+ .project-identicon-holder
+ = project_icon(@project, alt: '', class: 'avatar project-avatar')
.project-home-row
.project-home-desc
- if @project.description.present?
= escaped_autolink(@project.description)
- if can?(current_user, :admin_project, @project)
&ndash;
- = link_to 'Edit', edit_project_path
+ = link_to 'Edit', edit_namespace_project_path
- elsif !@project.empty_repo? && @repository.readme
- readme = @repository.readme
&ndash;
- = link_to project_blob_path(@project, tree_join(@repository.root_ref, readme.name)) do
+ = link_to namespace_project_blob_path(@project.namespace, @project, tree_join(@repository.root_ref, readme.name)) do
= readme.name
.star-fork-buttons
- unless @project.empty_repo?
.fork-buttons
- 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)), title: 'Go to my fork' do
+ - if current_user.already_forked?(@project) && current_user.manageable_namespaces.size < 2
+ = link_to namespace_project_path(current_user, current_user.fork_of(@project)), title: 'Go to my fork' do
= link_to_toggle_fork
- else
- = link_to fork_project_path(@project), title: "Fork project", method: "POST" do
+ = link_to new_namespace_project_fork_path(@project.namespace, @project), title: "Fork project" do
= link_to_toggle_fork
.star-buttons
@@ -31,7 +33,7 @@
- else
= link_to_toggle_star('You must sign in to star a project.', false, false)
- .project-home-row
+ .project-home-row.hidden-xs
- if current_user && !empty_repo
.project-home-dropdown
= render "dropdown"
diff --git a/app/views/projects/_issuable_form.html.haml b/app/views/projects/_issuable_form.html.haml
index 6cdfab933b4..bfacab5e48e 100644
--- a/app/views/projects/_issuable_form.html.haml
+++ b/app/views/projects/_issuable_form.html.haml
@@ -14,17 +14,20 @@
.form-group.issuable-description
= f.label :description, 'Description', class: 'control-label'
.col-sm-10
- = render 'projects/zen', f: f, attr: :description,
- classes: 'description form-control'
- .col-sm-12.hint
- .pull-left
- Parsed with
- #{link_to 'GitLab Flavored Markdown', help_page_path('markdown', 'markdown'), target: '_blank'}.
- .pull-right
- Attach images (JPG, PNG, GIF) by dragging &amp; dropping
- or #{link_to 'selecting them', '#', class: 'markdown-selector' }.
- .clearfix
- .error-alert
+
+ = render layout: 'projects/md_preview', locals: { preview_class: "wiki" } do
+ = render 'projects/zen', f: f, attr: :description,
+ classes: 'description form-control'
+ .col-sm-12.hint
+ .pull-left
+ Parsed with
+ #{link_to 'GitLab Flavored Markdown', help_page_path('markdown', 'markdown'), target: '_blank'}.
+ .pull-right
+ Attach files by dragging &amp; dropping
+ or #{link_to 'selecting them', '#', class: 'markdown-selector' }.
+
+ .clearfix
+ .error-alert
%hr
.form-group
.issue-assignee
@@ -49,10 +52,11 @@
- else
%span.light No open milestones available.
&nbsp;
- = link_to 'Create new milestone', new_project_milestone_path(issuable.project)
+ - if can? current_user, :admin_milestone, issuable.project
+ = link_to 'Create new milestone', new_namespace_project_milestone_path(issuable.project.namespace, issuable.project), target: :blank
.form-group
= f.label :label_ids, class: 'control-label' do
- %i.icon-tag
+ %i.fa.fa-tag
Labels
.col-sm-10
- if issuable.project.labels.any?
@@ -61,9 +65,15 @@
- else
%span.light No labels yet.
&nbsp;
- = link_to 'Create new label', new_project_label_path(issuable.project)
+ - if can? current_user, :admin_label, issuable.project
+ = link_to 'Create new label', new_namespace_project_label_path(issuable.project.namespace, issuable.project), target: :blank
.form-actions
+ - if !issuable.project.empty_repo? && contribution_guide_url(issuable.project) && !issuable.persisted?
+ %p
+ Please review the
+ %strong #{link_to 'guidelines for contribution', contribution_guide_url(issuable.project)}
+ to this repository.
- if issuable.new_record?
= f.submit "Submit new #{issuable.class.model_name.human.downcase}", class: 'btn btn-create'
- else
@@ -72,4 +82,4 @@
- cancel_project = issuable.source_project
- else
- cancel_project = issuable.project
- = link_to 'Cancel', [cancel_project, issuable], class: 'btn btn-cancel'
+ = link_to 'Cancel', [cancel_project.namespace.becomes(Namespace), cancel_project, issuable], class: 'btn btn-cancel'
diff --git a/app/views/projects/_md_preview.html.haml b/app/views/projects/_md_preview.html.haml
new file mode 100644
index 00000000000..f356a25dbfa
--- /dev/null
+++ b/app/views/projects/_md_preview.html.haml
@@ -0,0 +1,13 @@
+%ul.nav.nav-tabs
+ %li.active
+ = link_to '#md-write-holder', class: 'js-md-write-button' do
+ Write
+ %li
+ = link_to '#md-preview-holder', class: 'js-md-preview-button',
+ data: { url: markdown_preview_namespace_project_path(@project.namespace, @project) } do
+ Preview
+%div
+ .md-write-holder
+ = yield
+ .md-preview-holder.hide
+ .js-md-preview{class: (preview_class if defined?(preview_class))}
diff --git a/app/views/projects/_settings_nav.html.haml b/app/views/projects/_settings_nav.html.haml
index 2008f8c558d..7fc3d44034f 100644
--- a/app/views/projects/_settings_nav.html.haml
+++ b/app/views/projects/_settings_nav.html.haml
@@ -1,25 +1,31 @@
-%ul.nav.nav-pills.nav-stacked.nav-stacked-menu.append-bottom-20.project-settings-nav
+%ul.project-settings-nav.sidebar-subnav
= nav_link(path: 'projects#edit') do
- = link_to edit_project_path(@project), class: "stat-tab tab " do
+ = link_to edit_project_path(@project), title: 'Project', class: "stat-tab tab " do
%i.fa.fa-pencil-square-o
- Project
+ %span
+ Project
= nav_link(controller: [:team_members, :teams]) do
- = link_to project_team_index_path(@project), class: "team-tab tab" do
+ = link_to namespace_project_team_index_path(@project.namespace, @project), title: 'Members', class: "team-tab tab" do
%i.fa.fa-users
- Members
+ %span
+ Members
= nav_link(controller: :deploy_keys) do
- = link_to project_deploy_keys_path(@project) do
+ = link_to namespace_project_deploy_keys_path(@project.namespace, @project), title: 'Deploy Keys' do
%i.fa.fa-key
- Deploy Keys
+ %span
+ Deploy Keys
= nav_link(controller: :hooks) do
- = link_to project_hooks_path(@project) do
+ = link_to namespace_project_hooks_path(@project.namespace, @project), title: 'Web Hooks' do
%i.fa.fa-link
- Web Hooks
+ %span
+ Web Hooks
= nav_link(controller: :services) do
- = link_to project_services_path(@project) do
+ = link_to namespace_project_services_path(@project.namespace, @project), title: 'Services' do
%i.fa.fa-cogs
- Services
+ %span
+ Services
= nav_link(controller: :protected_branches) do
- = link_to project_protected_branches_path(@project) do
+ = link_to namespace_project_protected_branches_path(@project.namespace, @project), title: 'Protected Branches' do
%i.fa.fa-lock
- Protected branches
+ %span
+ Protected branches
diff --git a/app/views/projects/_visibility_level.html.haml b/app/views/projects/_visibility_level.html.haml
index 5f34e66b3ed..42c8e685224 100644
--- a/app/views/projects/_visibility_level.html.haml
+++ b/app/views/projects/_visibility_level.html.haml
@@ -7,8 +7,8 @@
- Gitlab::VisibilityLevel.values.each do |level|
.radio
- restricted = restricted_visibility_levels.include?(level)
- = f.radio_button :visibility_level, level, checked: (visibility_level == level), disabled: restricted
= label :project_visibility_level, level do
+ = f.radio_button :visibility_level, level, checked: (visibility_level == level), disabled: restricted
= visibility_level_icon(level)
.option-title
= visibility_level_label(level)
diff --git a/app/views/projects/_zen.html.haml b/app/views/projects/_zen.html.haml
index 2bbc49e8eb5..cf1c55ecca6 100644
--- a/app/views/projects/_zen.html.haml
+++ b/app/views/projects/_zen.html.haml
@@ -1,7 +1,10 @@
.zennable
- %input#zen-toggle-comment{ tabindex: '-1', type: 'checkbox' }
+ %input#zen-toggle-comment.zen-toggle-comment{ tabindex: '-1', type: 'checkbox' }
.zen-backdrop
- classes << ' js-gfm-input markdown-area'
= f.text_area attr, class: classes, placeholder: 'Leave a comment'
- %label{ for: 'zen-toggle-comment', class: 'expand' } Edit in fullscreen
- %label{ for: 'zen-toggle-comment', class: 'collapse' }
+ = link_to nil, class: 'zen-enter-link', tabindex: '-1' do
+ %i.fa.fa-expand
+ Edit in fullscreen
+ = link_to nil, class: 'zen-leave-link' do
+ %i.fa.fa-compress
diff --git a/app/views/projects/blame/show.html.haml b/app/views/projects/blame/show.html.haml
index bdf02c6285d..5a33d18e631 100644
--- a/app/views/projects/blame/show.html.haml
+++ b/app/views/projects/blame/show.html.haml
@@ -15,20 +15,20 @@
%tr
%td.blame-commit
%span.commit
- = link_to commit.short_id, project_commit_path(@project, commit), class: "commit_short_id"
+ = link_to commit.short_id, namespace_project_commit_path(@project.namespace, @project, commit), class: "commit_short_id"
&nbsp;
= commit_author_link(commit, avatar: true, size: 16)
&nbsp;
- = link_to_gfm truncate(commit.title, length: 20), project_commit_path(@project, commit.id), class: "row_title"
+ = link_to_gfm truncate(commit.title, length: 20), namespace_project_commit_path(@project.namespace, @project, commit.id), class: "row_title"
%td.lines.blame-numbers
%pre
- (since...(since + lines.count)).each do |i|
= i
\
%td.lines
- %pre
- %code{ class: highlightjs_class(@blob.name) }
+ %pre{class: 'code highlight white'}
+ %code
:erb
<% lines.each do |line| %>
- <%= line %>
+ <%= highlight(@blob.name, line.force_encoding("utf-8"), true).html_safe %>
<% end %>
diff --git a/app/views/projects/blob/_actions.html.haml b/app/views/projects/blob/_actions.html.haml
index 64c19a57803..b5b29540bb6 100644
--- a/app/views/projects/blob/_actions.html.haml
+++ b/app/views/projects/blob/_actions.html.haml
@@ -1,27 +1,22 @@
.btn-group.tree-btn-group
- -# only show edit link for text files
- - if @blob.text?
- - 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),
+ = edit_blob_link(@project, @ref, @path)
+ = link_to 'Raw', namespace_project_raw_path(@project.namespace, @project, @id),
class: 'btn btn-small', target: '_blank'
-# only show normal/blame view links for text files
- if @blob.text?
- - if current_page? project_blame_path(@project, @id)
- = link_to 'Normal View', project_blob_path(@project, @id),
+ - if current_page? namespace_project_blame_path(@project.namespace, @project, @id)
+ = link_to 'Normal View', namespace_project_blob_path(@project.namespace, @project, @id),
class: 'btn btn-small'
- else
- = link_to 'Blame', project_blame_path(@project, @id),
+ = link_to 'Blame', namespace_project_blame_path(@project.namespace, @project, @id),
class: 'btn btn-small' unless @blob.empty?
- = link_to 'History', project_commits_path(@project, @id),
+ = link_to 'History', namespace_project_commits_path(@project.namespace, @project, @id),
class: 'btn btn-small'
- if @ref != @commit.sha
- = link_to 'Permalink', project_blob_path(@project,
+ = link_to 'Permalink', namespace_project_blob_path(@project.namespace, @project,
tree_join(@commit.sha, @path)), 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
+ = button_tag class: 'remove-blob btn btn-small btn-remove',
+ 'data-toggle' => 'modal', 'data-target' => '#modal-remove-blob' do
Remove
diff --git a/app/views/projects/blob/_blob.html.haml b/app/views/projects/blob/_blob.html.haml
index 492dff437b5..64cc3fad6cf 100644
--- a/app/views/projects/blob/_blob.html.haml
+++ b/app/views/projects/blob/_blob.html.haml
@@ -1,22 +1,22 @@
%ul.breadcrumb.repo-breadcrumb
%li
%i.fa.fa-angle-right
- = link_to project_tree_path(@project, @ref) do
+ = link_to namespace_project_tree_path(@project.namespace, @project, @ref) do
= @project.path
- tree_breadcrumbs(@tree, 6) do |title, path|
%li
- if path
- if path.end_with?(@path)
- = link_to project_blob_path(@project, path) do
+ = link_to namespace_project_blob_path(@project.namespace, @project, path) do
%strong
= truncate(title, length: 40)
- else
- = link_to truncate(title, length: 40), project_tree_path(@project, path)
+ = link_to truncate(title, length: 40), namespace_project_tree_path(@project.namespace, @project, path)
- else
= link_to title, '#'
%ul.blob-commit-info.bs-callout.bs-callout-info.hidden-xs
- - blob_commit = @repository.last_commit_for_path(@commit.id, @blob.path)
+ - blob_commit = @repository.last_commit_for_path(@commit.id, blob.path)
= render blob_commit, project: @project
%div#tree-content-holder.tree-content-holder
diff --git a/app/views/projects/blob/_download.html.haml b/app/views/projects/blob/_download.html.haml
index c24eeea4931..f2c5e95ecf4 100644
--- a/app/views/projects/blob/_download.html.haml
+++ b/app/views/projects/blob/_download.html.haml
@@ -1,6 +1,6 @@
.file-content.blob_file.blob-no-preview
.center
- = link_to project_raw_path(@project, @id) do
+ = link_to namespace_project_raw_path(@project.namespace, @project, @id) do
%h1.light
%i.fa.fa-download
%h4
diff --git a/app/views/projects/blob/_editor.html.haml b/app/views/projects/blob/_editor.html.haml
new file mode 100644
index 00000000000..96f188e4aa7
--- /dev/null
+++ b/app/views/projects/blob/_editor.html.haml
@@ -0,0 +1,25 @@
+.file-holder.file
+ .file-title
+ .editor-ref
+ %i.fa.fa-code-fork
+ = ref
+ %span.editor-file-name
+ - if @path
+ %span.monospace
+ = @path
+
+ - if current_action?(:new) || current_action?(:create)
+ \/
+ = text_field_tag 'file_name', params[:file_name], placeholder: "File name",
+ required: true, class: 'form-control new-file-name'
+ .pull-right
+ = select_tag :encoding, options_for_select([ "base64", "text" ], "text"), class: 'form-control'
+
+ .file-content.code
+ %pre.js-edit-mode-pane#editor
+ = params[:content] || local_assigns[:blob_data]
+ - if local_assigns[:path]
+ .js-edit-mode-pane#preview.hide
+ .center
+ %h2
+ %i.icon-spinner.icon-spin
diff --git a/app/views/projects/blob/_remove.html.haml b/app/views/projects/blob/_remove.html.haml
index c5568315cb1..09559a4967b 100644
--- a/app/views/projects/blob/_remove.html.haml
+++ b/app/views/projects/blob/_remove.html.haml
@@ -9,7 +9,7 @@
%strong= @ref
.modal-body
- = form_tag project_blob_path(@project, @id), method: :delete, class: 'form-horizontal' do
+ = form_tag namespace_project_blob_path(@project.namespace, @project, @id), method: :delete, class: 'form-horizontal' do
= render 'shared/commit_message_container', params: params,
placeholder: 'Removed this file because...'
.form-group
diff --git a/app/views/projects/blob/_text.html.haml b/app/views/projects/blob/_text.html.haml
index 7cbea7c3eb6..f6bd62f239b 100644
--- a/app/views/projects/blob/_text.html.haml
+++ b/app/views/projects/blob/_text.html.haml
@@ -8,6 +8,6 @@
- else
.file-content.code
- unless blob.empty?
- = render 'shared/file_hljs', blob: blob
+ = render 'shared/file_highlight', blob: blob
- else
.nothing-here-block Empty file
diff --git a/app/views/projects/blob/edit.html.haml b/app/views/projects/blob/edit.html.haml
new file mode 100644
index 00000000000..1f61a0b940c
--- /dev/null
+++ b/app/views/projects/blob/edit.html.haml
@@ -0,0 +1,31 @@
+.file-editor
+ %ul.nav.nav-tabs.js-edit-mode
+ %li.active
+ = link_to '#editor' do
+ %i.fa.fa-edit
+ Edit file
+
+ %li
+ = link_to '#preview', 'data-preview-url' => namespace_project_preview_blob_path(@project.namespace, @project, @id) do
+ %i.fa.fa-eye
+ = editing_preview_title(@blob.name)
+
+ = form_tag(namespace_project_update_blob_path(@project.namespace, @project, @id), method: :put, class: "form-horizontal") do
+ = render 'projects/blob/editor', ref: @ref, path: @path, blob_data: @blob.data
+ = render 'shared/commit_message_container', params: params,
+ placeholder: "Update #{@blob.name}"
+
+ .form-group.branch
+ = label_tag 'branch', class: 'control-label' do
+ Branch
+ .col-sm-10
+ = text_field_tag 'new_branch', @ref, class: "form-control"
+
+ = hidden_field_tag 'last_commit', @last_commit
+ = hidden_field_tag 'content', '', id: "file-content"
+ = hidden_field_tag 'from_merge_request_id', params[:from_merge_request_id]
+ = render 'projects/commit_button', ref: @ref,
+ cancel_path: @after_edit_path
+
+:javascript
+ blob = new EditBlob(gon.relative_url_root + "#{Gitlab::Application.config.assets.prefix}", "#{@blob.language.try(:ace_mode)}")
diff --git a/app/views/projects/blob/new.html.haml b/app/views/projects/blob/new.html.haml
new file mode 100644
index 00000000000..d78a01f6422
--- /dev/null
+++ b/app/views/projects/blob/new.html.haml
@@ -0,0 +1,19 @@
+%h3.page-title New file
+.file-editor
+ = form_tag(namespace_project_create_blob_path(@project.namespace, @project, @id), method: :post, class: 'form-horizontal form-new-file') do
+ = render 'projects/blob/editor', ref: @ref
+ = render 'shared/commit_message_container', params: params,
+ placeholder: 'Add new file'
+
+ .form-group.branch
+ = label_tag 'branch', class: 'control-label' do
+ Branch
+ .col-sm-10
+ = text_field_tag 'new_branch', @ref, class: "form-control"
+
+ = hidden_field_tag 'content', '', id: 'file-content'
+ = render 'projects/commit_button', ref: @ref,
+ cancel_path: namespace_project_tree_path(@project.namespace, @project, @id)
+
+:javascript
+ blob = new NewBlob(gon.relative_url_root + "#{Gitlab::Application.config.assets.prefix}", null)
diff --git a/app/views/projects/edit_tree/preview.html.haml b/app/views/projects/blob/preview.html.haml
index e7c3460ad78..e7c3460ad78 100644
--- a/app/views/projects/edit_tree/preview.html.haml
+++ b/app/views/projects/blob/preview.html.haml
diff --git a/app/views/projects/branches/_branch.html.haml b/app/views/projects/branches/_branch.html.haml
index 8e58f3c247a..8de629b03e9 100644
--- a/app/views/projects/branches/_branch.html.haml
+++ b/app/views/projects/branches/_branch.html.haml
@@ -1,7 +1,7 @@
- commit = @repository.commit(branch.target)
%li(class="js-branch-#{branch.name}")
%h4
- = link_to project_tree_path(@project, branch.name) do
+ = link_to namespace_project_tree_path(@project.namespace, @project, branch.name) do
%strong.str-truncated= branch.name
- if branch.name == @repository.root_ref
%span.label.label-info default
@@ -13,12 +13,12 @@
- if can?(current_user, :download_code, @project)
= render 'projects/repositories/download_archive', ref: branch.name, btn_class: 'btn-grouped btn-group-small'
- if branch.name != @repository.root_ref
- = link_to project_compare_index_path(@project, from: @repository.root_ref, to: branch.name), class: 'btn btn-grouped btn-small', method: :post, title: "Compare" do
+ = link_to namespace_project_compare_index_path(@project.namespace, @project, from: @repository.root_ref, to: branch.name), class: 'btn btn-grouped btn-small', method: :post, title: "Compare" do
%i.fa.fa-files-o
Compare
- if can_remove_branch?(@project, branch.name)
- = link_to project_branch_path(@project, branch.name), class: 'btn btn-grouped btn-small btn-remove remove-row', method: :delete, data: { confirm: 'Removed branch cannot be restored. Are you sure?'}, remote: true do
+ = link_to namespace_project_branch_path(@project.namespace, @project, branch.name), class: 'btn btn-grouped btn-small btn-remove remove-row', method: :delete, data: { confirm: 'Removed branch cannot be restored. Are you sure?'}, remote: true do
%i.fa.fa-trash-o
- if commit
diff --git a/app/views/projects/branches/index.html.haml b/app/views/projects/branches/index.html.haml
index 9f2b1b59292..f77d02a97fb 100644
--- a/app/views/projects/branches/index.html.haml
+++ b/app/views/projects/branches/index.html.haml
@@ -3,7 +3,7 @@
Branches
.pull-right
- if can? current_user, :push_code, @project
- = link_to new_project_branch_path(@project), class: 'btn btn-create' do
+ = link_to new_namespace_project_branch_path(@project.namespace, @project), class: 'btn btn-create' do
%i.fa.fa-add-sign
New branch
&nbsp;
@@ -17,12 +17,12 @@
%b.caret
%ul.dropdown-menu
%li
- = link_to project_branches_path(sort: nil) do
+ = link_to namespace_project_branches_path(sort: nil) do
Name
- = link_to project_branches_path(sort: 'recently_updated') do
- Recently updated
- = link_to project_branches_path(sort: 'last_updated') do
- Last updated
+ = link_to namespace_project_branches_path(sort: 'recently_updated') do
+ = sort_title_recently_updated
+ = link_to namespace_project_branches_path(sort: 'last_updated') do
+ = sort_title_oldest_updated
%hr
- unless @branches.empty?
%ul.bordered-list.top-list.all-branches
diff --git a/app/views/projects/branches/new.html.haml b/app/views/projects/branches/new.html.haml
index a6623240da1..e5fcb98c68c 100644
--- a/app/views/projects/branches/new.html.haml
+++ b/app/views/projects/branches/new.html.haml
@@ -5,7 +5,7 @@
%h3.page-title
%i.fa.fa-code-fork
New branch
-= form_tag project_branches_path, method: :post, class: "form-horizontal" do
+= form_tag namespace_project_branches_path, method: :post, id: "new-branch-form", class: "form-horizontal" do
.form-group
= label_tag :branch_name, 'Name for new branch', class: 'control-label'
.col-sm-10
@@ -16,9 +16,10 @@
= text_field_tag :ref, params[:ref], placeholder: 'existing branch name, tag or commit SHA', required: true, tabindex: 2, class: 'form-control'
.form-actions
= button_tag 'Create branch', class: 'btn btn-create', tabindex: 3
- = link_to 'Cancel', project_branches_path(@project), class: 'btn btn-cancel'
+ = link_to 'Cancel', namespace_project_branches_path(@project.namespace, @project), class: 'btn btn-cancel'
:javascript
+ disableButtonIfAnyEmptyField($("#new-branch-form"), ".form-control", ".btn-create");
var availableTags = #{@project.repository.ref_names.to_json};
$("#ref").autocomplete({
diff --git a/app/views/projects/commit/_commit_box.html.haml b/app/views/projects/commit/_commit_box.html.haml
index e149f017f84..7409f702c5d 100644
--- a/app/views/projects/commit/_commit_box.html.haml
+++ b/app/views/projects/commit/_commit_box.html.haml
@@ -10,15 +10,15 @@
Download as
%span.caret
%ul.dropdown-menu
- %li= link_to "Email Patches", project_commit_path(@project, @commit, format: :patch)
- %li= link_to "Plain Diff", project_commit_path(@project, @commit, format: :diff)
- = link_to project_tree_path(@project, @commit), class: "btn btn-primary btn-grouped" do
+ %li= link_to "Email Patches", namespace_project_commit_path(@project.namespace, @project, @commit, format: :patch)
+ %li= link_to "Plain Diff", namespace_project_commit_path(@project.namespace, @project, @commit, format: :diff)
+ = link_to namespace_project_tree_path(@project.namespace, @project, @commit), class: "btn btn-primary btn-grouped" do
%span Browse Code »
%div
%p
%span.light Commit
- = link_to @commit.id, project_commit_path(@project, @commit)
+ = link_to @commit.id, namespace_project_commit_path(@project.namespace, @project, @commit)
.commit-info-row
%span.light Authored by
%strong
@@ -35,20 +35,10 @@
.commit-info-row
%span.cgray= pluralize(@commit.parents.count, "parent")
- @commit.parents.each do |parent|
- = link_to parent.short_id, project_commit_path(@project, parent)
+ = link_to parent.short_id, namespace_project_commit_path(@project.namespace, @project, parent)
-- if @branches.any?
- .commit-info-row
- %span.cgray
- Exists in
- %span
- - branch = commit_default_branch(@project, @branches)
- = link_to(branch, project_tree_path(@project, branch))
- - if @branches.any?
- and in
- = link_to("#{pluralize(@branches.count, "other branch")}", "#", class: "js-details-expand")
- %span.js-details-content.hide
- = commit_branches_links(@project, @branches)
+.commit-info-row.branches
+ %i.fa.fa-spinner.fa-spin
.commit-box
%h3.commit-title
@@ -56,3 +46,7 @@
- if @commit.description.present?
%pre.commit-description
= preserve(gfm(escape_once(@commit.description)))
+
+:coffeescript
+ $ ->
+ $(".commit-info-row.branches").load("#{branches_namespace_project_commit_path(@project.namespace, @project, @commit.id)}")
diff --git a/app/views/projects/commit/branches.html.haml b/app/views/projects/commit/branches.html.haml
new file mode 100644
index 00000000000..82aac1fbd15
--- /dev/null
+++ b/app/views/projects/commit/branches.html.haml
@@ -0,0 +1,16 @@
+- if @branches.any?
+ %span
+ - branch = commit_default_branch(@project, @branches)
+ = link_to(namespace_project_tree_path(@project.namespace, @project, branch)) do
+ %span.label.label-gray
+ %i.fa.fa-code-fork
+ = branch
+ - if @branches.any? || @tags.any?
+ = link_to("#", class: "js-details-expand") do
+ %span.label.label-gray
+ \...
+ %span.js-details-content.hide
+ - if @branches.any?
+ = commit_branches_links(@project, @branches)
+ - if @tags.any?
+ = commit_tags_links(@project, @tags)
diff --git a/app/views/projects/commits/_commit.html.haml b/app/views/projects/commits/_commit.html.haml
index 1eb17f760dc..4c853f577e9 100644
--- a/app/views/projects/commits/_commit.html.haml
+++ b/app/views/projects/commits/_commit.html.haml
@@ -1,13 +1,12 @@
%li.commit.js-toggle-container
.commit-row-title
- = link_to commit.short_id, project_commit_path(project, commit), class: "commit_short_id"
- &nbsp;
- %span.str-truncated
- = link_to_gfm commit.title, project_commit_path(project, commit.id), class: "commit-row-message"
+ %strong.str-truncated
+ = link_to_gfm commit.title, namespace_project_commit_path(project.namespace, project, commit.id), class: "commit-row-message"
- if commit.description?
%a.text-expander.js-toggle-button ...
- = link_to_browse_code(project, commit)
+ .pull-right
+ = link_to commit.short_id, namespace_project_commit_path(project.namespace, project, commit), class: "commit_short_id"
.notes_count
- if @note_counts
@@ -17,8 +16,9 @@
- note_count = notes.count
- if note_count > 0
- %span.label.label-gray
- %i.fa.fa-comment= note_count
+ %span.light
+ %i.fa.fa-comments
+ = note_count
- if commit.description?
.commit-row-description.js-toggle-content
@@ -26,6 +26,8 @@
= preserve(gfm(escape_once(commit.description)))
.commit-row-info
- = commit_author_link(commit, avatar: true, size: 16)
+ = commit_author_link(commit, avatar: true, size: 24)
+ authored
.committed_ago
#{time_ago_with_tooltip(commit.committed_date)} &nbsp;
+ = link_to_browse_code(project, commit)
diff --git a/app/views/projects/commits/_commits.html.haml b/app/views/projects/commits/_commits.html.haml
index d57659065a8..0cd9ce1f371 100644
--- a/app/views/projects/commits/_commits.html.haml
+++ b/app/views/projects/commits/_commits.html.haml
@@ -1,11 +1,15 @@
+- unless defined?(project)
+ - project = @project
+
- @commits.group_by { |c| c.committed_date.to_date }.sort.reverse.each do |day, commits|
.row.commits-row
- .col-md-2
- %h4
+ .col-md-2.hidden-xs.hidden-sm
+ %h5.commits-row-date
%i.fa.fa-calendar
%span= day.stamp("28 Aug, 2010")
- %p= pluralize(commits.count, 'commit')
- .col-md-10
+ .light
+ = pluralize(commits.count, 'commit')
+ .col-md-10.col-sm-12
%ul.bordered-list
- = render commits, project: @project
+ = render commits, project: project
%hr.lists-separator
diff --git a/app/views/projects/commits/_head.html.haml b/app/views/projects/commits/_head.html.haml
index 0c9d906481b..83e4d24cf5f 100644
--- a/app/views/projects/commits/_head.html.haml
+++ b/app/views/projects/commits/_head.html.haml
@@ -1,15 +1,15 @@
%ul.nav.nav-tabs
= nav_link(controller: [:commit, :commits]) do
- = link_to 'Commits', project_commits_path(@project, @repository.root_ref)
+ = link_to 'Commits', namespace_project_commits_path(@project.namespace, @project, @repository.root_ref)
= nav_link(controller: :compare) do
- = link_to 'Compare', project_compare_index_path(@project, from: @repository.root_ref, to: @ref || @repository.root_ref)
+ = link_to 'Compare', namespace_project_compare_index_path(@project.namespace, @project, from: @repository.root_ref, to: @ref || @repository.root_ref)
= nav_link(html_options: {class: branches_tab_class}) do
- = link_to project_branches_path(@project) do
+ = link_to namespace_project_branches_path(@project.namespace, @project) do
Branches
%span.badge.js-totalbranch-count= @repository.branches.size
= nav_link(controller: :tags) do
- = link_to project_tags_path(@project) do
+ = link_to namespace_project_tags_path(@project.namespace, @project) do
Tags
%span.badge.js-totaltags-count= @repository.tags.length
diff --git a/app/views/projects/commits/_inline_commit.html.haml b/app/views/projects/commits/_inline_commit.html.haml
index 574599aa2d2..c03bc3f9df9 100644
--- a/app/views/projects/commits/_inline_commit.html.haml
+++ b/app/views/projects/commits/_inline_commit.html.haml
@@ -1,8 +1,8 @@
%li.commit.inline-commit
.commit-row-title
- = link_to commit.short_id, project_commit_path(project, commit), class: "commit_short_id"
+ = link_to commit.short_id, namespace_project_commit_path(project.namespace, project, commit), class: "commit_short_id"
&nbsp;
%span.str-truncated
- = link_to_gfm commit.title, project_commit_path(project, commit.id), class: "commit-row-message"
+ = link_to_gfm commit.title, namespace_project_commit_path(project.namespace, project, commit.id), class: "commit-row-message"
.pull-right
#{time_ago_with_tooltip(commit.committed_date)}
diff --git a/app/views/projects/commits/show.atom.builder b/app/views/projects/commits/show.atom.builder
index 32c82edb248..9211de72b1b 100644
--- a/app/views/projects/commits/show.atom.builder
+++ b/app/views/projects/commits/show.atom.builder
@@ -1,15 +1,15 @@
xml.instruct!
xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://search.yahoo.com/mrss/" do
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, @ref)
+ xml.link :href => namespace_project_commits_url(@project.namespace, @project, @ref, format: :atom), :rel => "self", :type => "application/atom+xml"
+ xml.link :href => namespace_project_commits_url(@project.namespace, @project, @ref), :rel => "alternate", :type => "text/html"
+ xml.id namespace_project_commits_url(@project.namespace, @project, @ref)
xml.updated @commits.first.committed_date.strftime("%Y-%m-%dT%H:%M:%SZ") if @commits.any?
@commits.each do |commit|
xml.entry do
- xml.id project_commit_url(@project, :id => commit.id)
- xml.link :href => project_commit_url(@project, :id => commit.id)
+ xml.id namespace_project_commit_url(@project.namespace, @project, :id => commit.id)
+ xml.link :href => namespace_project_commit_url(@project.namespace, @project, :id => commit.id)
xml.title truncate(commit.title, :length => 80)
xml.updated commit.committed_date.strftime("%Y-%m-%dT%H:%M:%SZ")
xml.media :thumbnail, :width => "40", :height => "40", :url => avatar_icon(commit.author_email)
diff --git a/app/views/projects/commits/show.html.haml b/app/views/projects/commits/show.html.haml
index 5717c24c274..7ea855e1a4e 100644
--- a/app/views/projects/commits/show.html.haml
+++ b/app/views/projects/commits/show.html.haml
@@ -5,17 +5,15 @@
- if current_user && current_user.private_token
.commits-feed-holder.hidden-xs.hidden-sm
- = link_to project_commits_path(@project, @ref, {format: :atom, private_token: current_user.private_token}), title: "Feed", class: 'btn' do
+ = link_to namespace_project_commits_path(@project.namespace, @project, @ref, {format: :atom, private_token: current_user.private_token}), title: "Feed", class: 'btn' do
%i.fa.fa-rss
Commits feed
%ul.breadcrumb.repo-breadcrumb
= commits_breadcrumbs
- %li.active
- commits
%div{id: dom_id(@project)}
- #commits-list= render "commits"
+ #commits-list= render "commits", project: @project
.clear
= spinner
diff --git a/app/views/projects/compare/_form.html.haml b/app/views/projects/compare/_form.html.haml
index cb0a3747f7d..dfb1dded9ea 100644
--- a/app/views/projects/compare/_form.html.haml
+++ b/app/views/projects/compare/_form.html.haml
@@ -1,4 +1,4 @@
-= form_tag project_compare_index_path(@project), method: :post, class: 'form-inline' do
+= form_tag namespace_project_compare_index_path(@project.namespace, @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'}
diff --git a/app/views/projects/create.js.haml b/app/views/projects/create.js.haml
deleted file mode 100644
index 89710d3a09a..00000000000
--- a/app/views/projects/create.js.haml
+++ /dev/null
@@ -1,13 +0,0 @@
-- if @project.saved?
- - if @project.import?
- :plain
- location.href = "#{import_project_path(@project)}";
- - else
- :plain
- location.href = "#{project_path(@project)}";
-- else
- :plain
- $(".project-edit-errors").html("#{escape_javascript(render('errors'))}");
- $('.project-submit').enable();
- $('.save-project-loader').hide();
- $('.project-edit-container').show();
diff --git a/app/views/projects/deploy_keys/_deploy_key.html.haml b/app/views/projects/deploy_keys/_deploy_key.html.haml
index a0345dbd9c3..230e164f24c 100644
--- a/app/views/projects/deploy_keys/_deploy_key.html.haml
+++ b/app/views/projects/deploy_keys/_deploy_key.html.haml
@@ -1,19 +1,20 @@
%li
.pull-right
- if @available_keys.include?(deploy_key)
- = link_to enable_project_deploy_key_path(@project, deploy_key), class: 'btn btn-small', method: :put do
+ = link_to enable_namespace_project_deploy_key_path(@project.namespace, @project, deploy_key), class: 'btn btn-small', method: :put do
%i.fa.fa-plus
Enable
- else
- if deploy_key.projects.count > 1
- = link_to disable_project_deploy_key_path(@project, deploy_key), class: 'btn btn-small', method: :put do
+ = link_to disable_namespace_project_deploy_key_path(@project.namespace, @project, deploy_key), class: 'btn btn-small', method: :put do
%i.fa.fa-power-off
Disable
- else
- = 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 'Remove', namespace_project_deploy_key_path(@project.namespace, @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
+ - key_project = deploy_key.projects.include?(@project) ? @project : deploy_key.projects.first
+ = link_to namespace_project_deploy_key_path(key_project.namespace, key_project, deploy_key) do
%i.fa.fa-key
%strong= deploy_key.title
diff --git a/app/views/projects/deploy_keys/_form.html.haml b/app/views/projects/deploy_keys/_form.html.haml
index 162ef05b367..91675b3738e 100644
--- a/app/views/projects/deploy_keys/_form.html.haml
+++ b/app/views/projects/deploy_keys/_form.html.haml
@@ -1,5 +1,5 @@
%div
- = form_for [@project, @key], url: project_deploy_keys_path, html: { class: 'deploy-key-form form-horizontal' } do |f|
+ = form_for [@project.namespace.becomes(Namespace), @project, @key], url: namespace_project_deploy_keys_path, html: { class: 'deploy-key-form form-horizontal' } do |f|
-if @key.errors.any?
.alert.alert-danger
%ul
@@ -19,5 +19,5 @@
.form-actions
= f.submit 'Create', class: "btn-create btn"
- = link_to "Cancel", project_deploy_keys_path(@project), class: "btn btn-cancel"
+ = link_to "Cancel", namespace_project_deploy_keys_path(@project.namespace, @project), class: "btn btn-cancel"
diff --git a/app/views/projects/deploy_keys/index.html.haml b/app/views/projects/deploy_keys/index.html.haml
index 6f475e0b395..c02a18146eb 100644
--- a/app/views/projects/deploy_keys/index.html.haml
+++ b/app/views/projects/deploy_keys/index.html.haml
@@ -1,7 +1,7 @@
%h3.page-title
Deploy keys allow read-only access to the repository
- = link_to new_project_deploy_key_path(@project), class: "btn btn-new pull-right", title: "New Deploy Key" do
+ = link_to new_namespace_project_deploy_key_path(@project.namespace, @project), class: "btn btn-new pull-right", title: "New Deploy Key" do
%i.fa.fa-plus
New Deploy Key
@@ -20,7 +20,7 @@
= render @enabled_keys
- if @enabled_keys.blank?
.light-well
- .nothing-here-block Create a #{link_to 'new deploy key', new_project_deploy_key_path(@project)} or add an existing one
+ .nothing-here-block Create a #{link_to 'new deploy key', new_namespace_project_deploy_key_path(@project.namespace, @project)} or add an existing one
.col-md-6.available-keys
%h5
%strong Deploy keys
diff --git a/app/views/projects/deploy_keys/show.html.haml b/app/views/projects/deploy_keys/show.html.haml
index c66e6bc69c3..405b5bcd0d3 100644
--- a/app/views/projects/deploy_keys/show.html.haml
+++ b/app/views/projects/deploy_keys/show.html.haml
@@ -5,9 +5,9 @@
created on
= @key.created_at.stamp("Aug 21, 2011")
.back-link
- = link_to project_deploy_keys_path(@project) do
+ = link_to namespace_project_deploy_keys_path(@project.namespace, @project) do
&larr; To keys list
%hr
%pre= @key.key
.pull-right
- = link_to 'Remove', project_deploy_key_path(@project, @key), data: { confirm: 'Are you sure?'}, method: :delete, class: "btn-remove btn delete-key"
+ = link_to 'Remove', namespace_project_deploy_key_path(@project.namespace, @project, @key), data: { confirm: 'Are you sure?'}, method: :delete, class: "btn-remove btn delete-key"
diff --git a/app/views/projects/diffs/_diffs.html.haml b/app/views/projects/diffs/_diffs.html.haml
index 334ea1ba82f..48d4c33ce85 100644
--- a/app/views/projects/diffs/_diffs.html.haml
+++ b/app/views/projects/diffs/_diffs.html.haml
@@ -2,15 +2,9 @@
.col-md-8
= render 'projects/diffs/stats', diffs: diffs
.col-md-4
- %ul.nav.nav-tabs
- %li.pull-right{class: params[:view] == 'parallel' ? 'active' : ''}
- - params_copy = params.dup
- - params_copy[:view] = 'parallel'
- = link_to "Side-by-side Diff", url_for(params_copy), {id: "commit-diff-viewtype"}
- %li.pull-right{class: params[:view] != 'parallel' ? 'active' : ''}
- - params_copy[:view] = 'inline'
- = link_to "Inline Diff", url_for(params_copy), {id: "commit-diff-viewtype"}
-
+ .btn-group.pull-right
+ = inline_diff_btn
+ = parallel_diff_btn
- if show_diff_size_warning?(diffs)
= render 'projects/diffs/warning', diffs: diffs
diff --git a/app/views/projects/diffs/_file.html.haml b/app/views/projects/diffs/_file.html.haml
index bf7770ceedf..2569e91ccfa 100644
--- a/app/views/projects/diffs/_file.html.haml
+++ b/app/views/projects/diffs/_file.html.haml
@@ -1,14 +1,17 @@
- blob = project.repository.blob_for_diff(@commit, diff_file.diff)
- return unless blob
-- blob_diff_path = diff_project_blob_path(project, tree_join(@commit.id, diff_file.file_path))
+- blob_diff_path = namespace_project_blob_diff_path(project.namespace, project, tree_join(@commit.id, diff_file.file_path))
.diff-file{id: "diff-#{i}", data: {blob_diff_path: blob_diff_path }}
.diff-header{id: "file-path-#{hexdigest(diff_file.new_path || diff_file.old_path)}"}
- if diff_file.deleted_file
- %span= diff_file.old_path
+ %span="#{diff_file.old_path} deleted"
.diff-btn-group
- if @commit.parent_ids.present?
= view_file_btn(@commit.parent_id, diff_file, project)
+ - elsif diff_file.diff.submodule?
+ - submodule_item = project.repository.blob_at(@commit.id, diff_file.file_path)
+ = submodule_link(submodule_item, @commit.id)
- else
- if diff_file.renamed_file
%span= "#{diff_file.old_path} renamed to #{diff_file.new_path}"
@@ -26,13 +29,13 @@
&nbsp;
= link_to '#', class: 'js-toggle-diff-comments btn btn-small' do
%i.fa.fa-chevron-down
- Diff comments
+ Show/Hide comments
&nbsp;
- if @merge_request && @merge_request.source_project
- = link_to project_edit_tree_path(@merge_request.source_project, tree_join(@merge_request.source_branch, diff_file.new_path), from_merge_request_id: @merge_request.id), { class: 'btn btn-small' } do
- Edit
- &nbsp;
+ = edit_blob_link(@merge_request.source_project,
+ @merge_request.source_branch, diff_file.new_path,
+ after: '&nbsp;', from_merge_request_id: @merge_request.id)
= view_file_btn(@commit.id, diff_file, project)
diff --git a/app/views/projects/diffs/_image.html.haml b/app/views/projects/diffs/_image.html.haml
index 900646dd0a4..058b71b21f5 100644
--- a/app/views/projects/diffs/_image.html.haml
+++ b/app/views/projects/diffs/_image.html.haml
@@ -10,7 +10,7 @@
%div.two-up.view
%span.wrap
.frame.deleted
- %a{href: project_blob_path(@project, tree_join(@commit.parent_id, diff.old_path))}
+ %a{href: namespace_project_blob_path(@project.namespace, @project, tree_join(@commit.parent_id, diff.old_path))}
%img{src: "data:#{old_file.mime_type};base64,#{Base64.encode64(old_file.data)}"}
%p.image-info.hide
%span.meta-filesize= "#{number_to_human_size old_file.size}"
@@ -22,7 +22,7 @@
%span.meta-height
%span.wrap
.frame.added
- %a{href: project_blob_path(@project, tree_join(@commit.id, diff.new_path))}
+ %a{href: namespace_project_blob_path(@project.namespace, @project, tree_join(@commit.id, diff.new_path))}
%img{src: "data:#{file.mime_type};base64,#{Base64.encode64(file.data)}"}
%p.image-info.hide
%span.meta-filesize= "#{number_to_human_size file.size}"
diff --git a/app/views/projects/diffs/_warning.html.haml b/app/views/projects/diffs/_warning.html.haml
index 86ed6bbeaa2..c9a6b3ebd9e 100644
--- a/app/views/projects/diffs/_warning.html.haml
+++ b/app/views/projects/diffs/_warning.html.haml
@@ -7,13 +7,13 @@
- if current_controller?(:commit) or current_controller?(:merge_requests)
- if current_controller?(:commit)
- = link_to "Plain diff", project_commit_path(@project, @commit, format: :diff), class: "btn btn-warning btn-small"
- = link_to "Email patch", project_commit_path(@project, @commit, format: :patch), class: "btn btn-warning btn-small"
+ = link_to "Plain diff", namespace_project_commit_path(@project.namespace, @project, @commit, format: :diff), class: "btn btn-warning btn-small"
+ = link_to "Email patch", namespace_project_commit_path(@project.namespace, @project, @commit, format: :patch), class: "btn btn-warning btn-small"
- elsif @merge_request && @merge_request.persisted?
- = link_to "Plain diff", project_merge_request_path(@project, @merge_request, format: :diff), class: "btn btn-warning btn-small"
- = link_to "Email patch", project_merge_request_path(@project, @merge_request, format: :patch), class: "btn btn-warning btn-small"
+ = link_to "Plain diff", merge_request_path(@merge_request, format: :diff), class: "btn btn-warning btn-small"
+ = link_to "Email patch", merge_request_path(@merge_request, format: :patch), class: "btn btn-warning btn-small"
%p
To preserve performance only
%strong #{allowed_diff_size} of #{diffs.size}
- files displayed.
+ files are displayed.
diff --git a/app/views/projects/edit.html.haml b/app/views/projects/edit.html.haml
index b85cf7d8d37..b4c36beda88 100644
--- a/app/views/projects/edit.html.haml
+++ b/app/views/projects/edit.html.haml
@@ -3,11 +3,11 @@
.project-edit-content
%div
%h3.page-title
- Project settings:
- %p.light Some settings, such as "Transfer Project", are hidden inside the danger area below.
+ Project settings
%hr
.panel-body
- = form_for @project, remote: true, html: { class: "edit_project form-horizontal" } do |f|
+ = form_for [@project.namespace.becomes(Namespace), @project], remote: true, html: { multipart: true, class: "edit_project form-horizontal" }, authenticity_token: true do |f|
+
%fieldset
.form-group.project_name_holder
= f.label :name, class: 'control-label' do
@@ -50,15 +50,6 @@
= f.check_box :issues_enabled
%span.descr Lightweight issue tracking system for this project
- - if Project.issues_tracker.values.count > 1
- .form-group
- = f.label :issues_tracker, "Issues tracker", class: 'control-label'
- .col-sm-10= f.select(:issues_tracker, project_issues_trackers(@project.issues_tracker), {}, { disabled: !@project.issues_enabled })
-
- .form-group
- = f.label :issues_tracker_id, "Project name or id in issues tracker", class: 'control-label'
- .col-sm-10= f.text_field :issues_tracker_id, disabled: !@project.can_have_issues_tracker_id?, class: 'form-control'
-
.form-group
= f.label :merge_requests_enabled, "Merge Requests", class: 'control-label'
.col-sm-10
@@ -80,6 +71,32 @@
= f.check_box :snippets_enabled
%span.descr Share code pastes with others out of git repository
+ %fieldset.features
+ %legend
+ Project avatar:
+ .form-group
+ .col-sm-2
+ .col-sm-10
+ - if @project.avatar?
+ = project_icon("#{@project.namespace.to_param}/#{@project.to_param}", alt: '', class: 'avatar project-avatar s160')
+ %p.light
+ - if @project.avatar_in_git
+ Project avatar in repository: #{ @project.avatar_in_git }
+ %p.light
+ - if @project.avatar?
+ You can change your project avatar here
+ - else
+ You can upload a project avatar here
+ %a.choose-btn.btn.btn-small.js-choose-project-avatar-button
+ %i.icon-paper-clip
+ %span Choose File ...
+ &nbsp;
+ %span.file_name.js-avatar-filename File name...
+ = f.file_field :avatar, class: "js-project-avatar-input hidden"
+ .light The maximum file size allowed is 200KB.
+ - if @project.avatar?
+ %hr
+ = link_to 'Remove avatar', namespace_project_avatar_path(@project.namespace, @project), data: { confirm: "Project 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"
@@ -99,7 +116,7 @@
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),
+ = link_to 'Unarchive', unarchive_namespace_project_path(@project.namespace, @project),
data: { confirm: "Are you sure that you want to unarchive this project?\nWhen this project is unarchived it is active and can be committed to again." },
method: :post, class: "btn btn-success"
- else
@@ -113,7 +130,7 @@
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),
+ = link_to 'Archive', archive_namespace_project_path(@project.namespace, @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-warning"
- else
@@ -123,7 +140,7 @@
.panel-heading Rename repository
.errors-holder
.panel-body
- = form_for(@project, html: { class: 'form-horizontal' }) do |f|
+ = form_for([@project.namespace.becomes(Namespace), @project], html: { class: 'form-horizontal' }) do |f|
.form-group.project_name_holder
= f.label :name, class: 'control-label' do
Project name
@@ -136,6 +153,8 @@
.col-sm-9
.form-group
.input-group
+ .input-group-addon
+ #{URI.join(root_url, @project.namespace.path)}/
= f.text_field :path, class: 'form-control'
%span.input-group-addon .git
%ul
@@ -149,13 +168,13 @@
.panel-heading Transfer project
.errors-holder
.panel-body
- = form_for(@project, url: transfer_project_path(@project), method: :put, remote: true, html: { class: 'transfer-project form-horizontal' }) do |f|
+ = form_for([@project.namespace.becomes(Namespace), @project], url: transfer_namespace_project_path(@project.namespace, @project), method: :put, remote: true, html: { class: 'transfer-project form-horizontal' }) do |f|
.form-group
- = f.label :namespace_id, class: 'control-label' do
+ = label_tag :new_namespace_id, nil, class: 'control-label' do
%span Namespace
.col-sm-10
.form-group
- = f.select :namespace_id, namespaces_options(@project.namespace_id), { prompt: 'Choose a project namespace' }, { class: 'select2' }
+ = select_tag :new_namespace_id, namespaces_options(@project.namespace_id), { prompt: 'Choose a project namespace', class: 'select2' }
%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.
@@ -169,7 +188,7 @@
.panel.panel-default.panel.panel-danger
.panel-heading Remove project
.panel-body
- = form_tag(project_path(@project), method: :delete, html: { class: 'form-horizontal'}) do
+ = form_tag(namespace_project_path(@project.namespace, @project), method: :delete, html: { class: 'form-horizontal'}) do
%p
Removing the project will delete its repository and all related resources including issues, merge requests etc.
%br
diff --git a/app/views/projects/edit_tree/show.html.haml b/app/views/projects/edit_tree/show.html.haml
deleted file mode 100644
index 5ccde05063e..00000000000
--- a/app/views/projects/edit_tree/show.html.haml
+++ /dev/null
@@ -1,72 +0,0 @@
-.file-editor
- %ul.nav.nav-tabs.js-edit-mode
- %li.active
- = link_to 'Edit', '#editor'
- %li
- = link_to editing_preview_title(@blob.name), '#preview', 'data-preview-url' => preview_project_edit_tree_path(@project, @id)
-
- = form_tag(project_edit_tree_path(@project, @id), method: :put, class: "form-horizontal") do
- .file-holder.file
- .file-title
- %i.fa.fa-file
- %span.file_name
- %span.monospace.light #{@ref}:
- = @path
- %span.options
- .btn-group.tree-btn-group
- = link_to "Cancel", @after_edit_path, class: "btn btn-tiny btn-cancel", data: { confirm: leave_edit_message }
- .file-content.code
- %pre.js-edit-mode-pane#editor
- .js-edit-mode-pane#preview.hide
- .center
- %h2
- %i.fa.fa-spinner.fa-spin
- = render 'shared/commit_message_container', params: params,
- placeholder: "Update #{@blob.name}"
- = hidden_field_tag 'last_commit', @last_commit
- = hidden_field_tag 'content', '', id: "file-content"
- = hidden_field_tag 'from_merge_request_id', params[:from_merge_request_id]
- = render 'projects/commit_button', ref: @ref,
- cancel_path: @after_edit_path
-
-:javascript
- ace.config.set("modePath", gon.relative_url_root + "#{Gitlab::Application.config.assets.prefix}/ace")
- ace.config.loadModule("ace/ext/searchbox");
- var ace_mode = "#{@blob.language.try(:ace_mode)}";
- var editor = ace.edit("editor");
- editor.setValue("#{escape_javascript(@blob.data)}");
- if (ace_mode) {
- editor.getSession().setMode('ace/mode/' + ace_mode);
- }
-
- disableButtonIfEmptyField("#commit_message", ".js-commit-button");
-
- $(".js-commit-button").click(function(){
- $("#file-content").val(editor.getValue());
- $(".file-editor form").submit();
- });
-
- var editModePanes = $('.js-edit-mode-pane'),
- editModeLinks = $('.js-edit-mode a');
-
- editModeLinks.click(function(event) {
- event.preventDefault();
-
- var currentLink = $(this),
- paneId = currentLink.attr('href'),
- currentPane = editModePanes.filter(paneId);
-
- editModeLinks.parent().removeClass('active hover');
- currentLink.parent().addClass('active hover');
- editModePanes.hide();
-
- if (paneId == '#preview') {
- currentPane.fadeIn(200);
- $.post(currentLink.data('preview-url'), { content: editor.getValue() }, function(response) {
- currentPane.empty().append(response);
- })
- } else {
- currentPane.fadeIn(200);
- editor.focus()
- }
- })
diff --git a/app/views/projects/empty.html.haml b/app/views/projects/empty.html.haml
index 59f19c8b7a3..49806ceaa96 100644
--- a/app/views/projects/empty.html.haml
+++ b/app/views/projects/empty.html.haml
@@ -1,5 +1,20 @@
+- if current_user && can?(current_user, :download_code, @project)
+ = render 'shared/no_ssh'
+ = render 'shared/no_password'
+
= render "home_panel"
+.center.well
+ %h3
+ The repository for this project is empty
+ %h4
+ You can
+ = link_to namespace_project_new_blob_path(@project.namespace, @project, 'master'), class: 'btn btn-new btn-lg' do
+ add a file
+ &nbsp;or do a push via the command line.
+
+%h4
+ %strong Command line instructions
%div.git-empty
%fieldset
%legend Git global setup
@@ -31,4 +46,4 @@
- if can? current_user, :remove_project, @project
.prepend-top-20
- = link_to 'Remove project', @project, data: { confirm: remove_project_message(@project)}, method: :delete, class: "btn btn-remove pull-right"
+ = link_to 'Remove project', [@project.namespace.becomes(Namespace), @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
deleted file mode 100644
index d8f5c7b98d6..00000000000
--- a/app/views/projects/fork.html.haml
+++ /dev/null
@@ -1,19 +0,0 @@
-.alert.alert-danger.alert-block
- %h4
- %i.fa.fa-code-fork
- Fork Error!
- %p
- You tried to fork
- = link_to_project @project
- but it failed for the following reason:
-
-
- - if @forked_project && @forked_project.errors.any?
- %p
- &ndash;
- = @forked_project.errors.full_messages.first
-
- %p
- = link_to fork_project_path(@project), title: "Fork", class: "btn", method: "POST" do
- %i.fa.fa-code-fork
- Try to Fork again
diff --git a/app/views/projects/forks/error.html.haml b/app/views/projects/forks/error.html.haml
new file mode 100644
index 00000000000..8eb4f795971
--- /dev/null
+++ b/app/views/projects/forks/error.html.haml
@@ -0,0 +1,20 @@
+- if @forked_project && !@forked_project.saved?
+ .alert.alert-danger.alert-block
+ %h4
+ %i.fa.fa-code-fork
+ Fork Error!
+ %p
+ You tried to fork
+ = link_to_project @project
+ but it failed for the following reason:
+
+
+ - if @forked_project && @forked_project.errors.any?
+ %p
+ &ndash;
+ = @forked_project.errors.full_messages.first
+
+ %p
+ = link_to new_namespace_project_fork_path(@project.namespace, @project), title: "Fork", class: "btn" do
+ %i.fa.fa-code-fork
+ Try to Fork again
diff --git a/app/views/projects/forks/new.html.haml b/app/views/projects/forks/new.html.haml
new file mode 100644
index 00000000000..5a6c46f3208
--- /dev/null
+++ b/app/views/projects/forks/new.html.haml
@@ -0,0 +1,39 @@
+%h3.page-title Fork project
+%p.lead
+ Click to fork the project to a user or group
+%hr
+
+.fork-namespaces
+ - @namespaces.in_groups_of(6, false) do |group|
+ .row
+ - group.each do |namespace|
+ .col-md-2.col-sm-3
+ - if fork = namespace.find_fork_of(@project)
+ .thumbnail.fork-exists-thumbnail
+ = link_to project_path(fork), title: "Visit project fork", class: 'has_tooltip' do
+ = image_tag namespace_icon(namespace, 200)
+ .caption
+ %h4=namespace.human_name
+ %p
+ = namespace.path
+ - else
+ .thumbnail.fork-thumbnail
+ = link_to namespace_project_fork_path(@project.namespace, @project, namespace_key: namespace.id), title: "Fork here", method: "POST", class: 'has_tooltip' do
+ = image_tag namespace_icon(namespace, 200)
+ .caption
+ %h4=namespace.human_name
+ %p
+ = namespace.path
+
+ %p.light
+ Fork is a copy of a project repository.
+ %br
+ Forking a repository allows you to do changes without affecting the original project.
+
+.save-project-loader.hide
+ .center
+ %h2
+ %i.fa.fa-spinner.fa-spin
+ Forking repository
+ %p Please wait a moment, this page will automatically refresh when ready.
+
diff --git a/app/views/projects/graphs/_head.html.haml b/app/views/projects/graphs/_head.html.haml
index 9f37a760e61..9383df13305 100644
--- a/app/views/projects/graphs/_head.html.haml
+++ b/app/views/projects/graphs/_head.html.haml
@@ -1,5 +1,5 @@
%ul.nav.nav-tabs
= nav_link(action: :show) do
- = link_to 'Contributors', project_graph_path
+ = link_to 'Contributors', namespace_project_graph_path
= nav_link(action: :commits) do
- = link_to 'Commits', commits_project_graph_path
+ = link_to 'Commits', commits_namespace_project_graph_path
diff --git a/app/views/projects/hooks/index.html.haml b/app/views/projects/hooks/index.html.haml
index 9a003c87f68..e70cf5c3884 100644
--- a/app/views/projects/hooks/index.html.haml
+++ b/app/views/projects/hooks/index.html.haml
@@ -7,7 +7,7 @@
%hr.clearfix
-= form_for [@project, @hook], as: :hook, url: project_hooks_path(@project), html: { class: 'form-horizontal' } do |f|
+= form_for [@project.namespace.becomes(Namespace), @project, @hook], as: :hook, url: namespace_project_hooks_path(@project.namespace, @project), html: { class: 'form-horizontal' } do |f|
-if @hook.errors.any?
.alert.alert-danger
- @hook.errors.full_messages.each do |msg|
@@ -58,8 +58,8 @@
- @hooks.each do |hook|
%li
.pull-right
- = link_to 'Test Hook', test_project_hook_path(@project, hook), class: "btn btn-small btn-grouped"
- = link_to 'Remove', project_hook_path(@project, hook), data: { confirm: 'Are you sure?'}, method: :delete, class: "btn btn-remove btn-small btn-grouped"
+ = link_to 'Test Hook', test_namespace_project_hook_path(@project.namespace, @project, hook), class: "btn btn-small btn-grouped"
+ = link_to 'Remove', namespace_project_hook_path(@project.namespace, @project, hook), data: { confirm: 'Are you sure?'}, method: :delete, class: "btn btn-remove btn-small btn-grouped"
.clearfix
%span.monospace= hook.url
%p
diff --git a/app/views/projects/import.html.haml b/app/views/projects/import.html.haml
deleted file mode 100644
index 4513c89e784..00000000000
--- a/app/views/projects/import.html.haml
+++ /dev/null
@@ -1,31 +0,0 @@
-- if @project.import_in_progress?
- .save-project-loader
- .center
- %h2
- %i.fa.fa-spinner.fa-spin
- Import in progress.
- %p.monospace git clone --bare #{hidden_pass_url(@project.import_url)}
- %p Please wait while we import the repository for you. Refresh at will.
- :javascript
- new ProjectImport();
-
-- elsif @project.import_failed?
- .save-project-loader
- .center
- %h2
- Import failed. Retry?
- %hr
- - if can?(current_user, :admin_project, @project)
- = form_for @project, url: retry_import_project_path(@project), method: :put, html: { class: 'form-horizontal' } do |f|
- .form-group.import-url-data
- = f.label :import_url, class: 'control-label' do
- %span Import existing git repo
- .col-sm-10
- = f.text_field :import_url, class: 'form-control', placeholder: 'https://github.com/randx/six.git'
- .bs-callout.bs-callout-info
- This URL must be publicly accessible or you can add a username and password like this: https://username:password@gitlab.com/company/project.git.
- %br
- The import will time out after 4 minutes. For big repositories, use a clone/push combination.
- For SVN repositories, check #{link_to "this migrating from SVN doc.", "http://doc.gitlab.com/ce/workflow/migrating_from_svn.html"}
- .form-actions
- = f.submit 'Retry import', class: "btn btn-create", tabindex: 4
diff --git a/app/views/projects/imports/new.html.haml b/app/views/projects/imports/new.html.haml
new file mode 100644
index 00000000000..097374e1128
--- /dev/null
+++ b/app/views/projects/imports/new.html.haml
@@ -0,0 +1,21 @@
+%h3.page-title
+ - if @project.import_failed?
+ Import failed. Retry?
+ - else
+ Import repository
+
+%hr
+
+= form_for @project, url: namespace_project_import_path(@project.namespace, @project), method: :post, html: { class: 'form-horizontal' } do |f|
+ .form-group.import-url-data
+ = f.label :import_url, class: 'control-label' do
+ %span Import existing git repo
+ .col-sm-10
+ = f.text_field :import_url, class: 'form-control', placeholder: 'https://github.com/randx/six.git'
+ .bs-callout.bs-callout-info
+ This URL must be publicly accessible or you can add a username and password like this: https://username:password@gitlab.com/company/project.git.
+ %br
+ The import will time out after 4 minutes. For big repositories, use a clone/push combination.
+ For SVN repositories, check #{link_to "this migrating from SVN doc.", "http://doc.gitlab.com/ce/workflow/migrating_from_svn.html"}
+ .form-actions
+ = f.submit 'Start import', class: "btn btn-create", tabindex: 4
diff --git a/app/views/projects/imports/show.html.haml b/app/views/projects/imports/show.html.haml
new file mode 100644
index 00000000000..2d1fdafed24
--- /dev/null
+++ b/app/views/projects/imports/show.html.haml
@@ -0,0 +1,9 @@
+.save-project-loader
+ .center
+ %h2
+ %i.fa.fa-spinner.fa-spin
+ Import in progress.
+ %p.monospace git clone --bare #{hidden_pass_url(@project.import_url)}
+ %p Please wait while we import the repository for you. Refresh at will.
+ :javascript
+ new ProjectImport();
diff --git a/app/views/projects/issues/_discussion.html.haml b/app/views/projects/issues/_discussion.html.haml
new file mode 100644
index 00000000000..fc3e35640dc
--- /dev/null
+++ b/app/views/projects/issues/_discussion.html.haml
@@ -0,0 +1,37 @@
+- content_for :note_actions do
+ - if can?(current_user, :modify_issue, @issue)
+ - if @issue.closed?
+ = link_to 'Reopen Issue', issue_path(@issue, issue: {state_event: :reopen }, status_only: true), method: :put, class: "btn btn-grouped btn-reopen js-note-target-reopen", title: 'Reopen Issue'
+ - else
+ = link_to 'Close Issue', issue_path(@issue, issue: {state_event: :close }, status_only: true), method: :put, class: "btn btn-grouped btn-close js-note-target-close", title: "Close Issue"
+.row
+ %section.col-md-9
+ .participants
+ %span= pluralize(@issue.participants.count, 'participant')
+ - @issue.participants.each do |participant|
+ = link_to_member(@project, participant, name: false, size: 24)
+
+ .voting_notes#notes= render "projects/notes/notes_with_form"
+ %aside.col-md-3
+ .issuable-affix
+ .clearfix
+ %span.slead.has_tooltip{:"data-original-title" => 'Cross-project reference'}
+ = cross_project_reference(@project, @issue)
+ %hr
+ .context
+ = render partial: 'issue_context', locals: { issue: @issue }
+ %hr
+ .clearfix
+ .votes-holder
+ %h6 Votes
+ #votes= render 'votes/votes_block', votable: @issue
+
+ - if @issue.labels.any?
+ %hr
+ %h6 Labels
+ .issue-show-labels
+ - @issue.labels.each do |label|
+ = link_to namespace_project_issues_path(@project.namespace, @project, label_name: label.name) do
+ = render_colored_label(label)
+ = link_to '#aside', class: 'show-aside' do
+ %i.fa.fa-angle-left
diff --git a/app/views/projects/issues/_form.html.haml b/app/views/projects/issues/_form.html.haml
index 64a28d8da49..76075124f9e 100644
--- a/app/views/projects/issues/_form.html.haml
+++ b/app/views/projects/issues/_form.html.haml
@@ -1,14 +1,8 @@
%div.issue-form-holder
%h3.page-title= @issue.new_record? ? "New Issue" : "Edit Issue ##{@issue.iid}"
%hr
- - if @repository.exists? && !@repository.empty? && @repository.contribution_guide && !@issue.persisted?
- - contribution_guide_url = project_blob_path(@project, tree_join(@repository.root_ref, @repository.contribution_guide.name))
- .row
- .col-sm-10.col-sm-offset-2
- .alert.alert-info
- = "Please review the <strong>#{link_to "guidelines for contribution", contribution_guide_url}</strong> to this repository.".html_safe
- = form_for [@project, @issue], html: { class: 'form-horizontal issue-form gfm-form' } do |f|
+ = form_for [@project.namespace.becomes(Namespace), @project, @issue], html: { class: 'form-horizontal issue-form gfm-form' } do |f|
= render 'projects/issuable_form', f: f, issuable: @issue
:javascript
@@ -17,4 +11,4 @@
e.preventDefault();
});
- window.project_image_path_upload = "#{upload_image_project_path @project}";
+ window.project_uploads_path = "#{namespace_project_uploads_path @project.namespace, @project}";
diff --git a/app/views/projects/issues/_head.html.haml b/app/views/projects/issues/_head.html.haml
deleted file mode 100644
index 1d2f3ed8118..00000000000
--- a/app/views/projects/issues/_head.html.haml
+++ /dev/null
@@ -1,36 +0,0 @@
-%ul.nav.nav-tabs
- = nav_link(controller: :issues) do
- = link_to project_issues_path(@project), class: "tab" do
- Browse Issues
- = 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_controller?(:milestones)
- %li.pull-right
- %button.btn.btn-default.sidebar-expand-button
- %i.icon.fa.fa-list
-
- - if current_controller?(:issues)
- - if current_user
- %li
- = link_to project_issues_path(@project, :atom, { private_token: current_user.private_token }) do
- %i.fa.fa-rss
-
- %li.pull-right
- .pull-right
- %button.btn.btn-default.sidebar-expand-button
- %i.icon.fa.fa-list
- = form_tag project_issues_path(@project), method: :get, id: "issue_search_form", class: 'pull-left issue-search-form' do
- .append-right-10.hidden-xs.hidden-sm
- = search_field_tag :issue_search, params[:issue_search], { placeholder: 'Filter by title or description', class: 'form-control issue_search search-text-input input-mn-300' }
- = hidden_field_tag :state, params['state']
- = hidden_field_tag :scope, params['scope']
- = hidden_field_tag :assignee_id, params['assignee_id']
- = hidden_field_tag :milestone_id, params['milestone_id']
- = hidden_field_tag :label_id, params['label_id']
- - 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-left", title: "New Issue", id: "new_issue_link" do
- %i.fa.fa-plus
- New Issue
diff --git a/app/views/projects/issues/_issue.html.haml b/app/views/projects/issues/_issue.html.haml
index 7525812696f..01e2133e283 100644
--- a/app/views/projects/issues/_issue.html.haml
+++ b/app/views/projects/issues/_issue.html.haml
@@ -1,25 +1,27 @@
-%li{ id: dom_id(issue), class: issue_css_classes(issue), url: project_issue_path(issue.project, issue) }
+%li{ id: dom_id(issue), class: issue_css_classes(issue), url: issue_path(issue) }
- if controller.controller_name == 'issues'
.issue-check
= check_box_tag dom_id(issue,"selected"), nil, false, 'data-id' => issue.id, class: "selected_issue", disabled: !can?(current_user, :modify_issue, issue)
.issue-title
- %span.light= "##{issue.iid}"
%span.str-truncated
- = link_to_gfm issue.title, project_issue_path(issue.project, issue), class: "row_title"
- - if issue.closed?
- %small.pull-right
- CLOSED
+ = link_to_gfm issue.title, issue_path(issue), class: "row_title"
+ .pull-right.light
+ - if issue.closed?
+ %span
+ CLOSED
+ - if issue.notes.any?
+ &nbsp;
+ %span
+ %i.fa.fa-comments
+ = issue.notes.count
.issue-info
+ %span.light= "##{issue.iid}"
- if issue.assignee
assigned to #{link_to_member(@project, issue.assignee)}
- if issue.votes_count > 0
= render 'votes/votes_inline', votable: issue
- - if issue.notes.any?
- %span
- %i.fa.fa-comments
- = issue.notes.count
- if issue.milestone
%span
%i.fa.fa-clock-o
@@ -28,21 +30,21 @@
%span.task-status
= issue.task_status
- .pull-right
+ .pull-right.issue-updated-at
%small updated #{time_ago_with_tooltip(issue.updated_at, 'bottom', 'issue_update_ago')}
.issue-labels
- issue.labels.each do |label|
- = link_to project_issues_path(issue.project, label_name: label.name) do
+ = link_to namespace_project_issues_path(issue.project.namespace, issue.project, label_name: label.name) do
= render_colored_label(label)
.issue-actions
- if can? current_user, :modify_issue, issue
- if issue.closed?
- = link_to 'Reopen', project_issue_path(issue.project, issue, issue: {state_event: :reopen }, status_only: true), method: :put, class: "btn btn-small btn-grouped reopen_issue btn-reopen", remote: true
+ = link_to 'Reopen', issue_path(issue, issue: {state_event: :reopen }, status_only: true), method: :put, class: "btn btn-small btn-grouped reopen_issue btn-reopen", remote: true
- else
- = link_to 'Close', project_issue_path(issue.project, issue, issue: {state_event: :close }, status_only: true), method: :put, class: "btn btn-small btn-grouped close_issue btn-close", remote: true
- = link_to edit_project_issue_path(issue.project, issue), class: "btn btn-small edit-issue-link btn-grouped" do
+ = link_to 'Close', issue_path(issue, issue: {state_event: :close }, status_only: true), method: :put, class: "btn btn-small btn-grouped close_issue btn-close", remote: true
+ = link_to edit_namespace_project_issue_path(issue.project.namespace, issue.project, issue), class: "btn btn-small edit-issue-link btn-grouped" do
%i.fa.fa-pencil-square-o
Edit
diff --git a/app/views/projects/issues/_issue_context.html.haml b/app/views/projects/issues/_issue_context.html.haml
index 648f459dc9e..4c7654354f4 100644
--- a/app/views/projects/issues/_issue_context.html.haml
+++ b/app/views/projects/issues/_issue_context.html.haml
@@ -1,25 +1,28 @@
-= form_for [@project, @issue], remote: true, html: {class: 'edit-issue inline-update'} do |f|
- .row
- .col-sm-6
- %strong.append-right-10
+= form_for [@project.namespace.becomes(Namespace), @project, @issue], remote: true, html: {class: 'edit-issue inline-update'} do |f|
+ %div.prepend-top-20
+ .issuable-context-title
+ %label
Assignee:
-
- - if can?(current_user, :modify_issue, @issue)
- = project_users_select_tag('issue[assignee_id]', placeholder: 'Select assignee', class: 'custom-form-control js-select2 js-assignee', selected: @issue.assignee_id)
- - elsif issue.assignee
- = link_to_member(@project, @issue.assignee)
+ - if issue.assignee
+ %strong= link_to_member(@project, @issue.assignee, size: 24)
- else
- None
+ none
+ - if can?(current_user, :modify_issue, @issue)
+ = project_users_select_tag('issue[assignee_id]', placeholder: 'Select assignee', class: 'custom-form-control js-select2 js-assignee', selected: @issue.assignee_id)
- .col-sm-6.text-right
- %strong.append-right-10
+ %div.prepend-top-20.clearfix
+ .issuable-context-title
+ %label
Milestone:
- - if can?(current_user, :modify_issue, @issue)
- = f.select(:milestone_id, milestone_options(@issue), { include_blank: "Select milestone" }, {class: 'select2 select2-compact js-select2 js-milestone'})
- = hidden_field_tag :issue_context
- = f.submit class: 'btn'
- - elsif issue.milestone
- = link_to project_milestone_path(@project, @issue.milestone) do
- = @issue.milestone.title
+ - if issue.milestone
+ %span.back-to-milestone
+ = link_to namespace_project_milestone_path(@project.namespace, @project, @issue.milestone) do
+ %strong
+ %i.fa.fa-clock-o
+ = @issue.milestone.title
- else
- None
+ none
+ - if can?(current_user, :modify_issue, @issue)
+ = f.select(:milestone_id, milestone_options(@issue), { include_blank: "Select milestone" }, {class: 'select2 select2-compact js-select2 js-milestone'})
+ = hidden_field_tag :issue_context
+ = f.submit class: 'btn'
diff --git a/app/views/projects/issues/_issues.html.haml b/app/views/projects/issues/_issues.html.haml
index 0bff8bdbead..5d243adb5fe 100644
--- a/app/views/projects/issues/_issues.html.haml
+++ b/app/views/projects/issues/_issues.html.haml
@@ -1,66 +1,3 @@
-.append-bottom-10
- .check-all-holder
- = check_box_tag "check_all_issues", nil, false, class: "check_all_issues left"
- .issues-filters
- .dropdown.inline
- %a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"}
- %i.fa.fa-user
- %span.light assignee:
- - if @assignee.present?
- %strong= @assignee.name
- - elsif params[:assignee_id] == "0"
- Unassigned
- - else
- Any
- %b.caret
- %ul.dropdown-menu
- %li
- = link_to project_filter_path(assignee_id: nil) do
- Any
- = link_to project_filter_path(assignee_id: 0) do
- Unassigned
- - @assignees.sort_by(&:name).each do |user|
- %li
- = link_to project_filter_path(assignee_id: user.id) do
- = image_tag avatar_icon(user.email), class: "avatar s16", alt: ''
- = user.name
-
- .dropdown.inline.prepend-left-10
- %a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"}
- %i.fa.fa-clock-o
- %span.light milestone:
- - if @milestone.present?
- %strong= @milestone.title
- - elsif params[:milestone_id] == "0"
- None (backlog)
- - else
- Any
- %b.caret
- %ul.dropdown-menu
- %li
- = link_to project_filter_path(milestone_id: nil) do
- Any
- = link_to project_filter_path(milestone_id: 0) do
- None (backlog)
- - project_active_milestones.each do |milestone|
- %li
- = link_to project_filter_path(milestone_id: milestone.id) do
- %strong= milestone.title
- %small.light= milestone.expires_at
-
- .pull-right
- = render 'shared/sort_dropdown'
-
- .clearfix
- .issues_bulk_update.hide
- = form_tag bulk_update_project_issues_path(@project), method: :post do
- = select_tag('update[status]', options_for_select([['Open', 'open'], ['Closed', 'closed']]), prompt: "Status")
- = project_users_select_tag('update[assignee_id]', placeholder: '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 "Update issues", class: "btn update_selected_issues btn-save"
-
.panel.panel-default
%ul.well-list.issues-list
= render @issues
diff --git a/app/views/projects/issues/index.atom.builder b/app/views/projects/issues/index.atom.builder
index 012ba235951..126f2c07faa 100644
--- a/app/views/projects/issues/index.atom.builder
+++ b/app/views/projects/issues/index.atom.builder
@@ -1,23 +1,12 @@
xml.instruct!
xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://search.yahoo.com/mrss/" do
xml.title "#{@project.name} issues"
- xml.link :href => project_issues_url(@project, :atom), :rel => "self", :type => "application/atom+xml"
- xml.link :href => project_issues_url(@project), :rel => "alternate", :type => "text/html"
- xml.id project_issues_url(@project)
+ xml.link :href => namespace_project_issues_url(@project.namespace, @project, :atom), :rel => "self", :type => "application/atom+xml"
+ xml.link :href => namespace_project_issues_url(@project.namespace, @project), :rel => "alternate", :type => "text/html"
+ xml.id namespace_project_issues_url(@project.namespace, @project)
xml.updated @issues.first.created_at.strftime("%Y-%m-%dT%H:%M:%SZ") if @issues.any?
@issues.each do |issue|
- xml.entry do
- xml.id project_issue_url(@project, issue)
- xml.link :href => project_issue_url(@project, issue)
- xml.title truncate(issue.title, :length => 80)
- xml.updated issue.created_at.strftime("%Y-%m-%dT%H:%M:%SZ")
- xml.media :thumbnail, :width => "40", :height => "40", :url => avatar_icon(issue.author_email)
- xml.author do |author|
- xml.name issue.author_name
- xml.email issue.author_email
- end
- xml.summary issue.title
- end
+ issue_to_atom(xml, issue)
end
end
diff --git a/app/views/projects/issues/index.html.haml b/app/views/projects/issues/index.html.haml
index 4ec362b3063..7defc8787a9 100644
--- a/app/views/projects/issues/index.html.haml
+++ b/app/views/projects/issues/index.html.haml
@@ -1,9 +1,36 @@
-= render "head"
-.row
- .fixed.fixed.sidebar-expand-button.hidden-lg.hidden-md.hidden-xs
- %i.fa.fa-list.fa-2x
- .col-md-3.responsive-side
- = render 'shared/project_filter', project_entities_path: project_issues_path(@project),
- labels: true, redirect: 'issues', entity: 'issue'
- .col-md-9.issues-holder
- = render "issues"
+.append-bottom-10
+ .pull-right
+ .pull-left
+ - if current_user
+ .hidden-xs.pull-left
+ = link_to namespace_project_issues_path(@project.namespace, @project, :atom, { private_token: current_user.private_token }), class: 'btn append-right-10' do
+ %i.fa.fa-rss
+
+ = form_tag namespace_project_issues_path(@project.namespace, @project), method: :get, id: "issue_search_form", class: 'pull-left issue-search-form' do
+ .append-right-10.hidden-xs.hidden-sm
+ = search_field_tag :issue_search, params[:issue_search], { placeholder: 'Filter by title or description', class: 'form-control issue_search search-text-input input-mn-300' }
+ = hidden_field_tag :state, params['state']
+ = hidden_field_tag :scope, params['scope']
+ = hidden_field_tag :assignee_id, params['assignee_id']
+ = hidden_field_tag :milestone_id, params['milestone_id']
+ = hidden_field_tag :label_id, params['label_id']
+
+ - if can? current_user, :write_issue, @project
+ = link_to new_namespace_project_issue_path(@project.namespace, @project, issue: { assignee_id: params[:assignee_id], milestone_id: params[:milestone_id]}), class: "btn btn-new pull-left", title: "New Issue", id: "new_issue_link" do
+ %i.fa.fa-plus
+ New Issue
+
+ = render 'shared/issuable_filter'
+
+ .clearfix
+ .issues_bulk_update.hide
+ = form_tag bulk_update_namespace_project_issues_path(@project.namespace, @project), method: :post do
+ = select_tag('update[status]', options_for_select([['Open', 'open'], ['Closed', 'closed']]), prompt: "Status")
+ = project_users_select_tag('update[assignee_id]', placeholder: '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 "Update issues", class: "btn update_selected_issues btn-save"
+
+.issues-holder
+ = render "issues"
diff --git a/app/views/projects/issues/show.html.haml b/app/views/projects/issues/show.html.haml
index 71eb0d5c866..bd28d8a1db2 100644
--- a/app/views/projects/issues/show.html.haml
+++ b/app/views/projects/issues/show.html.haml
@@ -1,75 +1,40 @@
-%h3.page-title
- Issue ##{@issue.iid}
-
- %span.pull-right.issue-btn-group
- - if can?(current_user, :write_issue, @project)
- = link_to new_project_issue_path(@project), class: "btn btn-grouped", title: "New Issue", id: "new_issue_link" do
- %i.fa.fa-plus
- New Issue
- - if can?(current_user, :modify_issue, @issue)
- - if @issue.closed?
- = link_to 'Reopen', project_issue_path(@project, @issue, issue: {state_event: :reopen }, status_only: true), method: :put, class: "btn btn-grouped btn-reopen"
- - else
- = link_to 'Close', project_issue_path(@project, @issue, issue: {state_event: :close }, status_only: true), method: :put, class: "btn btn-grouped btn-close", title: "Close Issue"
-
- = link_to edit_project_issue_path(@project, @issue), class: "btn btn-grouped" do
- %i.fa.fa-pencil-square-o
- Edit
-
-.clearfix
- .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
- |
- %span.light Milestone
- = link_to project_milestone_path(@project, @issue.milestone) do
- = @issue.milestone.title
-
-.issue-box{ class: issue_box_class(@issue) }
- .state.clearfix
- .state-label
- - if @issue.closed?
- Closed
- - else
- Open
-
- .creator
- Created by #{link_to_member(@project, @issue.author)} #{time_ago_with_tooltip(@issue.created_at)}
-
- %h4.title
- = gfm escape_once(@issue.title)
-
- - if @issue.description.present?
- .description
- .wiki
- = preserve do
- = markdown(@issue.description, parse_tasks: true)
- .context
- %cite.cgray
- = render partial: 'issue_context', locals: { issue: @issue }
-
-
-- content_for :note_actions do
- - if can?(current_user, :modify_issue, @issue)
- - if @issue.closed?
- = link_to 'Reopen Issue', project_issue_path(@project, @issue, issue: {state_event: :reopen }, status_only: true), method: :put, class: "btn btn-grouped btn-reopen js-note-target-reopen", title: 'Reopen Issue'
- - else
- = link_to 'Close Issue', project_issue_path(@project, @issue, issue: {state_event: :close }, status_only: true), method: :put, class: "btn btn-grouped btn-close js-note-target-close", title: "Close Issue"
-
-.participants
- %cite.cgray
- = pluralize(@issue.participants.count, 'participant')
- - @issue.participants.each do |participant|
- = link_to_member(@project, participant, name: false, size: 24)
-
- .issue-show-labels.pull-right
- - @issue.labels.each do |label|
- = link_to project_issues_path(@project, label_name: label.name) do
- = render_colored_label(label)
-
-.voting_notes#notes= render "projects/notes/notes_with_form"
+.issue
+ .issue-details
+ %h4.page-title
+ .issue-box{ class: issue_box_class(@issue) }
+ - if @issue.closed?
+ Closed
+ - else
+ Open
+ Issue ##{@issue.iid}
+ %small.creator
+ &middot; created by #{link_to_member(@project, @issue.author)} #{issue_timestamp(@issue)}
+
+ .pull-right
+ - if can?(current_user, :write_issue, @project)
+ = link_to new_namespace_project_issue_path(@project.namespace, @project), class: "btn btn-grouped new-issue-link", title: "New Issue", id: "new_issue_link" do
+ %i.fa.fa-plus
+ New Issue
+ - if can?(current_user, :modify_issue, @issue)
+ - if @issue.closed?
+ = link_to 'Reopen', issue_path(@issue, issue: {state_event: :reopen }, status_only: true), method: :put, class: "btn btn-grouped btn-reopen"
+ - else
+ = link_to 'Close', issue_path(@issue, issue: {state_event: :close }, status_only: true), method: :put, class: "btn btn-grouped btn-close", title: "Close Issue"
+
+ = link_to edit_namespace_project_issue_path(@project.namespace, @project, @issue), class: "btn btn-grouped issuable-edit" do
+ %i.fa.fa-pencil-square-o
+ Edit
+
+ %hr
+ %h2.issue-title
+ = gfm escape_once(@issue.title)
+ %div
+ - if @issue.description.present?
+ .description
+ .wiki
+ = preserve do
+ = markdown(@issue.description, parse_tasks: true)
+
+ %hr
+ .issue-discussion
+ = render "projects/issues/discussion"
diff --git a/app/views/projects/issues/update.js.haml b/app/views/projects/issues/update.js.haml
index 5199e9fc61f..82c0e653759 100644
--- a/app/views/projects/issues/update.js.haml
+++ b/app/views/projects/issues/update.js.haml
@@ -3,8 +3,15 @@
:plain
$("##{dom_id(@issue)}").fadeOut();
- elsif params[:issue_context]
- $('.issue-box .context').effect('highlight');
+ $('.context').html("#{escape_javascript(render partial: 'issue_context', locals: { issue: @issue })}");
+ $('.context').effect('highlight');
- if @issue.milestone
- $('.milestone-nav-link').replaceWith("<span class='milestone-nav-link'>| <span class='light'>Milestone</span> #{escape_javascript(link_to @issue.milestone.title, project_milestone_path(@issue.project, @issue.milestone))}</span>")
+ $('.milestone-nav-link').replaceWith("<span class='milestone-nav-link'>| <span class='light'>Milestone</span> #{escape_javascript(link_to @issue.milestone.title, namespace_project_milestone_path(@issue.project.namespace, @issue.project, @issue.milestone))}</span>")
- else
$('.milestone-nav-link').html('')
+
+
+$('select.select2').select2({width: 'resolve', dropdownAutoWidth: true})
+$('.edit-issue.inline-update input[type="submit"]').hide();
+new ProjectUsersSelect();
+new Issue();
diff --git a/app/views/projects/labels/_form.html.haml b/app/views/projects/labels/_form.html.haml
index 72a01e1c271..95912536e42 100644
--- a/app/views/projects/labels/_form.html.haml
+++ b/app/views/projects/labels/_form.html.haml
@@ -1,4 +1,4 @@
-= form_for [@project, @label], html: { class: 'form-horizontal label-form' } do |f|
+= form_for [@project.namespace.becomes(Namespace), @project, @label], html: { class: 'form-horizontal label-form' } do |f|
-if @label.errors.any?
.row
.col-sm-10.col-sm-offset-2
@@ -16,7 +16,7 @@
.col-sm-10
.input-group
.input-group-addon.label-color-preview &nbsp;
- = f.text_field :color, placeholder: "#AA33EE", class: "form-control"
+ = f.color_field :color, placeholder: "#AA33EE", class: "form-control"
.help-block
6 character hex values starting with a # sign.
%br
@@ -29,5 +29,5 @@
.form-actions
= f.submit 'Save', class: 'btn btn-save js-save-button'
- = link_to "Cancel", project_labels_path(@project), class: 'btn btn-cancel'
+ = link_to "Cancel", namespace_project_labels_path(@project.namespace, @project), class: 'btn btn-cancel'
diff --git a/app/views/projects/labels/_label.html.haml b/app/views/projects/labels/_label.html.haml
index 03a8f0921b7..82829452862 100644
--- a/app/views/projects/labels/_label.html.haml
+++ b/app/views/projects/labels/_label.html.haml
@@ -2,9 +2,9 @@
= render_colored_label(label)
.pull-right
%strong.append-right-20
- = link_to project_issues_path(@project, label_name: label.name) do
+ = link_to namespace_project_issues_path(@project.namespace, @project, label_name: label.name) do
= pluralize label.open_issues_count, 'open issue'
- if can? current_user, :admin_label, @project
- = link_to 'Edit', edit_project_label_path(@project, label), class: 'btn'
- = link_to 'Remove', project_label_path(@project, label), class: 'btn btn-remove remove-row', method: :delete, remote: true, data: {confirm: "Remove this label? Are you sure?"}
+ = link_to 'Edit', edit_namespace_project_label_path(@project.namespace, @project, label), class: 'btn'
+ = link_to 'Remove', namespace_project_label_path(@project.namespace, @project, label), class: 'btn btn-remove remove-row', method: :delete, remote: true, data: {confirm: "Remove this label? Are you sure?"}
diff --git a/app/views/projects/labels/edit.html.haml b/app/views/projects/labels/edit.html.haml
index 52435c5d892..e003d1dfe7f 100644
--- a/app/views/projects/labels/edit.html.haml
+++ b/app/views/projects/labels/edit.html.haml
@@ -2,7 +2,7 @@
Edit label
%span.light #{@label.name}
.back-link
- = link_to project_labels_path(@project) do
+ = link_to namespace_project_labels_path(@project.namespace, @project) do
&larr; To labels list
%hr
= render 'form'
diff --git a/app/views/projects/labels/index.html.haml b/app/views/projects/labels/index.html.haml
index 06568278de8..0700e72d39c 100644
--- a/app/views/projects/labels/index.html.haml
+++ b/app/views/projects/labels/index.html.haml
@@ -1,7 +1,5 @@
-= render "projects/issues/head"
-
- if can? current_user, :admin_label, @project
- = link_to new_project_label_path(@project), class: "pull-right btn btn-new" do
+ = link_to new_namespace_project_label_path(@project.namespace, @project), class: "pull-right btn btn-new" do
New label
%h3.page-title
Labels
@@ -14,4 +12,4 @@
= paginate @labels, theme: 'gitlab'
- else
.light-well
- .nothing-here-block Create first label or #{link_to 'generate', generate_project_labels_path(@project), method: :post} default set of labels
+ .nothing-here-block Create first label or #{link_to 'generate', generate_namespace_project_labels_path(@project.namespace, @project), method: :post} default set of labels
diff --git a/app/views/projects/labels/new.html.haml b/app/views/projects/labels/new.html.haml
index 850da0b192b..0683ed5d4fb 100644
--- a/app/views/projects/labels/new.html.haml
+++ b/app/views/projects/labels/new.html.haml
@@ -1,6 +1,6 @@
%h3 New label
.back-link
- = link_to project_labels_path(@project) do
+ = link_to namespace_project_labels_path(@project.namespace, @project) do
&larr; To labels list
%hr
= render 'form'
diff --git a/app/views/projects/merge_requests/_discussion.html.haml b/app/views/projects/merge_requests/_discussion.html.haml
new file mode 100644
index 00000000000..79a093dc775
--- /dev/null
+++ b/app/views/projects/merge_requests/_discussion.html.haml
@@ -0,0 +1,33 @@
+- content_for :note_actions do
+ - if can?(current_user, :modify_merge_request, @merge_request)
+ - if @merge_request.open?
+ = link_to 'Close', merge_request_path(@merge_request, merge_request: {state_event: :close }), method: :put, class: "btn btn-grouped btn-close close-mr-link js-note-target-close", title: "Close merge request"
+ - if @merge_request.closed?
+ = link_to 'Reopen', merge_request_path(@merge_request, merge_request: {state_event: :reopen }), method: :put, class: "btn btn-grouped btn-reopen reopen-mr-link js-note-target-reopen", title: "Reopen merge request"
+
+.row
+ %section.col-md-9
+ = render "projects/merge_requests/show/participants"
+ = render "projects/notes/notes_with_form"
+ %aside.col-md-3
+ .issuable-affix
+ .clearfix
+ %span.slead.has_tooltip{:"data-original-title" => 'Cross-project reference'}
+ = cross_project_reference(@project, @merge_request)
+ %hr
+ .context
+ = render partial: 'projects/merge_requests/show/context', locals: { merge_request: @merge_request }
+ %hr
+ .votes-holder
+ %h6 Votes
+ #votes= render 'votes/votes_block', votable: @merge_request
+
+ - if @merge_request.labels.any?
+ %hr
+ %h6 Labels
+ .merge-request-show-labels
+ - @merge_request.labels.each do |label|
+ = link_to namespace_project_merge_requests_path(@project.namespace, @project, label_name: label.name) do
+ = render_colored_label(label)
+ = link_to '#aside', class: 'show-aside' do
+ %i.fa.fa-angle-left
diff --git a/app/views/projects/merge_requests/_form.html.haml b/app/views/projects/merge_requests/_form.html.haml
index d52e64666a0..1c7160bce5f 100644
--- a/app/views/projects/merge_requests/_form.html.haml
+++ b/app/views/projects/merge_requests/_form.html.haml
@@ -1,4 +1,4 @@
-= form_for [@project, @merge_request], html: { class: 'merge-request-form form-horizontal gfm-form' } do |f|
+= form_for [@project.namespace.becomes(Namespace), @project, @merge_request], html: { class: 'merge-request-form form-horizontal gfm-form' } do |f|
.merge-request-form-info
= render 'projects/issuable_form', f: f, issuable: @merge_request
@@ -9,4 +9,4 @@
e.preventDefault();
});
- window.project_image_path_upload = "#{upload_image_project_path @project}";
+ window.project_uploads_path = "#{namespace_project_uploads_path @project.namespace, @project}";
diff --git a/app/views/projects/merge_requests/_head.html.haml b/app/views/projects/merge_requests/_head.html.haml
index 35a86e6511c..19e4dab874b 100644
--- a/app/views/projects/merge_requests/_head.html.haml
+++ b/app/views/projects/merge_requests/_head.html.haml
@@ -1,5 +1,5 @@
.top-tabs
- = link_to project_merge_requests_path(@project), class: "tab #{'active' if current_page?(project_merge_requests_path(@project)) }" do
+ = link_to namespace_project_merge_requests_path(@project.namespace, @project), class: "tab #{'active' if current_page?(namespace_project_merge_requests_path(@project.namespace, @project)) }" do
%span
Merge Requests
diff --git a/app/views/projects/merge_requests/_merge_request.html.haml b/app/views/projects/merge_requests/_merge_request.html.haml
index 1ee2e1bdae8..1eba1a96b7b 100644
--- a/app/views/projects/merge_requests/_merge_request.html.haml
+++ b/app/views/projects/merge_requests/_merge_request.html.haml
@@ -1,28 +1,34 @@
%li{ class: mr_css_classes(merge_request) }
.merge-request-title
+ %span.str-truncated
+ = link_to_gfm merge_request.title, merge_request_path(merge_request), class: "row_title"
+ .pull-right.light
+ - if merge_request.merged?
+ %span
+ %i.fa.fa-check
+ MERGED
+ - elsif merge_request.closed?
+ %span
+ %i.fa.fa-close
+ CLOSED
+ - else
+ %span.hidden-xs.hidden-sm
+ %span.label-branch<
+ %i.fa.fa-code-fork
+ %span= merge_request.target_branch
+ - if merge_request.notes.any?
+ &nbsp;
+ %span
+ %i.fa.fa-comments
+ = merge_request.mr_and_commit_notes.count
+ .merge-request-info
%span.light= "##{merge_request.iid}"
- = link_to_gfm truncate(merge_request.title, length: 80), project_merge_request_path(merge_request.target_project, merge_request), class: "row_title"
- - if merge_request.merged?
- %small.pull-right
- %i.fa.fa-check
- MERGED
+ - if merge_request.assignee
+ assigned to #{link_to_member(merge_request.source_project, merge_request.assignee)}
- else
- %span.pull-right
- - if merge_request.for_fork?
- %span.light
- #{merge_request.source_project_namespace}:
- = truncate merge_request.source_branch, length: 25
- %i.fa.fa-angle-right.light
- = merge_request.target_branch
- .merge-request-info
- - if merge_request.author
- authored by #{link_to_member(merge_request.source_project, merge_request.author)}
+ Unassigned
- if merge_request.votes_count > 0
= render 'votes/votes_inline', votable: merge_request
- - if merge_request.notes.any?
- %span
- %i.fa.fa-comments
- = merge_request.mr_and_commit_notes.count
- if merge_request.milestone_id?
%span
%i.fa.fa-clock-o
@@ -31,10 +37,11 @@
%span.task-status
= merge_request.task_status
- .pull-right
+
+ .pull-right.hidden-xs
%small updated #{time_ago_with_tooltip(merge_request.updated_at, 'bottom', 'merge_request_updated_ago')}
.merge-request-labels
- merge_request.labels.each do |label|
- = link_to project_merge_requests_path(merge_request.project, label_name: label.name) do
+ = link_to namespace_project_merge_requests_path(merge_request.project.namespace, merge_request.project, label_name: label.name) do
= render_colored_label(label)
diff --git a/app/views/projects/merge_requests/_new_compare.html.haml b/app/views/projects/merge_requests/_new_compare.html.haml
index 99726172154..17e76059fdb 100644
--- a/app/views/projects/merge_requests/_new_compare.html.haml
+++ b/app/views/projects/merge_requests/_new_compare.html.haml
@@ -1,7 +1,7 @@
%h3.page-title Compare branches for new Merge Request
%hr
-= form_for [@project, @merge_request], url: new_project_merge_request_path(@project), method: :get, html: { class: "merge-request-form form-inline" } do |f|
+= form_for [@project.namespace.becomes(Namespace), @project, @merge_request], url: new_namespace_project_merge_request_path(@project.namespace, @project), method: :get, html: { class: "merge-request-form form-inline" } do |f|
.hide.alert.alert-danger.mr-compare-errors
.merge-request-branches.row
.col-md-6
@@ -60,19 +60,19 @@
, target_branch = $("#merge_request_target_branch")
, target_project = $("#merge_request_target_project_id");
- $.get("#{branch_from_project_merge_requests_path(@source_project)}", {ref: source_branch.val() });
- $.get("#{branch_to_project_merge_requests_path(@source_project)}", {target_project_id: target_project.val(),ref: target_branch.val() });
+ $.get("#{branch_from_namespace_project_merge_requests_path(@source_project.namespace, @source_project)}", {ref: source_branch.val() });
+ $.get("#{branch_to_namespace_project_merge_requests_path(@source_project.namespace, @source_project)}", {target_project_id: target_project.val(),ref: target_branch.val() });
target_project.on("change", function() {
- $.get("#{update_branches_project_merge_requests_path(@source_project)}", {target_project_id: $(this).val() });
+ $.get("#{update_branches_namespace_project_merge_requests_path(@source_project.namespace, @source_project)}", {target_project_id: $(this).val() });
});
source_branch.on("change", function() {
- $.get("#{branch_from_project_merge_requests_path(@source_project)}", {ref: $(this).val() });
+ $.get("#{branch_from_namespace_project_merge_requests_path(@source_project.namespace, @source_project)}", {ref: $(this).val() });
$(".mr-compare-errors").fadeOut();
$(".mr-compare-btn").enable();
});
target_branch.on("change", function() {
- $.get("#{branch_to_project_merge_requests_path(@source_project)}", {target_project_id: target_project.val(),ref: $(this).val() });
+ $.get("#{branch_to_namespace_project_merge_requests_path(@source_project.namespace, @source_project)}", {target_project_id: target_project.val(),ref: $(this).val() });
$(".mr-compare-errors").fadeOut();
$(".mr-compare-btn").enable();
});
diff --git a/app/views/projects/merge_requests/_new_submit.html.haml b/app/views/projects/merge_requests/_new_submit.html.haml
index d4666eacd7e..73eccfa556e 100644
--- a/app/views/projects/merge_requests/_new_submit.html.haml
+++ b/app/views/projects/merge_requests/_new_submit.html.haml
@@ -7,75 +7,105 @@
%strong.label-branch #{@merge_request.target_project_namespace}:#{@merge_request.target_branch}
%span.pull-right
- = link_to 'Change branches', new_project_merge_request_path(@project)
+ = link_to 'Change branches', new_namespace_project_merge_request_path(@project.namespace, @project)
-= form_for [@project, @merge_request], html: { class: "merge-request-form gfm-form" } do |f|
- .panel.panel-default
+= form_for [@project.namespace.becomes(Namespace), @project, @merge_request], html: { class: "merge-request-form form-horizontal gfm-form" } do |f|
+ .merge-request-form-info
+ .form-group
+ = f.label :title, class: 'control-label' do
+ %strong Title *
+ .col-sm-10
+ = f.text_field :title, maxlength: 255, autofocus: true, class: 'form-control pad js-gfm-input', required: true
+ .form-group.issuable-description
+ = f.label :description, 'Description', class: 'control-label'
+ .col-sm-10
+ = render layout: 'projects/md_preview', locals: { preview_class: "wiki" } do
+ = render 'projects/zen', f: f, attr: :description, classes: 'description form-control'
- .panel-body
- .form-group
- .light
- = f.label :title do
- Title *
- = f.text_field :title, class: "form-control input-lg js-gfm-input", maxlength: 255, rows: 5, required: true
- .form-group
- .light
- = f.label :description, "Description"
- = render 'projects/zen', f: f, attr: :description,
- classes: 'description form-control'
- .clearfix.hint
- .pull-left Description is parsed with #{link_to "GitLab Flavored Markdown", help_page_path("markdown", "markdown"), target: '_blank'}.
- .pull-right Attach images (JPG, PNG, GIF) by dragging & dropping or #{link_to "selecting them", '#', class: 'markdown-selector' }.
- .error-alert
- .form-group
- .issue-assignee
- = f.label :assignee_id do
- %i.fa.fa-user
- Assign to
- %div
- = project_users_select_tag('merge_request[assignee_id]', placeholder: 'Select a user', class: 'custom-form-control', selected: @merge_request.assignee_id, project_id: @merge_request.target_project_id)
- &nbsp;
- = link_to 'Assign to me', '#', class: 'btn assign-to-me-link'
- .form-group
- .issue-milestone
- = f.label :milestone_id do
- %i.fa.fa-clock-o
- Milestone
- %div= f.select(:milestone_id, milestone_options(@merge_request), { include_blank: "Select milestone" }, {class: 'select2'})
- .form-group
- = f.label :label_ids do
- %i.fa.fa-tag
- Labels
- %div
- = f.collection_select :label_ids, @merge_request.target_project.labels.all, :id, :name, { selected: @merge_request.label_ids }, multiple: true, class: 'select2'
+ .col-sm-12-hint
+ .pull-left
+ Parsed with
+ #{link_to 'Gitlab Flavored Markdown', help_page_path('markdown', 'markdown'), target: '_blank'}.
+ .pull-right
+ Attach files by dragging &amp; dropping
+ or #{link_to 'selecting them', '#', class: 'markdown-selector'}.
- .panel-footer
+ .clearfix
+ .error-alert
+ %hr
+ .form-group
+ .issue-assignee
+ = f.label :assignee_id, class: 'control-label' do
+ %i.fa.fa-user
+ Assign to
+ .col-sm-10
+ = project_users_select_tag('merge_request[assignee_id]', placeholder: 'Select a user', class: 'custom-form-control', selected: @merge_request.assignee_id, project_id: @merge_request.target_project_id)
+ &nbsp;
+ = link_to 'Assign to me', '#', class: 'btn assign-to-me-link'
+ .form-group
+ .issue-milestone
+ = f.label :milestone_id, class: 'control-label' do
+ %i.fa.fa-clock-o
+ Milestone
+ .col-sm-10
+ - if milestone_options(@merge_request).present?
+ = f.select(:milestone_id, milestone_options(@merge_request), {include_blank: 'Select milestone'}, {class: 'select2'})
+ - else
+ %span.light No open milestones available.
+ &nbsp;
+ - if can? current_user, :admin_milestone, @merge_request.target_project
+ = link_to 'Create new milestone', new_namespace_project_milestone_path(@merge_request.target_project.namespace, @merge_request.target_project), target: :blank
+ .form-group
+ = f.label :label_ids, class: 'control-label' do
+ %i.fa.fa-tag
+ Labels
+ .col-sm-10
+ - if @merge_request.target_project.labels.any?
+ = f.collection_select :label_ids, @merge_request.target_project.labels.all, :id, :name, {selected: @merge_request.label_ids}, multiple: true, class: 'select2'
+ - else
+ %span.light No labels yet.
+ &nbsp;
+ - if can? current_user, :admin_label, @merge_request.target_project
+ = link_to 'Create new label', new_namespace_project_label_path(@merge_request.target_project.namespace, @merge_request.target_project), target: :blank
+
+ .form-actions
- if contribution_guide_url(@target_project)
%p
Please review the
- %strong #{link_to "guidelines for contribution", contribution_guide_url(@target_project)}
+ %strong #{link_to 'guidelines for contribution', contribution_guide_url(@target_project)}
to this repository.
= f.hidden_field :source_project_id
+ = f.hidden_field :source_branch
= f.hidden_field :target_project_id
= f.hidden_field :target_branch
- = f.hidden_field :source_branch
- = f.submit 'Submit merge request', class: "btn btn-create"
-
-.mr-compare
- = render "projects/commits/commit_list"
+ = f.submit 'Submit merge request', class: 'btn btn-create'
- %h4 Changes
- - if @diffs.present?
- = render "projects/diffs/diffs", diffs: @diffs, project: @project
- - elsif @commits.size > MergeRequestDiff::COMMITS_SAFE_SIZE
- .bs-callout.bs-callout-danger
- %h4 This comparison includes more than #{MergeRequestDiff::COMMITS_SAFE_SIZE} commits.
- %p To preserve performance the line changes are not shown.
- - else
- .bs-callout.bs-callout-danger
- %h4 This comparison includes huge diff.
- %p To preserve performance the line changes are not shown.
+.mr-compare.merge-request
+ %ul.nav.nav-tabs.merge-request-tabs
+ %li.commits-tab{data: {action: 'commits'}}
+ = link_to url_for(params) do
+ %i.fa.fa-history
+ Commits
+ %span.badge= @commits.size
+ %li.diffs-tab{data: {action: 'diffs'}}
+ = link_to url_for(params) do
+ %i.fa.fa-list-alt
+ Changes
+ %span.badge= @diffs.size
+ .commits.tab-content
+ = render "projects/commits/commits", project: @project
+ .diffs.tab-content
+ - if @diffs.present?
+ = render "projects/diffs/diffs", diffs: @diffs, project: @project
+ - elsif @commits.size > MergeRequestDiff::COMMITS_SAFE_SIZE
+ .bs-callout.bs-callout-danger
+ %h4 This comparison includes more than #{MergeRequestDiff::COMMITS_SAFE_SIZE} commits.
+ %p To preserve performance the line changes are not shown.
+ - else
+ .bs-callout.bs-callout-danger
+ %h4 This comparison includes a huge diff.
+ %p To preserve performance the line changes are not shown.
:javascript
$('.assign-to-me-link').on('click', function(e){
@@ -83,4 +113,11 @@
e.preventDefault();
});
- window.project_image_path_upload = "#{upload_image_project_path @project}";
+ window.project_uploads_path = "#{namespace_project_uploads_path @project.namespace, @project}";
+
+:javascript
+ var merge_request
+ merge_request = new MergeRequest({
+ action: 'commits'
+ });
+
diff --git a/app/views/projects/merge_requests/_show.html.haml b/app/views/projects/merge_requests/_show.html.haml
index 7b28dd5e7da..ca4ceecb225 100644
--- a/app/views/projects/merge_requests/_show.html.haml
+++ b/app/views/projects/merge_requests/_show.html.haml
@@ -1,46 +1,76 @@
-.merge-request
- = 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/state_widget"
- = render "projects/merge_requests/show/commits"
- = render "projects/merge_requests/show/participants"
+.merge-request{'data-url' => merge_request_path(@merge_request)}
+ .merge-request-details
+ = render "projects/merge_requests/show/mr_title"
+ %hr
+ = render "projects/merge_requests/show/mr_box"
+ %hr
+ .append-bottom-20
+ .slead
+ %span From
+ - if @merge_request.for_fork?
+ %strong.label-branch<
+ - if @merge_request.source_project
+ = link_to @merge_request.source_project_namespace, namespace_project_path(@merge_request.source_project.namespace, @merge_request.source_project)
+ - else
+ \ #{@merge_request.source_project_namespace}
+ \:#{@merge_request.source_branch}
+ %span into
+ %strong.label-branch #{@merge_request.target_project_namespace}:#{@merge_request.target_branch}
+ - else
+ %strong.label-branch #{@merge_request.source_branch}
+ %span into
+ %strong.label-branch #{@merge_request.target_branch}
+ - if @merge_request.open?
+ %span.pull-right
+ .btn-group
+ %a.btn.dropdown-toggle{ data: {toggle: :dropdown} }
+ %i.fa.fa-download
+ Download as
+ %span.caret
+ %ul.dropdown-menu
+ %li= link_to "Email Patches", merge_request_path(@merge_request, format: :patch)
+ %li= link_to "Plain Diff", merge_request_path(@merge_request, format: :diff)
+
+ = render "projects/merge_requests/show/how_to_merge"
+ = render "projects/merge_requests/show/state_widget"
- if @commits.present?
- %ul.nav.nav-pills.merge-request-tabs
+ %ul.nav.nav-tabs.merge-request-tabs
%li.notes-tab{data: {action: 'notes'}}
- = link_to project_merge_request_path(@project, @merge_request) do
- %i.fa.fa-comment
+ = link_to merge_request_path(@merge_request) do
+ %i.fa.fa-comments
Discussion
%span.badge= @merge_request.mr_and_commit_notes.count
+ %li.commits-tab{data: {action: 'commits'}}
+ = link_to merge_request_path(@merge_request), title: 'Commits' do
+ %i.fa.fa-history
+ Commits
+ %span.badge= @commits.size
%li.diffs-tab{data: {action: 'diffs'}}
- = link_to diffs_project_merge_request_path(@project, @merge_request) do
+ = link_to diffs_namespace_project_merge_request_path(@project.namespace, @project, @merge_request) do
%i.fa.fa-list-alt
Changes
%span.badge= @merge_request.diffs.size
- - content_for :note_actions do
- - if can?(current_user, :modify_merge_request, @merge_request)
- - if @merge_request.open?
- = link_to 'Close', project_merge_request_path(@project, @merge_request, merge_request: {state_event: :close }), method: :put, class: "btn btn-grouped btn-close close-mr-link js-note-target-close", title: "Close merge request"
- - if @merge_request.closed?
- = link_to 'Reopen', project_merge_request_path(@project, @merge_request, merge_request: {state_event: :reopen }), method: :put, class: "btn btn-grouped btn-reopen reopen-mr-link js-note-target-reopen", title: "Reopen merge request"
-
+ .notes.tab-content.voting_notes#notes{ class: (controller.action_name == 'show') ? "" : "hide" }
+ = render "projects/merge_requests/discussion"
+ .commits.tab-content
+ = render "projects/merge_requests/show/commits"
.diffs.tab-content
- if current_page?(action: 'diffs')
= render "projects/merge_requests/show/diffs"
- .notes.tab-content.voting_notes#notes{ class: (controller.action_name == 'show') ? "" : "hide" }
- = render "projects/notes/notes_with_form"
+
.mr-loading-status
= spinner
+
:javascript
var merge_request;
merge_request = new MergeRequest({
- url_to_automerge_check: "#{automerge_check_project_merge_request_path(@project, @merge_request)}",
+ url_to_automerge_check: "#{automerge_check_namespace_project_merge_request_path(@project.namespace, @project, @merge_request)}",
check_enable: #{@merge_request.unchecked? ? "true" : "false"},
- url_to_ci_check: "#{ci_status_project_merge_request_path(@project, @merge_request)}",
+ url_to_ci_check: "#{ci_status_namespace_project_merge_request_path(@project.namespace, @project, @merge_request)}",
ci_enable: #{@project.ci_service ? "true" : "false"},
current_status: "#{@merge_request.merge_status_name}",
action: "#{controller.action_name}"
diff --git a/app/views/projects/merge_requests/automerge.js.haml b/app/views/projects/merge_requests/automerge.js.haml
index e01ff662e7d..a53cbb150a4 100644
--- a/app/views/projects/merge_requests/automerge.js.haml
+++ b/app/views/projects/merge_requests/automerge.js.haml
@@ -1,7 +1,6 @@
-if @status
:plain
- location.reload();
+ merge_request.mergeInProgress();
-else
:plain
merge_request.alreadyOrCannotBeMerged()
-
diff --git a/app/views/projects/merge_requests/index.html.haml b/app/views/projects/merge_requests/index.html.haml
index be638d7cac1..e3b9a28033b 100644
--- a/app/views/projects/merge_requests/index.html.haml
+++ b/app/views/projects/merge_requests/index.html.haml
@@ -1,78 +1,22 @@
-- if can? current_user, :write_merge_request, @project
- = link_to new_project_merge_request_path(@project), class: "pull-right btn btn-new", title: "New Merge Request" do
- %i.fa.fa-plus
- New Merge Request
-%h3.page-title
- Merge Requests
-%hr
-.row
- .fixed.sidebar-expand-button.hidden-lg.hidden-md
- %i.fa.fa-list.fa-2x
- .col-md-3.responsive-side
- = render 'shared/project_filter', project_entities_path: project_merge_requests_path(@project),
- labels: true, redirect: 'merge_requests', entity: 'merge_request'
- .col-md-9
- .mr-filters.append-bottom-10
- .dropdown.inline
- %a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"}
- %i.fa.fa-user
- %span.light assignee:
- - if @assignee.present?
- %strong= @assignee.name
- - elsif params[:assignee_id] == "0"
- Unassigned
- - else
- Any
- %b.caret
- %ul.dropdown-menu
- %li
- = link_to project_filter_path(assignee_id: nil) do
- Any
- = link_to project_filter_path(assignee_id: 0) do
- Unassigned
- - @assignees.sort_by(&:name).each do |user|
- %li
- = link_to project_filter_path(assignee_id: user.id) do
- = image_tag avatar_icon(user.email), class: "avatar s16", alt: ''
- = user.name
+.merge-requests-holder
+ .append-bottom-10
+ .pull-right
+ - if can? current_user, :write_merge_request, @project
+ = link_to new_namespace_project_merge_request_path(@project.namespace, @project), class: "btn btn-new pull-left", title: "New Merge Request" do
+ %i.fa.fa-plus
+ New Merge Request
+ = render 'shared/issuable_filter'
+ .panel.panel-default
+ %ul.well-list.mr-list
+ = render @merge_requests
+ - if @merge_requests.blank?
+ %li
+ .nothing-here-block No merge requests to show
+ - if @merge_requests.present?
+ .pull-right
+ %span.cgray.pull-right #{@merge_requests.total_count} merge requests for this filter
- .dropdown.inline.prepend-left-10
- %a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"}
- %i.fa.fa-clock-o
- %span.light milestone:
- - if @milestone.present?
- %strong= @milestone.title
- - elsif params[:milestone_id] == "0"
- None (backlog)
- - else
- Any
- %b.caret
- %ul.dropdown-menu
- %li
- = link_to project_filter_path(milestone_id: nil) do
- Any
- = link_to project_filter_path(milestone_id: 0) do
- None (backlog)
- - project_active_milestones.each do |milestone|
- %li
- = link_to project_filter_path(milestone_id: milestone.id) do
- %strong= milestone.title
- %small.light= milestone.expires_at
-
- .pull-right
- = render 'shared/sort_dropdown'
-
- .panel.panel-default
- %ul.well-list.mr-list
- = render @merge_requests
- - if @merge_requests.blank?
- %li
- .nothing-here-block No merge requests to show
- - if @merge_requests.present?
- .pull-right
- %span.cgray.pull-right #{@merge_requests.total_count} merge requests for this filter
-
- = paginate @merge_requests, theme: "gitlab"
+ = paginate @merge_requests, theme: "gitlab"
:javascript
$(merge_requestsPage);
diff --git a/app/views/projects/merge_requests/show/_commits.html.haml b/app/views/projects/merge_requests/show/_commits.html.haml
index a6587403871..3b7f283daf0 100644
--- a/app/views/projects/merge_requests/show/_commits.html.haml
+++ b/app/views/projects/merge_requests/show/_commits.html.haml
@@ -1,30 +1 @@
-- if @commits.present?
- .panel.panel-default
- .panel-heading
- %i.fa.fa-list
- Commits (#{@commits.count})
- .commits.mr-commits
- - if @commits.count > 8
- %ul.first-commits.well-list
- - @commits.first(8).each do |commit|
- = render "projects/commits/commit", commit: commit, project: @merge_request.source_project
- %li.bottom
- 8 of #{@commits.count} commits displayed.
- %strong
- %a.show-all-commits Click here to show all
- - if @commits.size > MergeRequestDiff::COMMITS_SAFE_SIZE
- %ul.all-commits.hide.well-list
- - @commits.first(MergeRequestDiff::COMMITS_SAFE_SIZE).each do |commit|
- = render "projects/commits/inline_commit", commit: commit, project: @merge_request.source_project
- %li
- other #{@commits.size - MergeRequestDiff::COMMITS_SAFE_SIZE} commits hidden to prevent performance issues.
- - else
- %ul.all-commits.hide.well-list
- - @commits.each do |commit|
- = render "projects/commits/inline_commit", commit: commit, project: @merge_request.source_project
-
- - else
- %ul.well-list
- - @commits.each do |commit|
- = render "projects/commits/commit", commit: commit, project: @merge_request.source_project
-
+= render "projects/commits/commits", project: @merge_request.source_project
diff --git a/app/views/projects/merge_requests/show/_context.html.haml b/app/views/projects/merge_requests/show/_context.html.haml
index 089302e3588..a74f3fb24e7 100644
--- a/app/views/projects/merge_requests/show/_context.html.haml
+++ b/app/views/projects/merge_requests/show/_context.html.haml
@@ -1,24 +1,30 @@
-= form_for [@project, @merge_request], remote: true, html: {class: 'edit-merge_request inline-update'} do |f|
- .row
- .col-sm-6
- %strong.append-right-10
+= form_for [@project.namespace.becomes(Namespace), @project, @merge_request], remote: true, html: {class: 'edit-merge_request inline-update'} do |f|
+ %div.prepend-top-20
+ .issuable-context-title
+ %label
Assignee:
-
+ - if @merge_request.assignee
+ %strong= link_to_member(@project, @merge_request.assignee, size: 24)
+ - else
+ none
+ .issuable-context-selectbox
- if can?(current_user, :modify_merge_request, @merge_request)
= project_users_select_tag('merge_request[assignee_id]', placeholder: 'Select assignee', class: 'custom-form-control js-select2 js-assignee', selected: @merge_request.assignee_id)
- - elsif merge_request.assignee
- = link_to_member(@project, @merge_request.assignee)
- - else
- None
- .col-sm-6.text-right
- %strong.append-right-10
+ %div.prepend-top-20.clearfix
+ .issuable-context-title
+ %label
Milestone:
+ - if @merge_request.milestone
+ %span.back-to-milestone
+ = link_to namespace_project_milestone_path(@project.namespace, @project, @merge_request.milestone) do
+ %strong
+ %i.fa.fa-clock-o
+ = @merge_request.milestone.title
+ - else
+ none
+ .issuable-context-selectbox
- if can?(current_user, :modify_merge_request, @merge_request)
= f.select(:milestone_id, milestone_options(@merge_request), { include_blank: "Select milestone" }, {class: 'select2 select2-compact js-select2 js-milestone'})
= hidden_field_tag :merge_request_context
= f.submit class: 'btn'
- - elsif merge_request.milestone
- = link_to merge_request.milestone.title, project_milestone_path
- - else
- None
diff --git a/app/views/projects/merge_requests/show/_diffs.html.haml b/app/views/projects/merge_requests/show/_diffs.html.haml
index d361c5f579a..cfef1d5e4cc 100644
--- a/app/views/projects/merge_requests/show/_diffs.html.haml
+++ b/app/views/projects/merge_requests/show/_diffs.html.haml
@@ -8,5 +8,5 @@
Changes view for this comparison is extremely large.
%p
You can
- = link_to "download it", project_merge_request_path(@merge_request.target_project, @merge_request, format: :diff), class: "vlink"
+ = link_to "download it", merge_request_path(@merge_request, format: :diff), class: "vlink"
instead.
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 9540453ce3e..63db4b30968 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
@@ -10,11 +10,11 @@
- target_remote = @merge_request.target_project.namespace.nil? ? "target" :@merge_request.target_project.namespace.path
%p
%strong Step 1.
- Checkout the branch we are going to merge and pull in the code
+ Fetch the code and create a new branch pointing to it
%pre.dark
:preserve
- git checkout -b #{@merge_request.source_project_path}-#{@merge_request.source_branch} #{@merge_request.target_branch}
- git pull #{@merge_request.source_project.http_url_to_repo} #{@merge_request.source_branch}
+ git fetch #{@merge_request.source_project.http_url_to_repo} #{@merge_request.source_branch}
+ git checkout -b #{@merge_request.source_project_path}-#{@merge_request.source_branch} FETCH_HEAD
%p
%strong Step 2.
Merge the branch and push the changes to GitLab
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 4939ae03994..fb2c3220b8a 100644
--- a/app/views/projects/merge_requests/show/_mr_accept.html.haml
+++ b/app/views/projects/merge_requests/show/_mr_accept.html.haml
@@ -12,26 +12,23 @@
- if @show_merge_controls
.automerge_widget.can_be_merged.hide
.clearfix
- = form_for [:automerge, @project, @merge_request], remote: true, method: :post do |f|
- %h4
- You can accept this request automatically.
- .accept-merge-holder.clearfix
- .accept-group
- .pull-left
- = f.submit "Accept Merge Request", class: "btn btn-create accept_merge_request"
- - if can_remove_branch?(@merge_request.source_project, @merge_request.source_branch) && !@merge_request.for_fork?
- .remove_branch_holder.pull-left
- = label_tag :should_remove_source_branch, class: "checkbox" do
- = check_box_tag :should_remove_source_branch
- Remove source-branch
- .js-toggle-container
- %label
- %i.fa.fa-edit
- = link_to "modify merge commit message", "#", class: "modify-merge-commit-link js-toggle-button", title: "Modify merge commit message"
- .js-toggle-content.hide
- = render 'shared/commit_message_container', params: params,
- text: @merge_request.merge_commit_message,
- rows: 14, hint: true
+ = form_for [:automerge, @project.namespace.becomes(Namespace), @project, @merge_request], remote: true, method: :post do |f|
+ .accept-merge-holder.clearfix.js-toggle-container
+ .accept-action
+ = f.submit "Accept Merge Request", class: "btn btn-create accept_merge_request"
+ - if can_remove_branch?(@merge_request.source_project, @merge_request.source_branch) && !@merge_request.for_fork?
+ .accept-control.checkbox
+ = label_tag :should_remove_source_branch, class: "remove_source_checkbox" do
+ = check_box_tag :should_remove_source_branch
+ Remove source-branch
+ .accept-control
+ = link_to "#", class: "modify-merge-commit-link js-toggle-button", title: "Modify merge commit message" do
+ %i.fa.fa-edit
+ Modify commit message
+ .js-toggle-content.hide.prepend-top-20
+ = render 'shared/commit_message_container', params: params,
+ text: @merge_request.merge_commit_message,
+ rows: 14, hint: true
%hr
.light
@@ -48,10 +45,17 @@
.automerge_widget.cannot_be_merged.hide
%h4
This request can't be merged with GitLab.
- %p
You should do it manually with
%strong
- = link_to "command line", "#modal_merge_info", class: "how_to_merge_link", title: "How To Merge", "data-toggle" => "modal"
+ = link_to "#modal_merge_info", class: "underlined-link how_to_merge_link", title: "How To Merge", "data-toggle" => "modal" do
+ command line
+
+ %p
+ %button.btn.disabled
+ %i.fa.fa-warning
+ Accept Merge Request
+ &nbsp;
+ This usually happens when git can not resolve conflicts between branches automatically.
.automerge_widget.unchecked
%p
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 7e5a4eda508..ada9ae58b8f 100644
--- a/app/views/projects/merge_requests/show/_mr_box.html.haml
+++ b/app/views/projects/merge_requests/show/_mr_box.html.haml
@@ -1,25 +1,9 @@
-.issue-box{ class: issue_box_class(@merge_request) }
- .state.clearfix
- .state-label
- - if @merge_request.merged?
- Merged
- - elsif @merge_request.closed?
- Closed
- - else
- Open
-
- .creator
- Created by #{link_to_member(@project, @merge_request.author)} #{time_ago_with_tooltip(@merge_request.created_at)}
-
- %h4.title
- = gfm escape_once(@merge_request.title)
+%h2.issue-title
+ = gfm escape_once(@merge_request.title)
+%div
- if @merge_request.description.present?
.description
.wiki
= preserve do
= markdown(@merge_request.description, parse_tasks: true)
-
- .context
- %cite.cgray
- = render partial: 'projects/merge_requests/show/context', locals: { merge_request: @merge_request }
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 941b15d3b32..ee7fd0ef157 100644
--- a/app/views/projects/merge_requests/show/_mr_ci.html.haml
+++ b/app/views/projects/merge_requests/show/_mr_ci.html.haml
@@ -3,21 +3,21 @@
%i.fa.fa-check
%span CI build passed
for #{@merge_request.last_commit_short_sha}.
- = link_to "Build page", ci_build_details_path(@merge_request)
+ = link_to "Build page", ci_build_details_path(@merge_request), :"data-no-turbolink" => "data-no-turbolink"
.ci_widget.ci-failed{style: "display:none"}
%i.fa.fa-times
%span CI build failed
for #{@merge_request.last_commit_short_sha}.
- = link_to "Build page", ci_build_details_path(@merge_request)
+ = link_to "Build page", ci_build_details_path(@merge_request), :"data-no-turbolink" => "data-no-turbolink"
- [:running, :pending].each do |status|
.ci_widget{class: "ci-#{status}", style: "display:none"}
%i.fa.fa-clock-o
%span CI build #{status}
for #{@merge_request.last_commit_short_sha}.
- = link_to "Build page", ci_build_details_path(@merge_request)
+ = link_to "Build page", ci_build_details_path(@merge_request), :"data-no-turbolink" => "data-no-turbolink"
.ci_widget
%i.fa.fa-spinner
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 6fe765248e4..46e92a9c558 100644
--- a/app/views/projects/merge_requests/show/_mr_title.html.haml
+++ b/app/views/projects/merge_requests/show/_mr_title.html.haml
@@ -1,45 +1,22 @@
-%h3.page-title
+%h4.page-title
+ .issue-box{ class: issue_box_class(@merge_request) }
+ - if @merge_request.merged?
+ Merged
+ - elsif @merge_request.closed?
+ Closed
+ - else
+ Open
= "Merge Request ##{@merge_request.iid}"
+ %small.creator
+ &middot;
+ created by #{link_to_member(@project, @merge_request.author)} #{time_ago_with_tooltip(@merge_request.created_at)}
- %span.pull-right.issue-btn-group
+ .issue-btn-group.pull-right
- if can?(current_user, :modify_merge_request, @merge_request)
- if @merge_request.open?
- .btn-group.pull-left
- %a.btn.btn-grouped.dropdown-toggle{ data: {toggle: :dropdown} }
- %i.fa.fa-download
- Download as
- %span.caret
- %ul.dropdown-menu
- %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 btn-grouped btn-close", title: "Close merge request"
-
- = link_to edit_project_merge_request_path(@project, @merge_request), class: "btn btn-grouped", id:"edit_merge_request" do
+ = link_to 'Close', merge_request_path(@merge_request, merge_request: { state_event: :close }), method: :put, class: "btn btn-grouped btn-close", title: "Close merge request"
+ = link_to edit_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), class: "btn btn-grouped issuable-edit", id: "edit_merge_request" do
%i.fa.fa-pencil-square-o
Edit
- if @merge_request.closed?
- = link_to 'Reopen', project_merge_request_path(@project, @merge_request, merge_request: {state_event: :reopen }), method: :put, class: "btn btn-grouped btn-reopen reopen-mr-link", title: "Close merge request"
-
-.votes-holder.hidden-sm.hidden-xs
- #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
- %span From
- - if @merge_request.for_fork?
- %strong.label-branch<
- - if @merge_request.source_project
- = link_to @merge_request.source_project_namespace, project_path(@merge_request.source_project)
- - else
- \ #{@merge_request.source_project_namespace}
- \:#{@merge_request.source_branch}
- %span into
- %strong.label-branch #{@merge_request.target_project_namespace}:#{@merge_request.target_branch}
- - else
- %strong.label-branch #{@merge_request.source_branch}
- %span into
- %strong.label-branch #{@merge_request.target_branch}
+ = link_to 'Reopen', merge_request_path(@merge_request, merge_request: {state_event: :reopen }), method: :put, class: "btn btn-grouped btn-reopen reopen-mr-link", title: "Close merge request"
diff --git a/app/views/projects/merge_requests/show/_participants.html.haml b/app/views/projects/merge_requests/show/_participants.html.haml
index b709c89cec2..4f34af1737d 100644
--- a/app/views/projects/merge_requests/show/_participants.html.haml
+++ b/app/views/projects/merge_requests/show/_participants.html.haml
@@ -1,9 +1,4 @@
.participants
- %cite.cgray #{@merge_request.participants.count} participants
+ %span #{@merge_request.participants.count} participants
- @merge_request.participants.each do |participant|
= link_to_member(@project, participant, name: false, size: 24)
-
- .merge-request-show-labels.pull-right
- - @merge_request.labels.each do |label|
- = link_to project_merge_requests_path(@project, label_name: label.name) do
- = render_colored_label(label)
diff --git a/app/views/projects/merge_requests/show/_remove_source_branch.html.haml b/app/views/projects/merge_requests/show/_remove_source_branch.html.haml
index 4fe5935bcf3..0a642b7e6d0 100644
--- a/app/views/projects/merge_requests/show/_remove_source_branch.html.haml
+++ b/app/views/projects/merge_requests/show/_remove_source_branch.html.haml
@@ -4,7 +4,7 @@
- elsif can_remove_branch?(@merge_request.source_project, @merge_request.source_branch) && @merge_request.merged?
.remove_source_branch_widget
%p Changes merged into #{@merge_request.target_branch}. You can remove source branch now
- = link_to project_branch_path(@merge_request.source_project, @source_branch), remote: true, method: :delete, class: "btn btn-primary btn-small remove_source_branch" do
+ = link_to namespace_project_branch_path(@merge_request.source_project.namespace, @merge_request.source_project, @source_branch), remote: true, method: :delete, class: "btn btn-primary btn-small remove_source_branch" do
%i.fa.fa-times
Remove Source Branch
@@ -12,6 +12,6 @@
Failed to remove source branch '#{@merge_request.source_branch}'
.remove_source_branch_in_progress.hide
- %i.fa.fa-refresh.fa-spin
+ %i.fa.fa-spinner.fa-spin
&nbsp;
Removing source branch '#{@merge_request.source_branch}'. Please wait. Page will be automatically reloaded. &nbsp;
diff --git a/app/views/projects/merge_requests/show/_state_widget.html.haml b/app/views/projects/merge_requests/show/_state_widget.html.haml
index 87dad6140be..a4f2a890969 100644
--- a/app/views/projects/merge_requests/show/_state_widget.html.haml
+++ b/app/views/projects/merge_requests/show/_state_widget.html.haml
@@ -11,14 +11,18 @@
- if @merge_request.closed?
%h4
- Closed by #{link_to_member(@project, @merge_request.closed_event.author, avatar: false)}
- #{time_ago_with_tooltip(@merge_request.closed_event.created_at)}
+ Closed
+ - if @merge_request.closed_event
+ by #{link_to_member(@project, @merge_request.closed_event.author, avatar: false)}
+ #{time_ago_with_tooltip(@merge_request.closed_event.created_at)}
%p Changes were not merged into target branch
- if @merge_request.merged?
%h4
- Merged by #{link_to_member(@project, @merge_request.merge_event.author, avatar: false)}
- #{time_ago_with_tooltip(@merge_request.merge_event.created_at)}
+ Merged
+ - if @merge_request.merge_event
+ by #{link_to_member(@project, @merge_request.merge_event.author, avatar: false)}
+ #{time_ago_with_tooltip(@merge_request.merge_event.created_at)}
= render "projects/merge_requests/show/remove_source_branch"
- if @merge_request.locked?
@@ -44,4 +48,3 @@
Accepting this merge request will close #{@closes_issues.size == 1 ? 'issue' : 'issues'}
= succeed '.' do
!= gfm(issues_sentence(@closes_issues))
-
diff --git a/app/views/projects/merge_requests/update.js.haml b/app/views/projects/merge_requests/update.js.haml
index 6452cc6382d..f5cc98c7fa4 100644
--- a/app/views/projects/merge_requests/update.js.haml
+++ b/app/views/projects/merge_requests/update.js.haml
@@ -1,2 +1,8 @@
- if params[:merge_request_context]
- $('.issue-box .context').effect('highlight');
+ $('.context').html("#{escape_javascript(render partial: 'projects/merge_requests/show/context', locals: { issue: @issue })}");
+ $('.context').effect('highlight');
+
+ new ProjectUsersSelect();
+
+ $('select.select2').select2({width: 'resolve', dropdownAutoWidth: true});
+ merge_request = new MergeRequest();
diff --git a/app/views/projects/milestones/_form.html.haml b/app/views/projects/milestones/_form.html.haml
index 5fb01a11cc5..95b7070ce5c 100644
--- a/app/views/projects/milestones/_form.html.haml
+++ b/app/views/projects/milestones/_form.html.haml
@@ -1,11 +1,11 @@
%h3.page-title= @milestone.new_record? ? "New Milestone" : "Edit Milestone ##{@milestone.iid}"
.back-link
- = link_to project_milestones_path(@project) do
+ = link_to namespace_project_milestones_path(@project.namespace, @project) do
&larr; To milestones
%hr
-= form_for [@project, @milestone], html: {class: 'form-horizontal milestone-form gfm-form'} do |f|
+= form_for [@project.namespace.becomes(Namespace), @project, @milestone], html: {class: 'form-horizontal milestone-form gfm-form'} do |f|
-if @milestone.errors.any?
.alert.alert-danger
%ul
@@ -18,13 +18,14 @@
.col-sm-10
= f.text_field :title, maxlength: 255, class: "form-control"
%p.hint Required
- .form-group
+ .form-group.milestone-description
= f.label :description, "Description", class: "control-label"
.col-sm-10
- = render 'projects/zen', f: f, attr: :description, classes: 'description form-control'
- .hint
- .pull-left Milestones are parsed with #{link_to "GitLab Flavored Markdown", help_page_path("markdown", "markdown"), target: '_blank'}.
- .pull-left Attach images (JPG, PNG, GIF) by dragging & dropping or #{link_to "selecting them", '#', class: 'markdown-selector' }.
+ = render layout: 'projects/md_preview', locals: { preview_class: "wiki" } do
+ = render 'projects/zen', f: f, attr: :description, classes: 'description form-control'
+ .hint
+ .pull-left Milestones are parsed with #{link_to "GitLab Flavored Markdown", help_page_path("markdown", "markdown"), target: '_blank'}.
+ .pull-left Attach files by dragging & dropping or #{link_to "selecting them", '#', class: 'markdown-selector' }.
.clearfix
.error-alert
.col-md-6
@@ -37,10 +38,10 @@
.form-actions
- if @milestone.new_record?
= f.submit 'Create milestone', class: "btn-create btn"
- = link_to "Cancel", project_milestones_path(@project), class: "btn btn-cancel"
+ = link_to "Cancel", namespace_project_milestones_path(@project.namespace, @project), class: "btn btn-cancel"
-else
= f.submit 'Save changes', class: "btn-save btn"
- = link_to "Cancel", project_milestone_path(@project, @milestone), class: "btn btn-cancel"
+ = link_to "Cancel", namespace_project_milestone_path(@project.namespace, @project, @milestone), class: "btn btn-cancel"
:javascript
@@ -50,4 +51,4 @@
onSelect: function(dateText, inst) { $("#milestone_due_date").val(dateText) }
}).datepicker("setDate", $.datepicker.parseDate('yy-mm-dd', $('#milestone_due_date').val()));
- window.project_image_path_upload = "#{upload_image_project_path @project}";
+ window.project_uploads_path = "#{namespace_project_uploads_path @project.namespace, @project}";
diff --git a/app/views/projects/milestones/_issue.html.haml b/app/views/projects/milestones/_issue.html.haml
index b5ec0fc9882..26c83841a22 100644
--- a/app/views/projects/milestones/_issue.html.haml
+++ b/app/views/projects/milestones/_issue.html.haml
@@ -1,8 +1,8 @@
-%li{ id: dom_id(issue, 'sortable'), class: 'issue-row', 'data-iid' => issue.iid, 'data-url' => project_issue_path(@project, issue) }
+%li{ id: dom_id(issue, 'sortable'), class: 'issue-row', 'data-iid' => issue.iid, 'data-url' => issue_path(issue) }
%span.str-truncated
- = link_to [@project, issue] do
+ = link_to [@project.namespace.becomes(Namespace), @project, issue] do
%span.cgray ##{issue.iid}
- = link_to_gfm issue.title, [@project, issue], title: issue.title
+ = link_to_gfm issue.title, [@project.namespace.becomes(Namespace), @project, issue], title: issue.title
.pull-right.assignee-icon
- if issue.assignee
= image_tag avatar_icon(issue.assignee.email, 16), class: "avatar s16"
diff --git a/app/views/projects/milestones/_merge_request.html.haml b/app/views/projects/milestones/_merge_request.html.haml
index d54cb3f8e74..46f2df1b183 100644
--- a/app/views/projects/milestones/_merge_request.html.haml
+++ b/app/views/projects/milestones/_merge_request.html.haml
@@ -1,5 +1,5 @@
-%li{ id: dom_id(merge_request, 'sortable'), class: 'mr-row', 'data-iid' => merge_request.iid, 'data-url' => project_merge_request_path(@project, merge_request) }
+%li{ id: dom_id(merge_request, 'sortable'), class: 'mr-row', 'data-iid' => merge_request.iid, 'data-url' => merge_request_path(merge_request) }
%span.str-truncated
- = link_to [@project, merge_request] do
+ = link_to [@project.namespace.becomes(Namespace), @project, merge_request] do
%span.cgray ##{merge_request.iid}
- = link_to_gfm merge_request.title, [@project, merge_request], title: merge_request.title
+ = link_to_gfm merge_request.title, [@project.namespace.becomes(Namespace), @project, merge_request], title: merge_request.title
diff --git a/app/views/projects/milestones/_milestone.html.haml b/app/views/projects/milestones/_milestone.html.haml
index 1002b9513ff..d32b2ba271f 100644
--- a/app/views/projects/milestones/_milestone.html.haml
+++ b/app/views/projects/milestones/_milestone.html.haml
@@ -1,12 +1,12 @@
%li{class: "milestone milestone-#{milestone.closed? ? 'closed' : 'open'}", id: dom_id(milestone) }
.pull-right
- if can?(current_user, :admin_milestone, milestone.project) and milestone.active?
- = link_to edit_project_milestone_path(milestone.project, milestone), class: "btn btn-small edit-milestone-link btn-grouped" do
+ = link_to edit_namespace_project_milestone_path(milestone.project.namespace, milestone.project, milestone), class: "btn btn-small edit-milestone-link btn-grouped" do
%i.fa.fa-pencil-square-o
Edit
- = link_to 'Close Milestone', project_milestone_path(@project, milestone, milestone: {state_event: :close }), method: :put, remote: true, class: "btn btn-small btn-close"
+ = link_to 'Close Milestone', namespace_project_milestone_path(@project.namespace, @project, milestone, milestone: {state_event: :close }), method: :put, remote: true, class: "btn btn-small btn-close"
%h4
- = link_to_gfm truncate(milestone.title, length: 100), project_milestone_path(milestone.project, milestone)
+ = link_to_gfm truncate(milestone.title, length: 100), namespace_project_milestone_path(milestone.project.namespace, milestone.project, milestone)
- if milestone.expired? and not milestone.closed?
%span.cred (Expired)
%small
@@ -16,10 +16,10 @@
- else
%div
%div
- = link_to project_issues_path(milestone.project, milestone_id: milestone.id) do
+ = link_to namespace_project_issues_path(milestone.project.namespace, milestone.project, milestone_id: milestone.id) do
= pluralize milestone.issues.count, 'Issue'
&nbsp;
- = link_to project_merge_requests_path(milestone.project, milestone_id: milestone.id) do
+ = link_to namespace_project_merge_requests_path(milestone.project.namespace, milestone.project, milestone_id: milestone.id) do
= pluralize milestone.merge_requests.count, 'Merge Request'
&nbsp;
%span.light #{milestone.percent_complete}% complete
diff --git a/app/views/projects/milestones/index.html.haml b/app/views/projects/milestones/index.html.haml
index 03367b7cdbf..d3eab8d6d75 100644
--- a/app/views/projects/milestones/index.html.haml
+++ b/app/views/projects/milestones/index.html.haml
@@ -1,33 +1,17 @@
-= render "projects/issues/head"
-.milestones_content
- %h3.page-title
- Milestones
- - if can? current_user, :admin_milestone, @project
- = link_to new_project_milestone_path(@project), class: "pull-right btn btn-new", title: "New Milestone" do
- %i.fa.fa-plus
- New Milestone
+.pull-right
+ - if can? current_user, :admin_milestone, @project
+ = link_to new_namespace_project_milestone_path(@project.namespace, @project), class: "pull-right btn btn-new", title: "New Milestone" do
+ %i.fa.fa-plus
+ New Milestone
+= render 'shared/milestones_filter'
- .row
- .fixed.sidebar-expand-button.hidden-lg.hidden-md.hidden-xs
- %i.fa.fa-list.fa-2x
- .col-md-3.responsive-side
- %ul.nav.nav-pills.nav-stacked
- %li{class: ("active" if (params[:f] == "active" || !params[:f]))}
- = link_to project_milestones_path(@project, f: "active") do
- Active
- %li{class: ("active" if params[:f] == "closed")}
- = link_to project_milestones_path(@project, f: "closed") do
- Closed
- %li{class: ("active" if params[:f] == "all")}
- = link_to project_milestones_path(@project, f: "all") do
- All
- .col-md-9
- .panel.panel-default
- %ul.well-list
- = render @milestones
+.milestones
+ .panel.panel-default
+ %ul.well-list
+ = render @milestones
- - if @milestones.blank?
- %li
- .nothing-here-block No milestones to show
+ - if @milestones.blank?
+ %li
+ .nothing-here-block No milestones to show
- = paginate @milestones, theme: "gitlab"
+ = paginate @milestones, theme: "gitlab"
diff --git a/app/views/projects/milestones/show.html.haml b/app/views/projects/milestones/show.html.haml
index 8263f7530a2..fea96f37011 100644
--- a/app/views/projects/milestones/show.html.haml
+++ b/app/views/projects/milestones/show.html.haml
@@ -1,57 +1,50 @@
-= render "projects/issues/head"
-%h3.page-title
+%h4.page-title
+ .issue-box{ class: issue_box_class(@milestone) }
+ - if @milestone.closed?
+ Closed
+ - elsif @milestone.expired?
+ Expired
+ - else
+ Open
Milestone ##{@milestone.iid}
+ %small.creator
+ = @milestone.expires_at
.pull-right
- if can?(current_user, :admin_milestone, @project)
- = link_to edit_project_milestone_path(@project, @milestone), class: "btn btn-grouped" do
+ = link_to edit_namespace_project_milestone_path(@project.namespace, @project, @milestone), class: "btn btn-grouped" do
%i.fa.fa-pencil-square-o
Edit
- if @milestone.active?
- = link_to 'Close Milestone', project_milestone_path(@project, @milestone, milestone: {state_event: :close }), method: :put, class: "btn btn-close btn-grouped"
+ = link_to 'Close Milestone', namespace_project_milestone_path(@project.namespace, @project, @milestone, milestone: {state_event: :close }), method: :put, class: "btn btn-close btn-grouped"
- else
- = link_to 'Reopen Milestone', project_milestone_path(@project, @milestone, milestone: {state_event: :activate }), method: :put, class: "btn btn-reopen btn-grouped"
+ = link_to 'Reopen Milestone', namespace_project_milestone_path(@project.namespace, @project, @milestone, milestone: {state_event: :activate }), method: :put, class: "btn btn-reopen btn-grouped"
+%hr
- if @milestone.issues.any? && @milestone.can_be_closed?
.alert.alert-success
%span All issues for this milestone are closed. You may close milestone now.
-.back-link
- = link_to project_milestones_path(@project) do
- &larr; To milestones list
-
-
-.issue-box{ class: issue_box_class(@milestone) }
- .state.clearfix
- .state-label
- - if @milestone.closed?
- Closed
- - elsif @milestone.expired?
- Expired
- - else
- Open
- .creator
- = @milestone.expires_at
-
- %h4.title
- = gfm escape_once(@milestone.title)
-
+%h3.issue-title
+ = gfm escape_once(@milestone.title)
+%div
- if @milestone.description.present?
.description
.wiki
= preserve do
= markdown @milestone.description
- .context
- %p
- Progress:
- #{@milestone.closed_items_count} closed
- &ndash;
- #{@milestone.open_items_count} open
- &nbsp;
- %span.light #{@milestone.percent_complete}% complete
- %span.pull-right= @milestone.expires_at
- .progress.progress-info
- .progress-bar{style: "width: #{@milestone.percent_complete}%;"}
+%hr
+.context
+ %p.lead
+ Progress:
+ #{@milestone.closed_items_count} closed
+ &ndash;
+ #{@milestone.open_items_count} open
+ &nbsp;
+ %span.light #{@milestone.percent_complete}% complete
+ %span.pull-right= @milestone.expires_at
+ .progress.progress-info
+ .progress-bar{style: "width: #{@milestone.percent_complete}%;"}
%ul.nav.nav-tabs
@@ -69,10 +62,10 @@
%span.badge= @users.count
.pull-right
- = link_to new_project_issue_path(@project, issue: { milestone_id: @milestone.id }), class: "btn btn-small btn-grouped", title: "New Issue" do
+ = link_to new_namespace_project_issue_path(@project.namespace, @project, issue: { milestone_id: @milestone.id }), class: "btn btn-grouped", title: "New Issue" do
%i.fa.fa-plus
New Issue
- = link_to 'Browse Issues', project_issues_path(@milestone.project, milestone_id: @milestone.id), class: "btn btn-small edit-milestone-link btn-grouped"
+ = link_to 'Browse Issues', namespace_project_issues_path(@milestone.project.namespace, @milestone.project, milestone_id: @milestone.id), class: "btn edit-milestone-link btn-grouped"
.tab-content
.tab-pane.active#tab-issues
diff --git a/app/views/projects/network/show.html.haml b/app/views/projects/network/show.html.haml
index 4a21b84fb85..c36bad1e94b 100644
--- a/app/views/projects/network/show.html.haml
+++ b/app/views/projects/network/show.html.haml
@@ -1,7 +1,7 @@
= render "head"
.project-network
.controls
- = form_tag project_network_path(@project, @id), method: :get, class: 'form-inline network-form' do |f|
+ = form_tag namespace_project_network_path(@project.namespace, @project, @id), method: :get, class: 'form-inline network-form' do |f|
= text_field_tag :extended_sha1, @options[:extended_sha1], placeholder: "Input an extended SHA1 syntax", class: 'search-input form-control input-mx-250 search-sha'
= button_tag class: 'btn btn-success btn-search-sha' do
%i.fa.fa-search
@@ -18,8 +18,8 @@
disableButtonIfEmptyField('#extended_sha1', '.btn-search-sha')
network_graph = new Network({
- url: '#{project_network_path(@project, @ref, @options.merge(format: :json))}',
- commit_url: '#{project_commit_path(@project, 'ae45ca32').gsub("ae45ca32", "%s")}',
+ url: '#{namespace_project_network_path(@project.namespace, @project, @ref, @options.merge(format: :json))}',
+ commit_url: '#{namespace_project_commit_path(@project.namespace, @project, 'ae45ca32').gsub("ae45ca32", "%s")}',
ref: '#{@ref}',
commit_id: '#{@commit.id}'
})
diff --git a/app/views/projects/new.html.haml b/app/views/projects/new.html.haml
index f5cd0f21e01..025c4fd5506 100644
--- a/app/views/projects/new.html.haml
+++ b/app/views/projects/new.html.haml
@@ -3,12 +3,15 @@
= render 'projects/errors'
.project-edit-content
- = form_for @project, remote: true, html: { class: 'new_project form-horizontal' } do |f|
+ = form_for @project, html: { class: 'new_project form-horizontal' } do |f|
.form-group.project-name-holder
- = f.label :name, class: 'control-label' do
- %strong Project name
+ = f.label :path, class: 'control-label' do
+ %strong Project path
.col-sm-10
- = f.text_field :name, placeholder: "Example Project", class: "form-control", tabindex: 1, autofocus: true
+ .input-group
+ = f.text_field :path, placeholder: "my-awesome-project", class: "form-control", tabindex: 1, autofocus: true
+ .input-group-addon
+ \.git
- if current_user.can_select_namespace?
.form-group
@@ -23,24 +26,8 @@
.col-sm-2
.col-sm-10
= link_to "#", class: 'js-toggle-button' do
- %i.fa.fa-pencil-square-o
- %span Customize repository name?
- .js-toggle-content.hide
- .form-group
- = f.label :path, class: 'control-label' do
- %span Repository name
- .col-sm-10
- .input-group
- = f.text_field :path, class: 'form-control'
- %span.input-group-addon .git
-
- .js-toggle-container
- .form-group
- .col-sm-2
- .col-sm-10
- = link_to "#", class: 'js-toggle-button' do
%i.fa.fa-upload
- %span Import existing repository?
+ %span Import existing repository by URL
.js-toggle-content.hide
.form-group.import-url-data
= f.label :import_url, class: 'control-label' do
@@ -52,7 +39,55 @@
%br
The import will time out after 4 minutes. For big repositories, use a clone/push combination.
For SVN repositories, check #{link_to "this migrating from SVN doc.", "http://doc.gitlab.com/ce/workflow/migrating_from_svn.html"}
- %hr
+
+ .project-import.form-group
+ .col-sm-2
+ .col-sm-10
+ - if github_import_enabled?
+ = link_to status_import_github_path do
+ %i.fa.fa-github
+ Import projects from GitHub
+ - else
+ = link_to '#', class: 'how_to_import_link light' do
+ %i.fa.fa-github
+ Import projects from GitHub
+ = render 'github_import_modal'
+
+ .project-import.form-group
+ .col-sm-2
+ .col-sm-10
+ - if bitbucket_import_enabled?
+ = link_to status_import_bitbucket_path do
+ %i.fa.fa-bitbucket
+ Import projects from Bitbucket
+ - else
+ = link_to '#', class: 'how_to_import_link light' do
+ %i.fa.fa-bitbucket
+ Import projects from Bitbucket
+ = render 'bitbucket_import_modal'
+
+ - unless request.host == 'gitlab.com'
+ .project-import.form-group
+ .col-sm-2
+ .col-sm-10
+ - if gitlab_import_enabled?
+ = link_to status_import_gitlab_path do
+ %i.fa.fa-heart
+ Import projects from GitLab.com
+ - else
+ = link_to '#', class: 'how_to_import_link light' do
+ %i.fa.fa-heart
+ Import projects from GitLab.com
+ = render 'gitlab_import_modal'
+
+ .project-import.form-group
+ .col-sm-2
+ .col-sm-10
+ = link_to new_import_gitorious_path do
+ %i.icon-gitorious.icon-gitorious-small
+ Import projects from Gitorious.org
+
+ %hr.prepend-botton-10
.form-group
= f.label :description, class: 'control-label' do
@@ -78,3 +113,11 @@
%i.fa.fa-spinner.fa-spin
Creating project &amp; repository.
%p Please wait a moment, this page will automatically refresh when ready.
+
+:coffeescript
+ $ ->
+ $('.how_to_import_link').bind 'click', (e) ->
+ e.preventDefault()
+ import_modal = $(this).parent().find(".modal").show()
+ $('.modal-header .close').bind 'click', ->
+ $(".modal").hide()
diff --git a/app/views/projects/new_tree/show.html.haml b/app/views/projects/new_tree/show.html.haml
deleted file mode 100644
index c47c0a3f642..00000000000
--- a/app/views/projects/new_tree/show.html.haml
+++ /dev/null
@@ -1,43 +0,0 @@
-%h3.page-title New file
-%hr
-.file-editor
- = form_tag(project_new_tree_path(@project, @id), method: :put, class: 'form-horizontal form-new-file') do
- .form-group.commit_message-group
- = label_tag 'file_name', class: 'control-label' do
- File name
- .col-sm-10
- .input-group
- %span.input-group-addon
- = @path[-1] == "/" ? @path : @path + "/"
- = text_field_tag 'file_name', params[:file_name], placeholder: "sample.rb", required: true, class: 'form-control'
- %span.input-group-addon
- on
- %span= @ref
-
- .form-group.commit_message-group
- = label_tag :encoding, class: "control-label" do
- Encoding
- .col-sm-10
- = select_tag :encoding, options_for_select([ "base64", "text" ], "text"), class: 'form-control'
- = render 'shared/commit_message_container', params: params,
- placeholder: 'Add new file'
- .file-holder
- .file-title
- %i.fa.fa-file
- .file-content.code
- %pre#editor= params[:content]
-
- = hidden_field_tag 'content', '', id: 'file-content'
- = render 'projects/commit_button', ref: @ref,
- cancel_path: project_tree_path(@project, @id)
-
-:javascript
- ace.config.set("modePath", gon.relative_url_root + "#{Gitlab::Application.config.assets.prefix}/ace-src-noconflict")
- var editor = ace.edit("editor");
-
- disableButtonIfAnyEmptyField($('.form-new-file'), '.form-control', '.btn-create')
-
- $(".js-commit-button").click(function(){
- $("#file-content").val(editor.getValue());
- $(".file-editor form").submit();
- });
diff --git a/app/views/projects/no_repo.html.haml b/app/views/projects/no_repo.html.haml
new file mode 100644
index 00000000000..720957e8336
--- /dev/null
+++ b/app/views/projects/no_repo.html.haml
@@ -0,0 +1,22 @@
+%h2
+ %i.fa.fa-warning
+ No repository
+
+%p.slead
+ The repository for this project does not exist.
+ %br
+ This means you can not push code until you create an empty repository or import existing one.
+%hr
+
+.no-repo-actions
+ = link_to namespace_project_repository_path(@project.namespace, @project), method: :post, class: 'btn btn-primary' do
+ Create empty bare repository
+
+ %strong.prepend-left-10.append-right-10 or
+
+ = link_to new_namespace_project_import_path(@project.namespace, @project), class: 'btn' do
+ Import repository
+
+- if can? current_user, :remove_project, @project
+ .prepend-top-20
+ = link_to 'Remove project', project_path(@project), data: { confirm: remove_project_message(@project)}, method: :delete, class: "btn btn-remove pull-right"
diff --git a/app/views/projects/notes/_edit_form.html.haml b/app/views/projects/notes/_edit_form.html.haml
new file mode 100644
index 00000000000..b51ca795418
--- /dev/null
+++ b/app/views/projects/notes/_edit_form.html.haml
@@ -0,0 +1,14 @@
+.note-edit-form
+ = form_for note, url: namespace_project_note_path(@project.namespace, @project, note), method: :put, remote: true, authenticity_token: true do |f|
+ = render layout: 'projects/md_preview', locals: { preview_class: "note-text" } do
+ = render 'projects/zen', f: f, attr: :note,
+ classes: 'note_text js-note-text'
+
+ .comment-hints.clearfix
+ .pull-left Comments are parsed with #{link_to "GitLab Flavored Markdown", help_page_path("markdown", "markdown"),{ target: '_blank', tabindex: -1 }}
+ .pull-right Attach files by dragging &amp; dropping or #{link_to "selecting them", '#', class: 'markdown-selector', tabindex: -1 }.
+
+ .note-form-actions
+ .buttons
+ = f.submit 'Save Comment', class: "btn btn-primary btn-save btn-grouped js-comment-button"
+ = link_to 'Cancel', "#", class: "btn btn-cancel note-edit-cancel" \ No newline at end of file
diff --git a/app/views/projects/notes/_form.html.haml b/app/views/projects/notes/_form.html.haml
index c68b3817e79..4476337cb10 100644
--- a/app/views/projects/notes/_form.html.haml
+++ b/app/views/projects/notes/_form.html.haml
@@ -1,27 +1,18 @@
-= form_for [@project, @note], remote: true, html: { :'data-type' => 'json', multipart: true, id: nil, class: "new_note js-new-note-form common-note-form gfm-form" }, authenticity_token: true do |f|
+= form_for [@project.namespace.becomes(Namespace), @project, @note], remote: true, html: { :'data-type' => 'json', multipart: true, id: nil, class: "new_note js-new-note-form common-note-form gfm-form" }, authenticity_token: true do |f|
= note_target_fields
= f.hidden_field :commit_id
= f.hidden_field :line_code
= f.hidden_field :noteable_id
= f.hidden_field :noteable_type
- %ul.nav.nav-tabs
- %li.active
- = link_to '#note-write-holder', class: 'js-note-write-button' do
- Write
- %li
- = link_to '#note-preview-holder', class: 'js-note-preview-button', data: { url: preview_project_notes_path(@project) } do
- Preview
- %div
- .note-write-holder
- = render 'projects/zen', f: f, attr: :note,
- classes: 'note_text js-note-text'
- .light.clearfix
- .pull-left Comments are parsed with #{link_to "GitLab Flavored Markdown", help_page_path("markdown", "markdown"),{ target: '_blank', tabindex: -1 }}
- .pull-right Attach images (JPG, PNG, GIF) by dragging &amp; dropping or #{link_to "selecting them", '#', class: 'markdown-selector', tabindex: -1 }.
+ = render layout: 'projects/md_preview', locals: { preview_class: "note-text" } do
+ = render 'projects/zen', f: f, attr: :note,
+ classes: 'note_text js-note-text'
+
+ .comment-hints.clearfix
+ .pull-left Comments are parsed with #{link_to "GitLab Flavored Markdown", help_page_path("markdown", "markdown"),{ target: '_blank', tabindex: -1 }}
+ .pull-right Attach files by dragging &amp; dropping or #{link_to "selecting them", '#', class: 'markdown-selector', tabindex: -1 }.
- .note-preview-holder.hide
- .js-note-preview
.note-form-actions
.buttons
@@ -29,13 +20,5 @@
= yield(:note_actions)
%a.btn.grouped.js-close-discussion-note-form Cancel
- .note-form-option
- %a.choose-btn.btn.js-choose-note-attachment-button
- %i.fa.fa-paperclip
- %span Choose File ...
- &nbsp;
- %span.file_name.js-attachment-filename File name...
- = f.file_field :attachment, class: "js-note-attachment-input hidden"
-
:javascript
- window.project_image_path_upload = "#{upload_image_project_path @project}";
+ window.project_uploads_path = "#{namespace_project_uploads_path @project.namespace, @project}";
diff --git a/app/views/projects/notes/_note.html.haml b/app/views/projects/notes/_note.html.haml
index 814bf19970c..f3d00a6f06d 100644
--- a/app/views/projects/notes/_note.html.haml
+++ b/app/views/projects/notes/_note.html.haml
@@ -1,7 +1,10 @@
-%li.timeline-entry{ id: dom_id(note), class: dom_class(note), data: { discussion: note.discussion_id } }
+%li.timeline-entry{ id: dom_id(note), class: [dom_class(note), "note-row-#{note.id}", ('system-note' if note.system)], data: { discussion: note.discussion_id } }
.timeline-entry-inner
.timeline-icon
- = image_tag avatar_icon(note.author_email), class: "avatar s40"
+ - if note.system
+ %span.fa.fa-circle
+ - else
+ = image_tag avatar_icon(note.author_email), class: "avatar s40"
.timeline-content
.note-header
.note-actions
@@ -14,56 +17,53 @@
%i.fa.fa-pencil-square-o
Edit
&nbsp;
- = 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
+ = link_to namespace_project_note_path(@project.namespace, @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.fa.fa-trash-o.cred
Remove
+ - if note.system
+ = image_tag avatar_icon(note.author_email), class: "avatar s16"
= link_to_member(@project, note.author, avatar: false)
+ %span.author-username
+ = '@' + note.author.username
%span.note-last-update
= note_timestamp(note)
- - if note.upvote?
- %span.vote.upvote.label.label-success
- %i.fa.fa-thumbs-up
- \+1
- - if note.downvote?
- %span.vote.downvote.label.label-danger
- %i.fa.fa-thumbs-down
- \-1
+ - if note.superceded?(@notes)
+ - if note.upvote?
+ %span.vote.upvote.label.label-gray.strikethrough
+ %i.fa.fa-thumbs-up
+ \+1
+ - if note.downvote?
+ %span.vote.downvote.label.label-gray.strikethrough
+ %i.fa.fa-thumbs-down
+ \-1
+ - else
+ - if note.upvote?
+ %span.vote.upvote.label.label-success
+ %i.fa.fa-thumbs-up
+ \+1
+ - if note.downvote?
+ %span.vote.downvote.label.label-danger
+ %i.fa.fa-thumbs-down
+ \-1
.note-body
.note-text
= preserve do
= markdown(note.note, {no_header_anchors: true})
-
- .note-edit-form
- = 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.clearfix
- = f.submit 'Save changes', class: "btn btn-primary btn-save js-comment-button"
-
- .note-form-option
- %a.choose-btn.btn.js-choose-note-attachment-button
- %i.fa.fa-paperclip
- %span Choose File ...
- &nbsp;
- %span.file_name.js-attachment-filename File name...
- = f.file_field :attachment, class: "js-note-attachment-input hidden"
-
- = link_to 'Cancel', "#", class: "btn btn-cancel note-edit-cancel"
-
+ = render 'projects/notes/edit_form', note: note
- if note.attachment.url
.note-attachment
- if note.attachment.image?
- = link_to note.attachment.secure_url, target: '_blank' do
- = image_tag note.attachment.secure_url, class: 'note-image-attach'
- .attachment.pull-right
- = link_to note.attachment.secure_url, target: "_blank" do
+ = link_to note.attachment.url, target: '_blank' do
+ = image_tag note.attachment.url, class: 'note-image-attach'
+ .attachment
+ = link_to note.attachment.url, target: "_blank" do
%i.fa.fa-paperclip
= note.attachment_identifier
- = link_to delete_attachment_project_note_path(@project, note),
+ = link_to delete_attachment_namespace_project_note_path(@project.namespace, @project, note),
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.fa.fa-trash-o.cred
.clear
diff --git a/app/views/projects/notes/_notes_with_form.html.haml b/app/views/projects/notes/_notes_with_form.html.haml
index 04ee17a40a0..813e37276bd 100644
--- a/app/views/projects/notes/_notes_with_form.html.haml
+++ b/app/views/projects/notes/_notes_with_form.html.haml
@@ -7,4 +7,4 @@
= render "projects/notes/form"
:javascript
- new Notes("#{project_notes_path(target_id: @noteable.id, target_type: @noteable.class.name.underscore)}", #{@notes.map(&:id).to_json}, #{Time.now.to_i})
+ new Notes("#{namespace_project_notes_path(namespace_id: @project.namespace, target_id: @noteable.id, target_type: @noteable.class.name.underscore)}", #{@notes.map(&:id).to_json}, #{Time.now.to_i})
diff --git a/app/views/projects/notes/discussions/_active.html.haml b/app/views/projects/notes/discussions/_active.html.haml
index 52c06ec172d..7c6f7243173 100644
--- a/app/views/projects/notes/discussions/_active.html.haml
+++ b/app/views/projects/notes/discussions/_active.html.haml
@@ -8,7 +8,7 @@
%div
= link_to_member(@project, note.author, avatar: false)
started a discussion
- = link_to diffs_project_merge_request_path(note.project, note.noteable, anchor: note.line_code) do
+ = link_to diffs_namespace_project_merge_request_path(note.project.namespace, note.project, note.noteable, anchor: note.line_code) do
%strong on the diff
.last-update.hide.js-toggle-content
- last_note = discussion_notes.last
diff --git a/app/views/projects/notes/discussions/_commit.html.haml b/app/views/projects/notes/discussions/_commit.html.haml
index 94f16a5f02e..62609cfc1c8 100644
--- a/app/views/projects/notes/discussions/_commit.html.haml
+++ b/app/views/projects/notes/discussions/_commit.html.haml
@@ -8,7 +8,7 @@
%div
= link_to_member(@project, note.author, avatar: false)
started a discussion on commit
- = link_to(note.noteable.short_id, project_commit_path(note.project, note.noteable), class: 'monospace')
+ = link_to(note.noteable.short_id, namespace_project_commit_path(note.project.namespace, note.project, note.noteable), class: 'monospace')
.last-update.hide.js-toggle-content
- last_note = discussion_notes.last
last updated by
diff --git a/app/views/projects/notes/discussions/_diff.html.haml b/app/views/projects/notes/discussions/_diff.html.haml
index b4d1cce7980..f717c77a898 100644
--- a/app/views/projects/notes/discussions/_diff.html.haml
+++ b/app/views/projects/notes/discussions/_diff.html.haml
@@ -19,8 +19,10 @@
%td.new_line= "..."
%td.line_content.matched= line.text
- else
- %td.old_line= raw(line.type == "new" ? "&nbsp;" : line.old_pos)
- %td.new_line= raw(line.type == "old" ? "&nbsp;" : line.new_pos)
+ %td.old_line{class: line.type == "new" ? "new" : "old"}
+ = raw(line.type == "new" ? "&nbsp;" : line.old_pos)
+ %td.new_line{class: line.type == "new" ? "new" : "old"}
+ = raw(line.type == "old" ? "&nbsp;" : line.new_pos)
%td.line_content{class: "noteable_line #{line.type} #{line_code}", "line_code" => line_code}= raw diff_line_content(line.text)
- if line_code == note.line_code
diff --git a/app/views/projects/protected_branches/_branches_list.html.haml b/app/views/projects/protected_branches/_branches_list.html.haml
new file mode 100644
index 00000000000..5406b80dc16
--- /dev/null
+++ b/app/views/projects/protected_branches/_branches_list.html.haml
@@ -0,0 +1,34 @@
+- unless @branches.empty?
+ %br
+ %h4 Already Protected:
+ %table.table.protected-branches-list
+ %thead
+ %tr.no-border
+ %th Branch
+ %th Developers can push
+ %th Last commit
+ %th
+
+ %tbody
+ - @branches.each do |branch|
+ - @url = namespace_project_protected_branch_path(@project.namespace, @project, branch)
+ %tr
+ %td
+ = link_to namespace_project_commits_path(@project.namespace, @project, branch.name) do
+ %strong= branch.name
+ - if @project.root_ref?(branch.name)
+ %span.label.label-info default
+ %td
+ = check_box_tag "developers_can_push", branch.id, branch.developers_can_push, "data-url" => @url
+ %td
+ - if commit = branch.commit
+ = link_to namespace_project_commit_path(@project.namespace, @project, commit.id), class: 'commit_short_id' do
+ = commit.short_id
+ &middot;
+ #{time_ago_with_tooltip(commit.committed_date)}
+ - else
+ (branch was removed from repository)
+ %td
+ .pull-right
+ - if can? current_user, :admin_project, @project
+ = link_to 'Unprotect', [@project.namespace.becomes(Namespace), @project, branch], data: { confirm: 'Branch will be writable for developers. Are you sure?' }, method: :delete, class: "btn btn-remove btn-small"
diff --git a/app/views/projects/protected_branches/index.html.haml b/app/views/projects/protected_branches/index.html.haml
index 227a2f9a061..dc20e96732e 100644
--- a/app/views/projects/protected_branches/index.html.haml
+++ b/app/views/projects/protected_branches/index.html.haml
@@ -11,7 +11,7 @@
%p Read more about #{link_to "project permissions", help_page_path("permissions", "permissions"), class: "underlined-link"}
- if can? current_user, :admin_project, @project
- = form_for [@project, @protected_branch], html: { class: 'form-horizontal' } do |f|
+ = form_for [@project.namespace.becomes(Namespace), @project, @protected_branch], html: { class: 'form-horizontal' } do |f|
-if @protected_branch.errors.any?
.alert.alert-danger
%ul
@@ -22,29 +22,14 @@
= f.label :name, "Branch", class: 'control-label'
.col-sm-10
= f.select(:name, @project.open_branches.map { |br| [br.name, br.name] } , {include_blank: "Select branch"}, {class: "select2"})
+ .form-group
+ = f.label :developers_can_push, class: 'control-label' do
+ Developers can push
+ .col-sm-10
+ .checkbox
+ = f.check_box :developers_can_push
+ %span.descr Allow developers to push to this branch
.form-actions
= f.submit 'Protect', class: "btn-create btn"
-- unless @branches.empty?
- %h5 Already Protected:
- %ul.bordered-list.protected-branches-list
- - @branches.each do |branch|
- %li
- %h4
- = link_to project_commits_path(@project, branch.name) do
- %strong= branch.name
- - if @project.root_ref?(branch.name)
- %span.label.label-info default
- %span.label.label-success
- %i.fa.fa-lock
- .pull-right
- - if can? current_user, :admin_project, @project
- = link_to 'Unprotect', [@project, branch], data: { confirm: 'Branch will be writable for developers. Are you sure?' }, method: :delete, class: "btn btn-remove btn-small"
+= render 'branches_list'
- - 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))
- #{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 948a21aa816..49ce6c0888e 100644
--- a/app/views/projects/refs/logs_tree.js.haml
+++ b/app/views/projects/refs/logs_tree.js.haml
@@ -11,9 +11,9 @@
- if @logs.present?
:plain
var current_url = location.href.replace(/\/?$/, '/');
- var log_url = '#{project_tree_url(@project, tree_join(@ref, @path || '/'))}'.replace(/\/?$/, '/');
+ var log_url = '#{namespace_project_tree_url(@project.namespace, @project, tree_join(@ref, @path || '/'))}'.replace(/\/?$/, '/');
if(current_url == log_url) {
// Load 10 more commit log for each file in tree
// if we still on the same page
- ajaxGet('#{logs_file_project_ref_path(@project, @ref, @path || '/', offset: (@offset + @limit))}');
+ ajaxGet('#{logs_file_namespace_project_ref_path(@project.namespace, @project, @ref, @path || '/', offset: (@offset + @limit))}');
}
diff --git a/app/views/projects/repositories/_download_archive.html.haml b/app/views/projects/repositories/_download_archive.html.haml
index ce69adeb48c..26669fb00a9 100644
--- a/app/views/projects/repositories/_download_archive.html.haml
+++ b/app/views/projects/repositories/_download_archive.html.haml
@@ -3,7 +3,7 @@
- split_button = split_button || false
- if split_button == true
%span.btn-group{class: btn_class}
- = link_to archive_project_repository_path(@project, ref: ref, format: 'zip'), class: 'btn', rel: 'nofollow' do
+ = link_to archive_namespace_project_repository_path(@project.namespace, @project, ref: ref, format: 'zip'), class: 'btn', rel: 'nofollow' do
%i.fa.fa-download
%span Download zip
%a.btn.dropdown-toggle{ 'data-toggle' => 'dropdown' }
@@ -12,26 +12,26 @@
Select Archive Format
%ul.dropdown-menu{ role: 'menu' }
%li
- = link_to archive_project_repository_path(@project, ref: ref, format: 'zip'), rel: 'nofollow' do
+ = link_to archive_namespace_project_repository_path(@project.namespace, @project, ref: ref, format: 'zip'), rel: 'nofollow' do
%i.fa.fa-download
%span Download zip
%li
- = link_to archive_project_repository_path(@project, ref: ref, format: 'tar.gz'), rel: 'nofollow' do
+ = link_to archive_namespace_project_repository_path(@project.namespace, @project, ref: ref, format: 'tar.gz'), rel: 'nofollow' do
%i.fa.fa-download
%span Download tar.gz
%li
- = link_to archive_project_repository_path(@project, ref: ref, format: 'tar.bz2'), rel: 'nofollow' do
+ = link_to archive_namespace_project_repository_path(@project.namespace, @project, ref: ref, format: 'tar.bz2'), rel: 'nofollow' do
%i.fa.fa-download
%span Download tar.bz2
%li
- = link_to archive_project_repository_path(@project, ref: ref, format: 'tar'), rel: 'nofollow' do
+ = link_to archive_namespace_project_repository_path(@project.namespace, @project, ref: ref, format: 'tar'), rel: 'nofollow' do
%i.fa.fa-download
%span Download tar
- else
%span.btn-group{class: btn_class}
- = link_to archive_project_repository_path(@project, ref: ref, format: 'zip'), class: 'btn', rel: 'nofollow' do
+ = link_to archive_namespace_project_repository_path(@project.namespace, @project, ref: ref, format: 'zip'), class: 'btn', rel: 'nofollow' do
%i.fa.fa-download
%span zip
- = link_to archive_project_repository_path(@project, ref: ref, format: 'tar.gz'), class: 'btn', rel: 'nofollow' do
+ = link_to archive_namespace_project_repository_path(@project.namespace, @project, ref: ref, format: 'tar.gz'), class: 'btn', rel: 'nofollow' do
%i.fa.fa-download
%span tar.gz
diff --git a/app/views/projects/repositories/_feed.html.haml b/app/views/projects/repositories/_feed.html.haml
index c77ffff43fe..f3526ad0747 100644
--- a/app/views/projects/repositories/_feed.html.haml
+++ b/app/views/projects/repositories/_feed.html.haml
@@ -1,7 +1,7 @@
- commit = update
%tr
%td
- = link_to project_commits_path(@project, commit.head.name) do
+ = link_to namespace_project_commits_path(@project.namespace, @project, commit.head.name) do
%strong
= commit.head.name
- if @project.root_ref?(commit.head.name)
@@ -9,7 +9,7 @@
%td
%div
- = link_to project_commits_path(@project, commit.id) do
+ = link_to namespace_project_commits_path(@project.namespace, @project, commit.id) do
%code= commit.short_id
= image_tag avatar_icon(commit.author_email), class: "", width: 16, alt: ''
= gfm escape_once(truncate(commit.title, length: 40))
diff --git a/app/views/projects/services/_form.html.haml b/app/views/projects/services/_form.html.haml
index 1151f22c7e8..8db6d67e06b 100644
--- a/app/views/projects/services/_form.html.haml
+++ b/app/views/projects/services/_form.html.haml
@@ -5,12 +5,12 @@
%p= @service.description
.back-link
- = link_to project_services_path(@project) do
+ = link_to namespace_project_services_path(@project.namespace, @project) do
&larr; to services
%hr
-= form_for(@service, as: :service, url: project_service_path(@project, @service.to_param), method: :put, html: { class: 'form-horizontal' }) do |f|
+= form_for(@service, as: :service, url: namespace_project_service_path(@project.namespace, @project, @service.to_param), method: :put, html: { class: 'form-horizontal' }) do |f|
- if @service.errors.any?
.alert.alert-danger
%ul
@@ -19,7 +19,8 @@
- if @service.help.present?
.bs-callout
- = @service.help
+ = preserve do
+ = markdown @service.help
.form-group
= f.label :active, "Active", class: "control-label"
@@ -52,4 +53,4 @@
= f.submit 'Save', class: 'btn btn-save'
&nbsp;
- if @service.valid? && @service.activated? && @service.can_test?
- = link_to 'Test settings', test_project_service_path(@project, @service.to_param), class: 'btn'
+ = link_to 'Test settings', test_namespace_project_service_path(@project.namespace, @project, @service.to_param), class: 'btn'
diff --git a/app/views/projects/services/index.html.haml b/app/views/projects/services/index.html.haml
index 7271dd830ca..d615d128653 100644
--- a/app/views/projects/services/index.html.haml
+++ b/app/views/projects/services/index.html.haml
@@ -1,13 +1,22 @@
%h3.page-title Project services
%p.light Project services allow you to integrate GitLab with other applications
-%hr
-%ul.bordered-list
+%table.table
+ %thead
+ %tr
+ %th
+ %th Service
+ %th Desription
+ %th Last edit
- @services.sort_by(&:title).each do |service|
- %li
- %h4
- = link_to edit_project_service_path(@project, service.to_param) do
- = service.title
- .pull-right
- = boolean_to_icon service.activated?
- %p= service.description
+ %tr
+ %td
+ = boolean_to_icon service.activated?
+ %td
+ = link_to edit_namespace_project_service_path(@project.namespace, @project, service.to_param) do
+ %strong= service.title
+ %td
+ = service.description
+ %td.light
+ = time_ago_in_words service.updated_at
+ ago
diff --git a/app/views/projects/show.html.haml b/app/views/projects/show.html.haml
index 9b06ebe95a4..787cfd9304f 100644
--- a/app/views/projects/show.html.haml
+++ b/app/views/projects/show.html.haml
@@ -1,3 +1,7 @@
+- if current_user && can?(current_user, :download_code, @project)
+ = render 'shared/no_ssh'
+ = render 'shared/no_password'
+
= render "home_panel"
- readme = @repository.readme
@@ -11,20 +15,22 @@
Readme
.project-home-links
- unless @project.empty_repo?
- = link_to pluralize(number_with_delimiter(@repository.commit_count), 'commit'), project_commits_path(@project, @ref || @repository.root_ref)
- = link_to pluralize(number_with_delimiter(@repository.branch_names.count), 'branch'), project_branches_path(@project)
- = link_to pluralize(number_with_delimiter(@repository.tag_names.count), 'tag'), project_tags_path(@project)
+ = link_to pluralize(number_with_delimiter(@repository.commit_count), 'commit'), namespace_project_commits_path(@project.namespace, @project, @ref || @repository.root_ref)
+ = link_to pluralize(number_with_delimiter(@repository.branch_names.count), 'branch'), namespace_project_branches_path(@project.namespace, @project)
+ = link_to pluralize(number_with_delimiter(@repository.tag_names.count), 'tag'), namespace_project_tags_path(@project.namespace, @project)
%span.light.prepend-left-20= repository_size
.tab-content
.tab-pane.active#tab-activity
.row
+ = link_to '#aside', class: 'show-aside' do
+ %i.fa.fa-angle-left
%section.col-md-9
= render "events/event_last_push", event: @last_push
= render 'shared/event_filter'
.content_list
= spinner
- %aside.col-md-3.project-side.hidden-sm.hidden-xs
+ %aside.col-md-3.project-side
.clearfix
- if @project.archived?
.alert.alert-warning
@@ -38,15 +44,15 @@
%i.fa.fa-code-fork.project-fork-icon
Forked from:
%br
- = link_to @project.forked_from_project.name_with_namespace, project_path(@project.forked_from_project)
+ = link_to @project.forked_from_project.name_with_namespace, namespace_project_path(@project.namespace, @project.forked_from_project)
- unless @project.empty_repo?
- = link_to project_compare_index_path(@project, from: @repository.root_ref, to: @ref || @repository.root_ref), class: 'btn btn-block' do
+ = link_to namespace_project_compare_index_path(@project.namespace, @project, from: @repository.root_ref, to: @ref || @repository.root_ref), class: 'btn btn-block' do
Compare code
- if @repository.version
- version = @repository.version
- = link_to project_blob_path(@project, tree_join(@repository.root_ref, version.name)), class: 'btn btn-block' do
+ = link_to namespace_project_blob_path(@project.namespace, @project, tree_join(@repository.root_ref, version.name)), class: 'btn btn-block' do
Version:
%span.count
= @repository.blob_by_oid(version.id).data
@@ -65,16 +71,16 @@
- @project.ci_services.each do |ci_service|
- if ci_service.active? && ci_service.respond_to?(:builds_path)
- if ci_service.respond_to?(:status_img_path)
- = link_to ci_service.builds_path do
+ = link_to ci_service.builds_path, :'data-no-turbolink' => 'data-no-turbolink' do
= image_tag ci_service.status_img_path, alt: "build status"
- else
%span.light CI provided by
- = link_to ci_service.title, ci_service.builds_path
+ = link_to ci_service.title, ci_service.builds_path, :'data-no-turbolink' => 'data-no-turbolink'
- if readme
.tab-pane#tab-readme
%article.readme-holder#README
- = link_to project_blob_path(@project, tree_join(@repository.root_ref, readme.name)) do
+ = link_to namespace_project_blob_path(@project.namespace, @project, tree_join(@repository.root_ref, readme.name)) do
%h4.readme-file-title
%i.fa.fa-file
= readme.name
diff --git a/app/views/projects/snippets/edit.html.haml b/app/views/projects/snippets/edit.html.haml
index f6a5bf9e4ff..2d4d5d030ab 100644
--- a/app/views/projects/snippets/edit.html.haml
+++ b/app/views/projects/snippets/edit.html.haml
@@ -1,4 +1,4 @@
%h3.page-title
Edit snippet
%hr
-= render "shared/snippets/form", url: project_snippet_path(@project, @snippet)
+= render "shared/snippets/form", url: namespace_project_snippet_path(@project.namespace, @project, @snippet)
diff --git a/app/views/projects/snippets/index.html.haml b/app/views/projects/snippets/index.html.haml
index e60f9a44322..e2d8ec673a1 100644
--- a/app/views/projects/snippets/index.html.haml
+++ b/app/views/projects/snippets/index.html.haml
@@ -1,7 +1,7 @@
%h3.page-title
Snippets
- if can? current_user, :write_project_snippet, @project
- = link_to new_project_snippet_path(@project), class: "btn btn-new pull-right", title: "New Snippet" do
+ = link_to new_namespace_project_snippet_path(@project.namespace, @project), class: "btn btn-new pull-right", title: "New Snippet" do
Add new snippet
%p.light
diff --git a/app/views/projects/snippets/new.html.haml b/app/views/projects/snippets/new.html.haml
index 10f684b6316..bb659dba0cf 100644
--- a/app/views/projects/snippets/new.html.haml
+++ b/app/views/projects/snippets/new.html.haml
@@ -1,4 +1,4 @@
%h3.page-title
New snippet
%hr
-= render "shared/snippets/form", url: project_snippets_path(@project, @snippet)
+= render "shared/snippets/form", url: namespace_project_snippets_path(@project.namespace, @project, @snippet)
diff --git a/app/views/projects/snippets/show.html.haml b/app/views/projects/snippets/show.html.haml
index ada0d30c496..345848fa6d1 100644
--- a/app/views/projects/snippets/show.html.haml
+++ b/app/views/projects/snippets/show.html.haml
@@ -2,7 +2,7 @@
= @snippet.title
.pull-right
- = link_to new_project_snippet_path(@project), class: "btn btn-new", title: "New Snippet" do
+ = link_to new_namespace_project_snippet_path(@project.namespace, @project), class: "btn btn-new", title: "New Snippet" do
Add new snippet
%hr
@@ -17,7 +17,7 @@
= @snippet.author_name
.back-link
- = link_to project_snippets_path(@project) do
+ = link_to namespace_project_snippets_path(@project.namespace, @project) do
&larr; project snippets
.file-holder
@@ -28,10 +28,10 @@
.options
.btn-group
- if can?(current_user, :modify_project_snippet, @snippet)
- = link_to "edit", edit_project_snippet_path(@project, @snippet), class: "btn btn-small", title: 'Edit Snippet'
- = link_to "raw", raw_project_snippet_path(@project, @snippet), class: "btn btn-small", target: "_blank"
+ = link_to "edit", edit_namespace_project_snippet_path(@project.namespace, @project, @snippet), class: "btn btn-small", title: 'Edit Snippet'
+ = link_to "raw", raw_namespace_project_snippet_path(@project.namespace, @project, @snippet), class: "btn btn-small", target: "_blank"
- if can?(current_user, :admin_project_snippet, @snippet)
- = link_to "remove", project_snippet_path(@project, @snippet), method: :delete, data: { confirm: "Are you sure?" }, class: "btn btn-small btn-remove", title: 'Delete Snippet'
+ = link_to "remove", namespace_project_snippet_path(@project.namespace, @project, @snippet), method: :delete, data: { confirm: "Are you sure?" }, class: "btn btn-small btn-remove", title: 'Delete Snippet'
= render 'shared/snippets/blob'
%div#notes= render "projects/notes/notes_with_form"
diff --git a/app/views/projects/tags/_tag.html.haml b/app/views/projects/tags/_tag.html.haml
index f93c1b4211f..8da07222cba 100644
--- a/app/views/projects/tags/_tag.html.haml
+++ b/app/views/projects/tags/_tag.html.haml
@@ -1,17 +1,17 @@
- commit = @repository.commit(tag.target)
%li
%h4
- = link_to project_commits_path(@project, tag.name), class: "" do
+ = link_to namespace_project_commits_path(@project.namespace, @project, tag.name), class: "" do
%i.fa.fa-tag
= tag.name
- if tag.message.present?
&nbsp;
- = tag.message
+ = strip_gpg_signature(tag.message)
.pull-right
- if can? current_user, :download_code, @project
= render 'projects/repositories/download_archive', ref: tag.name, btn_class: 'btn-grouped btn-group-small'
- if can?(current_user, :admin_project, @project)
- = link_to project_tag_path(@project, tag.name), class: 'btn btn-small btn-remove remove-row grouped', method: :delete, data: { confirm: 'Removed tag cannot be restored. Are you sure?'}, remote: true do
+ = link_to namespace_project_tag_path(@project.namespace, @project, tag.name), class: 'btn btn-small btn-remove remove-row grouped', method: :delete, data: { confirm: 'Removed tag cannot be restored. Are you sure?'}, remote: true do
%i.fa.fa-trash-o
- if commit
diff --git a/app/views/projects/tags/index.html.haml b/app/views/projects/tags/index.html.haml
index ac74e3b6d36..f1bc2bc9a2b 100644
--- a/app/views/projects/tags/index.html.haml
+++ b/app/views/projects/tags/index.html.haml
@@ -4,7 +4,7 @@
Git Tags
- if can? current_user, :push_code, @project
.pull-right
- = link_to new_project_tag_path(@project), class: 'btn btn-create new-tag-btn' do
+ = link_to new_namespace_project_tag_path(@project.namespace, @project), class: 'btn btn-create new-tag-btn' do
%i.fa.fa-add-sign
New tag
diff --git a/app/views/projects/tags/new.html.haml b/app/views/projects/tags/new.html.haml
index ad7ff8d3db8..655044438d5 100644
--- a/app/views/projects/tags/new.html.haml
+++ b/app/views/projects/tags/new.html.haml
@@ -5,7 +5,7 @@
%h3.page-title
%i.fa.fa-code-fork
New tag
-= form_tag project_tags_path, method: :post, class: "form-horizontal" do
+= form_tag namespace_project_tags_path, method: :post, id: "new-tag-form", class: "form-horizontal" do
.form-group
= label_tag :tag_name, 'Name for new tag', class: 'control-label'
.col-sm-10
@@ -22,9 +22,10 @@
.light (Optional) Entering a message will create an annotated tag.
.form-actions
= button_tag 'Create tag', class: 'btn btn-create', tabindex: 3
- = link_to 'Cancel', project_tags_path(@project), class: 'btn btn-cancel'
+ = link_to 'Cancel', namespace_project_tags_path(@project.namespace, @project), class: 'btn btn-cancel'
:javascript
+ disableButtonIfAnyEmptyField($("#new-tag-form"), ".form-control", ".btn-create");
var availableTags = #{@project.repository.ref_names.to_json};
$("#ref").autocomplete({
diff --git a/app/views/projects/team_members/_form.html.haml b/app/views/projects/team_members/_form.html.haml
index 2bf61fa12bb..166b6362a07 100644
--- a/app/views/projects/team_members/_form.html.haml
+++ b/app/views/projects/team_members/_form.html.haml
@@ -1,7 +1,7 @@
%h3.page-title
New project member(s)
-= form_for @user_project_relation, as: :project_member, url: project_team_members_path(@project), html: { class: "form-horizontal users-project-form" } do |f|
+= form_for @user_project_relation, as: :project_member, url: namespace_project_team_members_path(@project.namespace, @project), html: { class: "form-horizontal users-project-form" } do |f|
-if @user_project_relation.errors.any?
.alert.alert-danger
%ul
@@ -17,8 +17,13 @@
%p 2. Set access level for them
.form-group
= f.label :access_level, "Project Access", class: 'control-label'
- .col-sm-10= select_tag :access_level, options_for_select(Gitlab::Access.options, @user_project_relation.access_level), class: "project-access-select select2"
+ .col-sm-10
+ = select_tag :access_level, options_for_select(Gitlab::Access.options, @user_project_relation.access_level), class: "project-access-select select2"
+ .help-block
+ Read more about role permissions
+ %strong= link_to "here", help_page_path("permissions", "permissions"), class: "vlink"
+
.form-actions
= f.submit 'Add users', class: "btn btn-create"
- = link_to "Cancel", project_team_index_path(@project), class: "btn btn-cancel"
+ = link_to "Cancel", namespace_project_team_index_path(@project.namespace, @project), class: "btn btn-cancel"
diff --git a/app/views/projects/team_members/_team_member.html.haml b/app/views/projects/team_members/_team_member.html.haml
index 5f29b58de32..61c50af31bf 100644
--- a/app/views/projects/team_members/_team_member.html.haml
+++ b/app/views/projects/team_members/_team_member.html.haml
@@ -4,10 +4,10 @@
- if current_user_can_admin_project
- unless @project.personal? && user == current_user
.pull-left
- = form_for(member, as: :project_member, url: project_team_member_path(@project, member.user)) do |f|
- = f.select :access_level, options_for_select(ProjectMember.access_roles, member.access_level), {}, class: "medium project-access-select span2 trigger-submit"
+ = form_for(member, as: :project_member, url: namespace_project_team_member_path(@project.namespace, @project, member.user)) do |f|
+ = f.select :access_level, options_for_select(ProjectMember.access_roles, member.access_level), {}, class: "trigger-submit"
&nbsp;
- = 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
+ = link_to namespace_project_team_member_path(@project.namespace, @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.fa.fa-minus.fa-inverse
= 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 d1f46c61b2e..9e31d47117e 100644
--- a/app/views/projects/team_members/import.html.haml
+++ b/app/views/projects/team_members/import.html.haml
@@ -3,12 +3,12 @@
%p.light
Only project members will be imported. Group members will be skipped.
%hr
-= form_tag apply_import_project_team_members_path(@project), method: 'post', class: 'form-horizontal' do
+= form_tag apply_import_namespace_project_team_members_path(@project.namespace, @project), method: 'post', class: 'form-horizontal' do
.form-group
= label_tag :source_project_id, "Project", class: 'control-label'
.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: "select2 lg", required: true)
.form-actions
= button_tag 'Import project members', class: "btn btn-create"
- = link_to "Cancel", project_team_index_path(@project), class: "btn btn-cancel"
+ = link_to "Cancel", namespace_project_team_index_path(@project.namespace, @project), class: "btn btn-cancel"
diff --git a/app/views/projects/team_members/index.html.haml b/app/views/projects/team_members/index.html.haml
index ecb7c689e8a..fcc879a58df 100644
--- a/app/views/projects/team_members/index.html.haml
+++ b/app/views/projects/team_members/index.html.haml
@@ -3,9 +3,9 @@
- if can? current_user, :admin_team_member, @project
%span.pull-right
- = link_to new_project_team_member_path(@project), class: "btn btn-new btn-grouped", title: "New project member" do
+ = link_to new_namespace_project_team_member_path(@project.namespace, @project), class: "btn btn-new btn-grouped", title: "New project member" do
New project member
- = link_to import_project_team_members_path(@project), class: "btn btn-grouped", title: "Import members from another project" do
+ = link_to import_namespace_project_team_members_path(@project.namespace, @project), class: "btn btn-grouped", title: "Import members from another project" do
Import members
%p.light
diff --git a/app/views/projects/transfer.js.haml b/app/views/projects/transfer.js.haml
index 10b0de98c04..17b9fecfeb1 100644
--- a/app/views/projects/transfer.js.haml
+++ b/app/views/projects/transfer.js.haml
@@ -1,7 +1,2 @@
-- if @project.errors[:namespace_id].present?
- :plain
- $("#tab-transfer .errors-holder").replaceWith(errorMessage('#{escape_javascript(@project.errors[:namespace_id].first)}'));
- $("#tab-transfer .form-actions input").removeAttr('disabled').removeClass('disabled');
-- else
- :plain
- location.href = "#{edit_project_path(@project)}";
+:plain
+ location.href = "#{edit_namespace_project_path(@project.namespace, @project)}";
diff --git a/app/views/projects/tree/_blob_item.html.haml b/app/views/projects/tree/_blob_item.html.haml
index 393ef0e24bd..b253fe896e3 100644
--- a/app/views/projects/tree/_blob_item.html.haml
+++ b/app/views/projects/tree/_blob_item.html.haml
@@ -2,7 +2,7 @@
%td.tree-item-file-name
= tree_icon(type)
%span.str-truncated
- = link_to blob_item.name, project_blob_path(@project, tree_join(@id || @commit.id, blob_item.name))
+ = link_to blob_item.name, namespace_project_blob_path(@project.namespace, @project, tree_join(@id || @commit.id, blob_item.name))
%td.tree_time_ago.cgray
= render 'spinner'
%td.hidden-xs.tree_commit
diff --git a/app/views/projects/tree/_submodule_item.html.haml b/app/views/projects/tree/_submodule_item.html.haml
index 46e9be4af83..20c70cac699 100644
--- a/app/views/projects/tree/_submodule_item.html.haml
+++ b/app/views/projects/tree/_submodule_item.html.haml
@@ -1,14 +1,6 @@
-- tree, commit = submodule_links(submodule_item)
%tr{ class: "tree-item" }
%td.tree-item-file-name
%i.fa.fa-archive
- %span
- = link_to truncate(submodule_item.name, length: 40), tree
- @
- %span.monospace
- - if commit.nil?
- #{truncate_sha(submodule_item.id)}
- - else
- = link_to "#{truncate_sha(submodule_item.id)}", commit
+ = submodule_link(submodule_item, @ref)
%td
%td.hidden-xs
diff --git a/app/views/projects/tree/_tree.html.haml b/app/views/projects/tree/_tree.html.haml
index 1159fcadffd..d304690d162 100644
--- a/app/views/projects/tree/_tree.html.haml
+++ b/app/views/projects/tree/_tree.html.haml
@@ -1,16 +1,16 @@
%ul.breadcrumb.repo-breadcrumb
%li
- = link_to project_tree_path(@project, @ref) do
+ = link_to namespace_project_tree_path(@project.namespace, @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)
+ = link_to truncate(title, length: 40), namespace_project_tree_path(@project.namespace, @project, path)
- else
= link_to title, '#'
- if current_user && can_push_branch?(@project, @ref)
%li
- = link_to project_new_tree_path(@project, @id), title: 'New file', id: 'new-file-link' do
+ = link_to namespace_project_new_blob_path(@project.namespace, @project, @id), title: 'New file', id: 'new-file-link' do
%small
%i.fa.fa-plus
@@ -27,15 +27,15 @@
%i.fa.fa-angle-right
&nbsp;
%small.light
- = link_to @commit.short_id, project_commit_path(@project, @commit)
+ = link_to @commit.short_id, namespace_project_commit_path(@project.namespace, @project, @commit)
&ndash;
= truncate(@commit.title, length: 50)
- = link_to 'History', project_commits_path(@project, @id), class: 'pull-right'
+ = link_to 'History', namespace_project_commits_path(@project.namespace, @project, @id), class: 'pull-right'
- if @path.present?
%tr.tree-item
%td.tree-item-file-name
- = link_to "..", project_tree_path(@project, up_dir_path(tree)), class: 'prepend-left-10'
+ = link_to "..", namespace_project_tree_path(@project.namespace, @project, up_dir_path), class: 'prepend-left-10'
%td
%td.hidden-xs
diff --git a/app/views/projects/tree/_tree_commit_column.html.haml b/app/views/projects/tree/_tree_commit_column.html.haml
index bd50dd4d9a2..50521264a61 100644
--- a/app/views/projects/tree/_tree_commit_column.html.haml
+++ b/app/views/projects/tree/_tree_commit_column.html.haml
@@ -1,3 +1,3 @@
%span.str-truncated
%span.tree_author= commit_author_link(commit, avatar: true, size: 16)
- = link_to_gfm commit.title, project_commit_path(@project, commit.id), class: "tree-commit-link"
+ = link_to_gfm commit.title, namespace_project_commit_path(@project.namespace, @project, commit.id), class: "tree-commit-link"
diff --git a/app/views/projects/tree/_tree_item.html.haml b/app/views/projects/tree/_tree_item.html.haml
index f8cecf9be1f..94342bc9b2b 100644
--- a/app/views/projects/tree/_tree_item.html.haml
+++ b/app/views/projects/tree/_tree_item.html.haml
@@ -2,7 +2,8 @@
%td.tree-item-file-name
= tree_icon(type)
%span.str-truncated
- = link_to tree_item.name, project_tree_path(@project, tree_join(@id || @commit.id, tree_item.name))
+ - path = flatten_tree(tree_item)
+ = link_to path, namespace_project_tree_path(@project.namespace, @project, tree_join(@id || @commit.id, path))
%td.tree_time_ago.cgray
= render 'spinner'
%td.hidden-xs.tree_commit
diff --git a/app/views/projects/update.js.haml b/app/views/projects/update.js.haml
index cbb21f2b9fb..4f3f4cab8d5 100644
--- a/app/views/projects/update.js.haml
+++ b/app/views/projects/update.js.haml
@@ -1,6 +1,6 @@
- if @project.valid?
:plain
- location.href = "#{edit_project_path(@project)}";
+ location.href = "#{edit_namespace_project_path(@project.namespace, @project)}";
- else
:plain
$(".project-edit-errors").html("#{escape_javascript(render('errors'))}");
diff --git a/app/views/projects/wikis/_form.html.haml b/app/views/projects/wikis/_form.html.haml
index f37c086716d..9fbfa0b1aeb 100644
--- a/app/views/projects/wikis/_form.html.haml
+++ b/app/views/projects/wikis/_form.html.haml
@@ -1,4 +1,4 @@
-= form_for [@project, @page], method: @page.persisted? ? :put : :post, html: { class: 'form-horizontal wiki-form gfm-form' } do |f|
+= form_for [@project.namespace.becomes(Namespace), @project, @page], method: @page.persisted? ? :put : :post, html: { class: 'form-horizontal wiki-form gfm-form' } do |f|
-if @page.errors.any?
#error_explanation
.alert.alert-danger
@@ -19,13 +19,15 @@
%code [Link Title](page-slug)
\.
- .form-group
+ .form-group.wiki-content
= f.label :content, class: 'control-label'
.col-sm-10
- = render 'projects/zen', f: f, attr: :content, classes: 'description form-control'
- .col-sm-12.hint
- .pull-left Wiki content is parsed with #{link_to "GitLab Flavored Markdown", help_page_path("markdown", "markdown"), target: '_blank'}
- .pull-right Attach images (JPG, PNG, GIF) by dragging & dropping or #{link_to "selecting them", '#', class: 'markdown-selector' }.
+ = render layout: 'projects/md_preview', locals: { preview_class: "wiki" } do
+ = render 'projects/zen', f: f, attr: :content, classes: 'description form-control'
+ .col-sm-12.hint
+ .pull-left Wiki content is parsed with #{link_to "GitLab Flavored Markdown", help_page_path("markdown", "markdown"), target: '_blank'}
+ .pull-right Attach files by dragging &amp; dropping or #{link_to "selecting them", '#', class: 'markdown-selector' }.
+
.clearfix
.error-alert
.form-group
@@ -35,11 +37,10 @@
.form-actions
- if @page && @page.persisted?
= f.submit 'Save changes', class: "btn-save btn"
- = link_to "Cancel", project_wiki_path(@project, @page), class: "btn btn-cancel"
+ = link_to "Cancel", namespace_project_wiki_path(@project.namespace, @project, @page), class: "btn btn-cancel"
- else
= f.submit 'Create page', class: "btn-create btn"
- = link_to "Cancel", project_wiki_path(@project, :home), class: "btn btn-cancel"
+ = link_to "Cancel", namespace_project_wiki_path(@project.namespace, @project, :home), class: "btn btn-cancel"
:javascript
- window.project_image_path_upload = "#{upload_image_project_path @project}";
-
+ window.project_uploads_path = "#{namespace_project_uploads_path @project.namespace, @project}";
diff --git a/app/views/projects/wikis/_main_links.html.haml b/app/views/projects/wikis/_main_links.html.haml
index 30410bc95e0..633214a4e86 100644
--- a/app/views/projects/wikis/_main_links.html.haml
+++ b/app/views/projects/wikis/_main_links.html.haml
@@ -1,8 +1,8 @@
%span.pull-right
- if (@page && @page.persisted?)
- = link_to history_project_wiki_path(@project, @page), class: "btn btn-grouped" do
+ = link_to history_namespace_project_wiki_path(@project.namespace, @project, @page), class: "btn btn-grouped" do
Page History
- if can?(current_user, :write_wiki, @project)
- = link_to edit_project_wiki_path(@project, @page), class: "btn btn-grouped" do
+ = link_to edit_namespace_project_wiki_path(@project.namespace, @project, @page), class: "btn btn-grouped" do
%i.fa.fa-pencil-square-o
Edit
diff --git a/app/views/projects/wikis/_nav.html.haml b/app/views/projects/wikis/_nav.html.haml
index 90539fde583..693c3facb32 100644
--- a/app/views/projects/wikis/_nav.html.haml
+++ b/app/views/projects/wikis/_nav.html.haml
@@ -1,12 +1,12 @@
%ul.nav.nav-tabs
= nav_link(html_options: {class: params[:id] == 'home' ? 'active' : '' }) do
- = link_to 'Home', project_wiki_path(@project, :home)
+ = link_to 'Home', namespace_project_wiki_path(@project.namespace, @project, :home)
= nav_link(path: 'wikis#pages') do
- = link_to 'Pages', pages_project_wikis_path(@project)
+ = link_to 'Pages', pages_namespace_project_wikis_path(@project.namespace, @project)
= nav_link(path: 'wikis#git_access') do
- = link_to git_access_project_wikis_path(@project) do
+ = link_to git_access_namespace_project_wikis_path(@project.namespace, @project) do
%i.fa.fa-download
Git Access
diff --git a/app/views/projects/wikis/_new.html.haml b/app/views/projects/wikis/_new.html.haml
index 1ce292a02df..6834969de8b 100644
--- a/app/views/projects/wikis/_new.html.haml
+++ b/app/views/projects/wikis/_new.html.haml
@@ -7,7 +7,7 @@
.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)
+ = text_field_tag :new_wiki_path, nil, placeholder: 'how-to-setup', class: 'form-control', required: true, :'data-wikis-path' => namespace_project_wikis_path(@project.namespace, @project)
%p.hint
Please don't use spaces.
.modal-footer
diff --git a/app/views/projects/wikis/edit.html.haml b/app/views/projects/wikis/edit.html.haml
index 5347caf000a..5567f1af22a 100644
--- a/app/views/projects/wikis/edit.html.haml
+++ b/app/views/projects/wikis/edit.html.haml
@@ -9,5 +9,5 @@
.pull-right
- if @page.persisted? && can?(current_user, :admin_wiki, @project)
- = link_to project_wiki_path(@project, @page), data: { confirm: "Are you sure you want to delete this page?"}, method: :delete, class: "btn btn-small btn-remove" do
+ = link_to namespace_project_wiki_path(@project.namespace, @project, @page), 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/history.html.haml b/app/views/projects/wikis/history.html.haml
index ef4b8f74714..91291f753f7 100644
--- a/app/views/projects/wikis/history.html.haml
+++ b/app/views/projects/wikis/history.html.haml
@@ -1,7 +1,7 @@
= render 'nav'
%h3.page-title
%span.light History for
- = link_to @page.title, project_wiki_path(@project, @page)
+ = link_to @page.title, namespace_project_wiki_path(@project.namespace, @project, @page)
%table.table
%thead
@@ -12,18 +12,19 @@
%th Last updated
%th Format
%tbody
- - @page.versions.each do |version|
+ - @page.versions.each_with_index do |version, index|
- commit = version
%tr
%td
- = link_to project_wiki_path(@project, @page, version_id: commit.id) do
+ = link_to project_wiki_path_with_version(@project, @page,
+ commit.id, index == 0) do
= truncate_sha(commit.id)
%td
= commit.author.name
%td
= commit.message
%td
- #{time_ago_with_tooltip(version.date)}
+ #{time_ago_with_tooltip(version.authored_date)}
%td
%strong
= @page.page.wiki.page(@page.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 264b48ec36c..ee233d9086f 100644
--- a/app/views/projects/wikis/pages.html.haml
+++ b/app/views/projects/wikis/pages.html.haml
@@ -5,7 +5,7 @@
- @wiki_pages.each do |wiki_page|
%li
%h4
- = link_to wiki_page.title, project_wiki_path(@project, wiki_page)
+ = link_to wiki_page.title, namespace_project_wiki_path(@project.namespace, @project, wiki_page)
%small (#{wiki_page.format})
.pull-right
%small Last edited #{time_ago_with_tooltip(wiki_page.commit.authored_date)}
diff --git a/app/views/projects/wikis/show.html.haml b/app/views/projects/wikis/show.html.haml
index ede4fef9e24..a6263e93f67 100644
--- a/app/views/projects/wikis/show.html.haml
+++ b/app/views/projects/wikis/show.html.haml
@@ -5,7 +5,7 @@
- if @page.historical?
.warning_message
This is an old version of this page.
- You can view the #{link_to "most recent version", project_wiki_path(@project, @page)} or browse the #{link_to "history", history_project_wiki_path(@project, @page)}.
+ You can view the #{link_to "most recent version", namespace_project_wiki_path(@project.namespace, @project, @page)} or browse the #{link_to "history", history_namespace_project_wiki_path(@project.namespace, @project, @page)}.
%hr
diff --git a/app/views/search/_project_filter.html.haml b/app/views/search/_project_filter.html.haml
index c201b3d6c47..ad933502a28 100644
--- a/app/views/search/_project_filter.html.haml
+++ b/app/views/search/_project_filter.html.haml
@@ -25,6 +25,7 @@
= @search_results.notes_count
%li{class: ("active" if @scope == 'wiki_blobs')}
= link_to search_filter_path(scope: 'wiki_blobs') do
+ %i.fa.fa-book
Wiki
.pull-right
= @search_results.wiki_blobs_count
diff --git a/app/views/search/_results.html.haml b/app/views/search/_results.html.haml
index 58bcff9dbe3..796dd752a4c 100644
--- a/app/views/search/_results.html.haml
+++ b/app/views/search/_results.html.haml
@@ -2,7 +2,7 @@
#{@search_results.total_count} results found
- unless @show_snippets
- if @project
- for #{link_to @project.name_with_namespace, @project}
+ for #{link_to @project.name_with_namespace, [@project.namespace.becomes(Namespace), @project]}
- elsif @group
for #{link_to @group.name, @group}
diff --git a/app/views/search/results/_blob.html.haml b/app/views/search/results/_blob.html.haml
index b46b4832e19..84e9be82c44 100644
--- a/app/views/search/results/_blob.html.haml
+++ b/app/views/search/results/_blob.html.haml
@@ -1,9 +1,9 @@
.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
+ = link_to namespace_project_blob_path(@project.namespace, @project, tree_join(blob.ref, blob.filename), :anchor => "L" + blob.startline.to_s) do
%i.fa.fa-file
%strong
= blob.filename
.file-content.code.term
- = render 'shared/file_hljs', blob: blob, first_line_number: blob.startline
+ = render 'shared/file_highlight', blob: blob, first_line_number: blob.startline, user_color_scheme_class: 'white'
diff --git a/app/views/search/results/_issue.html.haml b/app/views/search/results/_issue.html.haml
index 7868f958261..ce8ddff9556 100644
--- a/app/views/search/results/_issue.html.haml
+++ b/app/views/search/results/_issue.html.haml
@@ -1,6 +1,6 @@
.search-result-row
%h4
- = link_to [issue.project, issue] do
+ = link_to [issue.project.namespace.becomes(Namespace), issue.project, issue] do
%span.term.str-truncated= issue.title
.pull-right ##{issue.iid}
- if issue.description.present?
diff --git a/app/views/search/results/_merge_request.html.haml b/app/views/search/results/_merge_request.html.haml
index 56b185283bd..2efa616d664 100644
--- a/app/views/search/results/_merge_request.html.haml
+++ b/app/views/search/results/_merge_request.html.haml
@@ -1,6 +1,6 @@
.search-result-row
%h4
- = link_to [merge_request.target_project, merge_request] do
+ = link_to [merge_request.target_project.namespace.becomes(Namespace), merge_request.target_project, merge_request] do
%span.term.str-truncated= merge_request.title
.pull-right ##{merge_request.iid}
- if merge_request.description.present?
diff --git a/app/views/search/results/_note.html.haml b/app/views/search/results/_note.html.haml
index a44a4542df5..5fcba2b7e93 100644
--- a/app/views/search/results/_note.html.haml
+++ b/app/views/search/results/_note.html.haml
@@ -9,7 +9,7 @@
= link_to project do
= project.name_with_namespace
&middot;
- = link_to project_commit_path(project, note.commit_id, anchor: dom_id(note)) do
+ = link_to namespace_project_commit_path(project.namespace, project, note.commit_id, anchor: dom_id(note)) do
Commit #{truncate_sha(note.commit_id)}
- else
= link_to project do
@@ -17,7 +17,7 @@
&middot;
%span #{note.noteable_type.titleize} ##{note.noteable.iid}
&middot;
- = link_to [project, note.noteable, anchor: dom_id(note)] do
+ = link_to [project.namespace.becomes(Namespace), project, note.noteable, anchor: dom_id(note)] do
= note.noteable.title
.note-search-result
diff --git a/app/views/search/results/_project.html.haml b/app/views/search/results/_project.html.haml
index 301b65eca29..195cf06c8ea 100644
--- a/app/views/search/results/_project.html.haml
+++ b/app/views/search/results/_project.html.haml
@@ -1,6 +1,6 @@
.search-result-row
%h4
- = link_to project do
+ = link_to [project.namespace.becomes(Namespace), project] do
%span.term= project.name_with_namespace
- if project.description.present?
%span.light.term= project.description
diff --git a/app/views/search/results/_snippet_title.html.haml b/app/views/search/results/_snippet_title.html.haml
index f7e5ee5e20e..c414acb6a11 100644
--- a/app/views/search/results/_snippet_title.html.haml
+++ b/app/views/search/results/_snippet_title.html.haml
@@ -11,7 +11,7 @@
%small.pull-right.cgray
- if snippet_title.project_id?
- = link_to snippet_title.project.name_with_namespace, project_path(snippet_title.project)
+ = link_to snippet_title.project.name_with_namespace, namespace_project_path(snippet_title.project.namespace, snippet_title.project)
.snippet-info
= "##{snippet_title.id}"
diff --git a/app/views/search/results/_wiki_blob.html.haml b/app/views/search/results/_wiki_blob.html.haml
index e361074b6a0..f9c5810e3d0 100644
--- a/app/views/search/results/_wiki_blob.html.haml
+++ b/app/views/search/results/_wiki_blob.html.haml
@@ -1,9 +1,9 @@
.blob-result
.file-holder
.file-title
- = link_to project_wiki_path(@project, wiki_blob.filename) do
+ = link_to namespace_project_wiki_path(@project.namespace, @project, wiki_blob.filename) do
%i.fa.fa-file
%strong
= wiki_blob.filename
.file-content.code.term
- = render 'shared/file_hljs', blob: wiki_blob, first_line_number: wiki_blob.startline
+ = render 'shared/file_highlight', blob: wiki_blob, first_line_number: wiki_blob.startline, user_color_scheme_class: 'white'
diff --git a/app/views/shared/_choose_group_avatar_button.html.haml b/app/views/shared/_choose_group_avatar_button.html.haml
index f32c2d388a7..299c0bd42a2 100644
--- a/app/views/shared/_choose_group_avatar_button.html.haml
+++ b/app/views/shared/_choose_group_avatar_button.html.haml
@@ -4,4 +4,4 @@
&nbsp;
%span.file_name.js-avatar-filename File name...
= f.file_field :avatar, class: 'js-group-avatar-input hidden'
-.light The maximum file size allowed is 100KB.
+.light The maximum file size allowed is 200KB.
diff --git a/app/views/shared/_clone_panel.html.haml b/app/views/shared/_clone_panel.html.haml
index 1cc6043f56b..a1121750ca3 100644
--- a/app/views/shared/_clone_panel.html.haml
+++ b/app/views/shared/_clone_panel.html.haml
@@ -1,8 +1,20 @@
- project = project || @project
.git-clone-holder.input-group
.input-group-btn
- %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
+ %button{ |
+ class: "btn #{ 'active' if default_clone_protocol == 'ssh' }#{ ' has_tooltip' if current_user && current_user.require_ssh_key? }", |
+ :"data-clone" => project.ssh_url_to_repo, |
+ :"data-title" => "Add an SSH key to your profile<br> to pull or push via SSH",
+ :"data-html" => "true",
+ :"data-container" => "body"}
+ SSH
+ %button{ |
+ class: "btn #{ 'active' if default_clone_protocol == 'http' }#{ ' has_tooltip' if current_user && current_user.require_password? }", |
+ :"data-clone" => project.http_url_to_repo, |
+ :"data-title" => "Set a password on your account<br> to pull or push via #{gitlab_config.protocol.upcase}",
+ :"data-html" => "true",
+ :"data-container" => "body"}
+ = gitlab_config.protocol.upcase
= text_field_tag :project_clone, default_url_to_repo(project), class: "one_click_select form-control", readonly: true
- if project.kind_of?(Project)
.input-group-addon
diff --git a/app/views/shared/_event_filter.html.haml b/app/views/shared/_event_filter.html.haml
index ee0b57fbe5a..d07a9e2b924 100644
--- a/app/views/shared/_event_filter.html.haml
+++ b/app/views/shared/_event_filter.html.haml
@@ -1,5 +1,19 @@
-.event_filter
+%ul.nav.nav-pills.event_filter
= event_filter_link EventFilter.push, 'Push events'
= event_filter_link EventFilter.merged, 'Merge events'
= event_filter_link EventFilter.comments, 'Comments'
= event_filter_link EventFilter.team, 'Team'
+
+ - if current_user
+ - if current_controller?(:dashboard)
+ %li.pull-right
+ = link_to dashboard_path(:atom, { private_token: current_user.private_token }), class: 'rss-btn' do
+ %i.fa.fa-rss
+ News Feed
+
+ - if current_controller?(:groups)
+ %li.pull-right
+ = link_to group_path(@group, { format: :atom, private_token: current_user.private_token }), title: "Feed", class: 'rss-btn' do
+ %i.fa.fa-rss
+ News Feed
+%hr
diff --git a/app/views/shared/_file_hljs.html.haml b/app/views/shared/_file_highlight.html.haml
index 444c948b026..fba69dd0f3f 100644
--- a/app/views/shared/_file_hljs.html.haml
+++ b/app/views/shared/_file_highlight.html.haml
@@ -1,4 +1,4 @@
-%div.highlighted-data{class: user_color_scheme_class}
+.file-content.code{class: user_color_scheme_class}
.line-numbers
- if blob.data.present?
- blob.data.lines.to_a.size.times do |index|
@@ -7,7 +7,5 @@
= link_to "#L#{i}", id: "L#{i}", rel: "#L#{i}" do
%i.fa.fa-link
= i
- .highlight
- %pre
- %code{ class: highlightjs_class(blob.name) }
- #{blob.data}
+ :preserve
+ #{highlight(blob.name, blob.data)}
diff --git a/app/views/shared/_filter.html.haml b/app/views/shared/_filter.html.haml
deleted file mode 100644
index d366dd97a71..00000000000
--- a/app/views/shared/_filter.html.haml
+++ /dev/null
@@ -1,50 +0,0 @@
-.side-filters
- = form_tag filter_path(entity), method: 'get' do
- - if current_user
- %fieldset.scope-filter
- %ul.nav.nav-pills.nav-stacked
- %li{class: ("active" if params[:scope] == 'assigned-to-me')}
- = link_to filter_path(entity, scope: 'assigned-to-me') do
- Assigned to me
- %span.pull-right
- = assigned_entities_count(current_user, entity, @group)
- %li{class: ("active" if params[:scope] == 'authored')}
- = link_to filter_path(entity, scope: 'authored') do
- Created by me
- %span.pull-right
- = authored_entities_count(current_user, entity, @group)
- %li{class: ("active" if params[:scope] == 'all')}
- = link_to filter_path(entity, scope: 'all') do
- Everyone's
- %span.pull-right
- = authorized_entities_count(current_user, entity, @group)
-
- %fieldset.status-filter
- %legend State
- %ul.nav.nav-pills
- %li{class: ("active" if params[:state] == 'opened')}
- = link_to filter_path(entity, state: 'opened') do
- Open
- %li{class: ("active" if params[:state] == 'closed')}
- = link_to filter_path(entity, state: 'closed') do
- Closed
- %li{class: ("active" if params[:state] == 'all')}
- = link_to filter_path(entity, state: 'all') do
- All
-
- %fieldset
- %legend Projects
- %ul.nav.nav-pills.nav-stacked.nav-small
- - @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[:state].present? || params[:project_id].present?
- = link_to filter_path(entity, state: nil, project_id: nil), class: 'pull-right cgray' do
- %i.fa.fa-times
- %strong Clear filter
-
diff --git a/app/views/shared/_group_form.html.haml b/app/views/shared/_group_form.html.haml
index 93294e42505..5875f71bac2 100644
--- a/app/views/shared/_group_form.html.haml
+++ b/app/views/shared/_group_form.html.haml
@@ -1,9 +1,26 @@
+- if @group.persisted?
+ .form-group
+ = f.label :name, class: 'control-label' do
+ Group name
+ .col-sm-10
+ = f.text_field :name, placeholder: 'open-source', class: 'form-control'
+
.form-group
- = f.label :name, class: 'control-label' do
- Group name
+ = f.label :path, class: 'control-label' do
+ Group path
.col-sm-10
- = f.text_field :name, placeholder: 'Example Group', class: 'form-control',
- autofocus: local_assigns[:autofocus] || false
+ .input-group
+ .input-group-addon
+ = root_url
+ = f.text_field :path, placeholder: 'open-source', class: 'form-control',
+ autofocus: local_assigns[:autofocus] || false
+ - if @group.persisted?
+ .bs-callout.bs-callout-danger
+ %ul
+ %li Changing group path can have unintended side effects.
+ %li Renaming group path will rename directory for all related projects
+ %li It will change web url for access group and group projects.
+ %li It will change the git path to repositories under this group.
.form-group.group-description-holder
= f.label :description, 'Details', class: 'control-label'
diff --git a/app/views/shared/_issuable_filter.html.haml b/app/views/shared/_issuable_filter.html.haml
new file mode 100644
index 00000000000..61647b09527
--- /dev/null
+++ b/app/views/shared/_issuable_filter.html.haml
@@ -0,0 +1,119 @@
+.issues-filters
+ .issues-state-filters
+ %ul.nav.nav-tabs
+ %li{class: ("active" if params[:state] == 'opened')}
+ = link_to page_filter_path(state: 'opened') do
+ %i.fa.fa-exclamation-circle
+ Open
+ %li{class: ("active" if params[:state] == 'closed')}
+ = link_to page_filter_path(state: 'closed') do
+ %i.fa.fa-check-circle
+ Closed
+ %li{class: ("active" if params[:state] == 'all')}
+ = link_to page_filter_path(state: 'all') do
+ %i.fa.fa-compass
+ All
+
+ %div
+ - if controller.controller_name == 'issues'
+ .check-all-holder
+ = check_box_tag "check_all_issues", nil, false,
+ class: "check_all_issues left",
+ disabled: !can?(current_user, :modify_issue, @project)
+ .issues-other-filters
+ .dropdown.inline.assignee-filter
+ %a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"}
+ %i.fa.fa-user
+ %span.light assignee:
+ - if @assignee.present?
+ %strong= @assignee.name
+ - elsif params[:assignee_id] == "0"
+ Unassigned
+ - else
+ Any
+ %b.caret
+ %ul.dropdown-menu
+ %li
+ = link_to page_filter_path(assignee_id: nil) do
+ Any
+ = link_to page_filter_path(assignee_id: 0) do
+ Unassigned
+ - @assignees.sort_by(&:name).each do |user|
+ %li
+ = link_to page_filter_path(assignee_id: user.id) do
+ = image_tag avatar_icon(user.email), class: "avatar s16", alt: ''
+ = user.name
+
+ .dropdown.inline.prepend-left-10.author-filter
+ %a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"}
+ %i.fa.fa-user
+ %span.light author:
+ - if @author.present?
+ %strong= @author.name
+ - elsif params[:author_id] == "0"
+ Unassigned
+ - else
+ Any
+ %b.caret
+ %ul.dropdown-menu
+ %li
+ = link_to page_filter_path(author_id: nil) do
+ Any
+ = link_to page_filter_path(author_id: 0) do
+ Unassigned
+ - @authors.sort_by(&:name).each do |user|
+ %li
+ = link_to page_filter_path(author_id: user.id) do
+ = image_tag avatar_icon(user.email), class: "avatar s16", alt: ''
+ = user.name
+
+ .dropdown.inline.prepend-left-10.milestone-filter
+ %a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"}
+ %i.fa.fa-clock-o
+ %span.light milestone:
+ - if @milestone.present?
+ %strong= @milestone.title
+ - elsif params[:milestone_id] == "0"
+ None (backlog)
+ - else
+ Any
+ %b.caret
+ %ul.dropdown-menu
+ %li
+ = link_to page_filter_path(milestone_id: nil) do
+ Any
+ = link_to page_filter_path(milestone_id: 0) do
+ None (backlog)
+ - @milestones.each do |milestone|
+ %li
+ = link_to page_filter_path(milestone_id: milestone.id) do
+ %strong= milestone.title
+ %small.light= milestone.expires_at
+
+ - if @project
+ .dropdown.inline.prepend-left-10.labels-filter
+ %a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"}
+ %i.fa.fa-tags
+ %span.light label:
+ - if params[:label_name].present?
+ %strong= params[:label_name]
+ - else
+ Any
+ %b.caret
+ %ul.dropdown-menu
+ %li
+ = link_to page_filter_path(label_name: nil) do
+ Any
+ - if @project.labels.any?
+ - @project.labels.each do |label|
+ %li
+ = link_to page_filter_path(label_name: label.name) do
+ = render_colored_label(label)
+ - else
+ %li
+ = link_to generate_namespace_project_labels_path(@project.namespace, @project, redirect: request.original_url), method: :post do
+ %i.fa.fa-plus-circle
+ Create default labels
+
+ .pull-right
+ = render 'shared/sort_dropdown'
diff --git a/app/views/shared/_issues.html.haml b/app/views/shared/_issues.html.haml
index e976f897dc9..0dbb6a04393 100644
--- a/app/views/shared/_issues.html.haml
+++ b/app/views/shared/_issues.html.haml
@@ -4,7 +4,7 @@
- project = group[0]
.panel-heading
= link_to_project project
- = link_to 'show all', project_issues_path(project), class: 'pull-right'
+ = link_to 'show all', namespace_project_issues_path(project.namespace, project), class: 'pull-right'
%ul.well-list.issues-list
- group[1].each do |issue|
diff --git a/app/views/shared/_merge_requests.html.haml b/app/views/shared/_merge_requests.html.haml
index 39a1ee38f8e..c02c5af008a 100644
--- a/app/views/shared/_merge_requests.html.haml
+++ b/app/views/shared/_merge_requests.html.haml
@@ -4,7 +4,7 @@
- project = group[0]
.panel-heading
= link_to_project project
- = link_to 'show all', project_merge_requests_path(project), class: 'pull-right'
+ = link_to 'show all', namespace_project_merge_requests_path(project.namespace, 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/_milestones_filter.html.haml b/app/views/shared/_milestones_filter.html.haml
new file mode 100644
index 00000000000..f685ae7726c
--- /dev/null
+++ b/app/views/shared/_milestones_filter.html.haml
@@ -0,0 +1,14 @@
+.milestones-filters.append-bottom-10
+ %ul.nav.nav-tabs
+ %li{class: ("active" if params[:state].blank? || params[:state] == 'opened')}
+ = link_to milestones_filter_path(state: 'opened') do
+ %i.fa.fa-exclamation-circle
+ Open
+ %li{class: ("active" if params[:state] == 'closed')}
+ = link_to milestones_filter_path(state: 'closed') do
+ %i.fa.fa-check-circle
+ Closed
+ %li{class: ("active" if params[:state] == 'all')}
+ = link_to milestones_filter_path(state: 'all') do
+ %i.fa.fa-compass
+ All
diff --git a/app/views/shared/_no_password.html.haml b/app/views/shared/_no_password.html.haml
new file mode 100644
index 00000000000..a43bf33751a
--- /dev/null
+++ b/app/views/shared/_no_password.html.haml
@@ -0,0 +1,8 @@
+- if cookies[:hide_no_password_message].blank? && !current_user.hide_no_password && current_user.require_password?
+ .no-password-message.alert.alert-warning.hidden-xs
+ You won't be able to pull or push project code via #{gitlab_config.protocol.upcase} until you #{link_to 'set a password', edit_profile_password_path} on your account
+
+ .pull-right
+ = link_to "Don't show again", profile_path(user: {hide_no_password: true}), method: :put
+ |
+ = link_to 'Remind later', '#', class: 'hide-no-password-message'
diff --git a/app/views/shared/_no_ssh.html.haml b/app/views/shared/_no_ssh.html.haml
index e70eb4d01b9..1a2946baccb 100644
--- a/app/views/shared/_no_ssh.html.haml
+++ b/app/views/shared/_no_ssh.html.haml
@@ -1,14 +1,8 @@
-- 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
- .pull-right.hidden-xs
- = 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'
- .links-xs.visible-xs
- = link_to "Add key", new_profile_key_path
- |
- = 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 'Later', '#', class: 'hide-no-ssh-message'
+- if cookies[:hide_no_ssh_message].blank? && !current_user.hide_no_ssh_key && current_user.require_ssh_key?
+ .no-ssh-key-message.alert.alert-warning.hidden-xs
+ 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
+
+ .pull-right
+ = link_to "Don't show again", profile_path(user: {hide_no_ssh_key: true}), method: :put
+ |
+ = link_to 'Remind later', '#', class: 'hide-no-ssh-message'
diff --git a/app/views/shared/_outdated_browser.html.haml b/app/views/shared/_outdated_browser.html.haml
new file mode 100644
index 00000000000..0eba1fe075f
--- /dev/null
+++ b/app/views/shared/_outdated_browser.html.haml
@@ -0,0 +1,8 @@
+- if outdated_browser?
+ - link = "https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/install/requirements.md#supported-web-browsers"
+ .browser-alert
+ GitLab may not work properly because you are using an outdated web browser.
+ %br
+ Please install a
+ = link_to 'supported web browser', link
+ for a better experience.
diff --git a/app/views/shared/_project_filter.html.haml b/app/views/shared/_project_filter.html.haml
deleted file mode 100644
index ea6a49e1501..00000000000
--- a/app/views/shared/_project_filter.html.haml
+++ /dev/null
@@ -1,64 +0,0 @@
-.side-filters
- = form_tag project_entities_path, method: 'get' do
- - if current_user
- %fieldset
- %ul.nav.nav-pills.nav-stacked
- %li{class: ("active" if params[:scope] == 'all')}
- = link_to project_filter_path(scope: 'all') do
- Everyone's
- %span.pull-right
- = authorized_entities_count(current_user, entity, @project)
- %li{class: ("active" if params[:scope] == 'assigned-to-me')}
- = link_to project_filter_path(scope: 'assigned-to-me') do
- Assigned to me
- %span.pull-right
- = assigned_entities_count(current_user, entity, @project)
- %li{class: ("active" if params[:scope] == 'created-by-me')}
- = link_to project_filter_path(scope: 'created-by-me') do
- Created by me
- %span.pull-right
- = authored_entities_count(current_user, entity, @project)
-
- %fieldset
- %legend State
- %ul.nav.nav-pills
- %li{class: ("active" if params[:state] == 'opened')}
- = link_to project_filter_path(state: 'opened') 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
-
- - if defined?(labels)
- %fieldset
- %legend
- Labels
- %small.pull-right
- = link_to project_labels_path(@project), class: 'light' do
- %i.fa.fa-pencil-square-o
- %ul.nav.nav-pills.nav-stacked.nav-small.labels-filter
- - @project.labels.order_by_name.each do |label|
- %li{class: label_filter_class(label.name)}
- = link_to labels_filter_path(label.name) do
- = render_colored_label(label)
- - if selected_label?(label.name)
- .pull-right
- %i.fa.fa-times
-
- - if @project.labels.empty?
- .light-well
- Create first label at
- = link_to 'labels page', project_labels_path(@project)
- %br
- or #{link_to 'generate', generate_project_labels_path(@project, redirect: redirect), method: :post} default set of labels
-
- %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.fa.fa-times
- %strong Clear filter
-
-
diff --git a/app/views/shared/_promo.html.haml b/app/views/shared/_promo.html.haml
index 3400c345c4c..3596aabe309 100644
--- a/app/views/shared/_promo.html.haml
+++ b/app/views/shared/_promo.html.haml
@@ -1,5 +1,5 @@
.gitlab-promo
= link_to 'Homepage', promo_url
= link_to "Blog", promo_url + '/blog/'
- = link_to "@gitlabhq", "https://twitter.com/gitlabhq"
+ = link_to "@gitlab", "https://twitter.com/gitlab"
= link_to "Requests", "http://feedback.gitlab.com/"
diff --git a/app/views/shared/_ref_switcher.html.haml b/app/views/shared/_ref_switcher.html.haml
index 4d9534f49b1..eb2e1919e19 100644
--- a/app/views/shared/_ref_switcher.html.haml
+++ b/app/views/shared/_ref_switcher.html.haml
@@ -1,4 +1,4 @@
-= form_tag switch_project_refs_path(@project), method: :get, class: "project-refs-form" do
+= form_tag switch_namespace_project_refs_path(@project.namespace, @project), method: :get, class: "project-refs-form" do
= select_tag "ref", grouped_options_refs, class: "project-refs-select select2 select2-sm"
= hidden_field_tag :destination, destination
- if defined?(path)
diff --git a/app/views/shared/_sort_dropdown.html.haml b/app/views/shared/_sort_dropdown.html.haml
index 7b37b39780e..ba14c8643cd 100644
--- a/app/views/shared/_sort_dropdown.html.haml
+++ b/app/views/shared/_sort_dropdown.html.haml
@@ -2,21 +2,21 @@
%a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"}
%span.light sort:
- if @sort.present?
- = @sort
+ = sort_options_hash[@sort]
- else
- Newest
+ = sort_title_recently_created
%b.caret
- %ul.dropdown-menu
+ %ul.dropdown-menu.dropdown-menu-align-right
%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
+ = link_to page_filter_path(sort: sort_value_recently_created) do
+ = sort_title_recently_created
+ = link_to page_filter_path(sort: sort_value_oldest_created) do
+ = sort_title_oldest_created
+ = link_to page_filter_path(sort: sort_value_recently_updated) do
+ = sort_title_recently_updated
+ = link_to page_filter_path(sort: sort_value_oldest_updated) do
+ = sort_title_oldest_updated
+ = link_to page_filter_path(sort: sort_value_milestone_soon) do
+ = sort_title_milestone_soon
+ = link_to page_filter_path(sort: sort_value_milestone_later) do
+ = sort_title_milestone_later
diff --git a/app/views/shared/snippets/_blob.html.haml b/app/views/shared/snippets/_blob.html.haml
index 8cec6168ab8..30458793fd1 100644
--- a/app/views/shared/snippets/_blob.html.haml
+++ b/app/views/shared/snippets/_blob.html.haml
@@ -8,7 +8,7 @@
= render_markup(@snippet.file_name, @snippet.data)
- else
.file-content.code
- = render 'shared/file_hljs', blob: @snippet
+ = render 'shared/file_highlight', blob: @snippet
- else
.file-content.code
.nothing-here-block Empty file
diff --git a/app/views/shared/snippets/_form.html.haml b/app/views/shared/snippets/_form.html.haml
index f729f129e45..4e0663ea208 100644
--- a/app/views/shared/snippets/_form.html.haml
+++ b/app/views/shared/snippets/_form.html.haml
@@ -30,7 +30,7 @@
= f.submit 'Save', class: "btn-save btn"
- if @snippet.respond_to?(:project)
- = link_to "Cancel", project_snippets_path(@project), class: "btn btn-cancel"
+ = link_to "Cancel", namespace_project_snippets_path(@project.namespace, @project), class: "btn btn-cancel"
- else
= link_to "Cancel", snippets_path(@project), class: "btn btn-cancel"
diff --git a/app/views/snippets/_snippet.html.haml b/app/views/snippets/_snippet.html.haml
index c584dd8dfb6..5bb28664349 100644
--- a/app/views/snippets/_snippet.html.haml
+++ b/app/views/snippets/_snippet.html.haml
@@ -11,7 +11,7 @@
%small.pull-right.cgray
- if snippet.project_id?
- = link_to snippet.project.name_with_namespace, project_path(snippet.project)
+ = link_to snippet.project.name_with_namespace, namespace_project_path(snippet.project.namespace, snippet.project)
.snippet-info
= "##{snippet.id}"
diff --git a/app/views/users/_groups.html.haml b/app/views/users/_groups.html.haml
index ea008c2dede..cb84570a6d5 100644
--- a/app/views/users/_groups.html.haml
+++ b/app/views/users/_groups.html.haml
@@ -1,3 +1,4 @@
-- groups.each do |group|
- = link_to group, class: 'profile-groups-avatars', :title => group.name do
- - image_tag group_icon(group.path)
+.clearfix
+ - groups.each do |group|
+ = link_to group, class: 'profile-groups-avatars inline', title: group.name do
+ = image_tag group_icon(group.path), class: 'avatar group-avatar s40'
diff --git a/app/views/users/_projects.html.haml b/app/views/users/_projects.html.haml
index 1d38f8e8ab8..c925a48f550 100644
--- a/app/views/users/_projects.html.haml
+++ b/app/views/users/_projects.html.haml
@@ -1,6 +1,21 @@
-.panel.panel-default
- .panel-heading Personal projects
- %ul.well-list
- - projects.each do |project|
- %li
- = link_to_project project
+- if @contributed_projects.present?
+ .panel.panel-default
+ .panel-heading Projects contributed to
+ %ul.well-list
+ - @contributed_projects.sort_by(&:star_count).reverse.each do |project|
+ %li
+ = link_to_project project
+ %span.pull-right.light
+ %i.fa.fa-star
+ = project.star_count
+
+- if @projects.present?
+ .panel.panel-default
+ .panel-heading Personal projects
+ %ul.well-list
+ - @projects.sort_by(&:star_count).reverse.each do |project|
+ %li
+ = link_to_project project
+ %span.pull-right.light
+ %i.fa.fa-star
+ = project.star_count
diff --git a/app/views/users/calendar.html.haml b/app/views/users/calendar.html.haml
new file mode 100644
index 00000000000..1d1c974da24
--- /dev/null
+++ b/app/views/users/calendar.html.haml
@@ -0,0 +1,8 @@
+%h4 Commits calendar
+#cal-heatmap.calendar
+ :javascript
+ new calendar(
+ #{@timestamps.to_json},
+ #{@starting_year},
+ #{@starting_month}
+ );
diff --git a/app/views/users/show.atom.builder b/app/views/users/show.atom.builder
new file mode 100644
index 00000000000..8fe30b23635
--- /dev/null
+++ b/app/views/users/show.atom.builder
@@ -0,0 +1,12 @@
+xml.instruct!
+xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://search.yahoo.com/mrss/" do
+ xml.title "Activity feed for #{@user.name}"
+ xml.link href: user_url(@user, :atom), rel: "self", type: "application/atom+xml"
+ xml.link href: user_url(@user), rel: "alternate", type: "text/html"
+ xml.id projects_url
+ xml.updated @events.maximum(:updated_at).strftime("%Y-%m-%dT%H:%M:%SZ") if @events.any?
+
+ @events.each do |event|
+ event_to_atom(xml, event)
+ end
+end
diff --git a/app/views/users/show.html.haml b/app/views/users/show.html.haml
index cb49c030af2..5e82d5780cf 100644
--- a/app/views/users/show.html.haml
+++ b/app/views/users/show.html.haml
@@ -1,7 +1,7 @@
.row
.col-md-8
%h3.page-title
- = image_tag avatar_icon(@user.email, 90), class: "avatar s90", alt: ''
+ = image_tag avatar_icon(@user.email, 90), class: "avatar avatar-tile s90", alt: ''
= @user.name
- if @user == current_user
.pull-right
@@ -15,12 +15,28 @@
.clearfix
- if @groups.any?
- %h4 Groups:
+ %h4 Groups
= render 'groups', groups: @groups
%hr
- %h4 User Activity:
+
+ .user-calendar
+ %h4.center.light
+ %i.fa.fa-spinner.fa-spin
+ %hr
+ %h4
+ User Activity
+
+ - if current_user
+ %span.rss-icon.pull-right
+ = link_to user_path(@user, :atom, { private_token: current_user.private_token }) do
+ %strong
+ %i.fa.fa-rss
+
= render @events
.col-md-4
= render 'profile', user: @user
- - if @projects.present?
- = render 'projects', projects: @projects
+ = render 'projects'
+
+:coffeescript
+ $ ->
+ $(".user-calendar").load("#{user_calendar_path}")
diff --git a/app/workers/auto_merge_worker.rb b/app/workers/auto_merge_worker.rb
new file mode 100644
index 00000000000..a6dd73eee5f
--- /dev/null
+++ b/app/workers/auto_merge_worker.rb
@@ -0,0 +1,13 @@
+class AutoMergeWorker
+ include Sidekiq::Worker
+
+ sidekiq_options queue: :default
+
+ def perform(merge_request_id, current_user_id, params)
+ params = params.with_indifferent_access
+ current_user = User.find(current_user_id)
+ merge_request = MergeRequest.find(merge_request_id)
+ merge_request.should_remove_source_branch = params[:should_remove_source_branch]
+ merge_request.automerge!(current_user, params[:commit_message])
+ end
+end
diff --git a/app/workers/emails_on_push_worker.rb b/app/workers/emails_on_push_worker.rb
index 2947c8e3ecd..e3f6f3a6aef 100644
--- a/app/workers/emails_on_push_worker.rb
+++ b/app/workers/emails_on_push_worker.rb
@@ -21,5 +21,8 @@ class EmailsOnPushWorker
recipients.split(" ").each do |recipient|
Notify.repository_push_email(project_id, recipient, author_id, branch, compare).deliver
end
+ ensure
+ compare = nil
+ GC.start
end
end
diff --git a/app/workers/project_service_worker.rb b/app/workers/project_service_worker.rb
new file mode 100644
index 00000000000..64d39c4d3f7
--- /dev/null
+++ b/app/workers/project_service_worker.rb
@@ -0,0 +1,10 @@
+class ProjectServiceWorker
+ include Sidekiq::Worker
+
+ sidekiq_options queue: :project_web_hook
+
+ def perform(hook_id, data)
+ data = data.with_indifferent_access
+ Service.find(hook_id).execute(data)
+ end
+end
diff --git a/app/workers/project_web_hook_worker.rb b/app/workers/project_web_hook_worker.rb
index 9f9b9b1df5f..73085c046bd 100644
--- a/app/workers/project_web_hook_worker.rb
+++ b/app/workers/project_web_hook_worker.rb
@@ -4,6 +4,7 @@ class ProjectWebHookWorker
sidekiq_options queue: :project_web_hook
def perform(hook_id, data)
- WebHook.find(hook_id).execute data
+ data = data.with_indifferent_access
+ WebHook.find(hook_id).execute(data)
end
end
diff --git a/app/workers/repository_import_worker.rb b/app/workers/repository_import_worker.rb
index 01586150cd2..437640d2305 100644
--- a/app/workers/repository_import_worker.rb
+++ b/app/workers/repository_import_worker.rb
@@ -6,17 +6,27 @@ class RepositoryImportWorker
def perform(project_id)
project = Project.find(project_id)
- result = gitlab_shell.send(:import_repository,
+
+ import_result = gitlab_shell.send(:import_repository,
project.path_with_namespace,
project.import_url)
+ return project.import_fail unless import_result
+
+ data_import_result = if project.import_type == 'github'
+ Gitlab::GithubImport::Importer.new(project).execute
+ elsif project.import_type == 'gitlab'
+ Gitlab::GitlabImport::Importer.new(project).execute
+ elsif project.import_type == 'bitbucket'
+ Gitlab::BitbucketImport::Importer.new(project).execute
+ else
+ true
+ end
+ return project.import_fail unless data_import_result
- if result
- project.import_finish
- project.save
- project.satellite.create unless project.satellite.exists?
- project.update_repository_size
- else
- project.import_fail
- end
+ project.import_finish
+ project.save
+ project.satellite.create unless project.satellite.exists?
+ project.update_repository_size
+ Gitlab::BitbucketImport::KeyDeleter.new(project).execute if project.import_type == 'bitbucket'
end
end