Commit 0f5eb7e4 authored by Phil Hughes's avatar Phil Hughes Committed by David O'Regan

Add user README to users profile

When a user has a project named the same as their username
and then this project has a README file inside of the repository
we will display the content of this README on the overview
tab of the users profile.

Changelog: added

Closes https://gitlab.com/gitlab-org/gitlab/-/issues/232157
parent 7b03b44b
...@@ -43,6 +43,8 @@ const onProjectPathChange = ($projectNameInput, $projectPathInput, hasExistingPr ...@@ -43,6 +43,8 @@ const onProjectPathChange = ($projectNameInput, $projectPathInput, hasExistingPr
}; };
const setProjectNamePathHandlers = ($projectNameInput, $projectPathInput) => { const setProjectNamePathHandlers = ($projectNameInput, $projectPathInput) => {
const specialRepo = document.querySelector('.js-user-readme-repo');
// eslint-disable-next-line @gitlab/no-global-event-off // eslint-disable-next-line @gitlab/no-global-event-off
$projectNameInput.off('keyup change').on('keyup change', () => { $projectNameInput.off('keyup change').on('keyup change', () => {
onProjectNameChange($projectNameInput, $projectPathInput); onProjectNameChange($projectNameInput, $projectPathInput);
...@@ -54,6 +56,11 @@ const setProjectNamePathHandlers = ($projectNameInput, $projectPathInput) => { ...@@ -54,6 +56,11 @@ const setProjectNamePathHandlers = ($projectNameInput, $projectPathInput) => {
$projectPathInput.off('keyup change').on('keyup change', () => { $projectPathInput.off('keyup change').on('keyup change', () => {
onProjectPathChange($projectNameInput, $projectPathInput, hasUserDefinedProjectName); onProjectPathChange($projectNameInput, $projectPathInput, hasUserDefinedProjectName);
hasUserDefinedProjectPath = $projectPathInput.val().trim().length > 0; hasUserDefinedProjectPath = $projectPathInput.val().trim().length > 0;
specialRepo.classList.toggle(
'gl-display-none',
$projectPathInput.val() !== $projectPathInput.data('username'),
);
}); });
}; };
......
...@@ -1992,6 +1992,18 @@ class User < ApplicationRecord ...@@ -1992,6 +1992,18 @@ class User < ApplicationRecord
saved saved
end end
def user_project
strong_memoize(:user_project) do
personal_projects.find_by(path: username, visibility_level: Gitlab::VisibilityLevel::PUBLIC)
end
end
def user_readme
strong_memoize(:user_readme) do
user_project&.repository&.readme
end
end
protected protected
# override, from Devise::Validatable # override, from Devise::Validatable
......
...@@ -40,12 +40,17 @@ ...@@ -40,12 +40,17 @@
.form-group.project-path.col-sm-6 .form-group.project-path.col-sm-6
= f.label :path, class: 'label-bold' do = f.label :path, class: 'label-bold' do
%span= _("Project slug") %span= _("Project slug")
= f.text_field :path, placeholder: "my-awesome-project", class: "form-control gl-form-input", required: true, aria: { required: true } = f.text_field :path, placeholder: "my-awesome-project", class: "form-control gl-form-input", required: true, aria: { required: true }, data: { username: current_user.username }
- if current_user.can_create_group? - if current_user.can_create_group?
.form-text.text-muted .form-text.text-muted
- link_start_group_path = '<a href="%{path}">' % { path: new_group_path } - link_start_group_path = '<a href="%{path}">' % { path: new_group_path }
- project_tip = s_('ProjectsNew|Want to house several dependent projects under the same namespace? %{link_start}Create a group.%{link_end}') % { link_start: link_start_group_path, link_end: '</a>' } - project_tip = s_('ProjectsNew|Want to house several dependent projects under the same namespace? %{link_start}Create a group.%{link_end}') % { link_start: link_start_group_path, link_end: '</a>' }
= project_tip.html_safe = project_tip.html_safe
.gl-alert.gl-alert-success.gl-mb-4.gl-display-none.js-user-readme-repo
= sprite_icon('check-circle', size: 16, css_class: 'gl-icon gl-alert-icon gl-alert-icon-no-title')
.gl-alert-body
- help_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: help_page_path('user/profile/index', anchor: 'user-profile-readme') }
= html_escape(_('%{project_path} is a project that you can use to add a README to your GitLab profile. Create a public project and initialize the repository with a README to get started. %{help_link_start}Learn more.%{help_link_end}')) % { project_path: "<strong>#{current_user.username} / #{current_user.username}</strong>".html_safe, help_link_start: help_link_start, help_link_end: '</a>'.html_safe }
.form-group .form-group
= f.label :description, class: 'label-bold' do = f.label :description, class: 'label-bold' do
......
...@@ -9,6 +9,22 @@ ...@@ -9,6 +9,22 @@
%a.js-retry-load{ href: '#' } %a.js-retry-load{ href: '#' }
= s_('UserProfile|Retry') = s_('UserProfile|Retry')
.user-calendar-activities .user-calendar-activities
- if @user.user_readme
.row
.col-12.gl-my-6
.gl-border-gray-100.gl-border-1.gl-border-solid.gl-rounded-small.gl-py-4.gl-px-6
.gl-display-flex
%ol.breadcrumb.gl-breadcrumb-list.gl-mb-4
%li.breadcrumb-item.gl-breadcrumb-item
= link_to @user.username, project_path(@user.user_project)
%span.gl-breadcrumb-separator
= sprite_icon("chevron-right", size: 16)
%li.breadcrumb-item.gl-breadcrumb-item
= link_to @user.user_readme.path, @user.user_project.readme_url
- if current_user == @user
.gl-ml-auto
= link_to _('Edit'), edit_blob_path(@user.user_project, @user.user_project.default_branch, @user.user_readme.path)
= render 'projects/blob/viewer', viewer: @user.user_readme.rich_viewer, load_async: false
.row .row
%div{ class: activity_pane_class } %div{ class: activity_pane_class }
- if can?(current_user, :read_cross_project) - if can?(current_user, :read_cross_project)
......
...@@ -100,6 +100,18 @@ When visiting the public page of a user, you can only see the projects which you ...@@ -100,6 +100,18 @@ When visiting the public page of a user, you can only see the projects which you
If the [public level is restricted](../admin_area/settings/visibility_and_access_controls.md#restrict-visibility-levels), If the [public level is restricted](../admin_area/settings/visibility_and_access_controls.md#restrict-visibility-levels),
user profiles are only visible to signed-in users. user profiles are only visible to signed-in users.
## User profile README
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/232157) in GitLab 14.5.
You can add a README section to your profile that can include more information and formatting than
your profile's bio.
To add a README to your profile:
1. Create a new public project with the same name as your GitLab username.
1. Create a README file inside this project. The file can be any valid [README or index file](../project/repository/index.md#readme-and-index-files).
## Add external accounts to your user profile page ## Add external accounts to your user profile page
You can add links to certain other external accounts you might have, like Skype and Twitter. You can add links to certain other external accounts you might have, like Skype and Twitter.
......
...@@ -815,6 +815,9 @@ msgstr "" ...@@ -815,6 +815,9 @@ msgstr ""
msgid "%{primary} (%{secondary})" msgid "%{primary} (%{secondary})"
msgstr "" msgstr ""
msgid "%{project_path} is a project that you can use to add a README to your GitLab profile. Create a public project and initialize the repository with a README to get started. %{help_link_start}Learn more.%{help_link_end}"
msgstr ""
msgid "%{ref} cannot be added: %{error}" msgid "%{ref} cannot be added: %{error}"
msgstr "" msgstr ""
......
...@@ -21,6 +21,14 @@ RSpec.describe 'User visits their profile' do ...@@ -21,6 +21,14 @@ RSpec.describe 'User visits their profile' do
expect(page).to have_content "This information will appear on your profile" expect(page).to have_content "This information will appear on your profile"
end end
it 'shows user readme' do
create(:project, :repository, :public, path: user.username, namespace: user.namespace)
visit(user_path(user))
expect(find('.file-content')).to have_content('testme')
end
context 'when user has groups' do context 'when user has groups' do
let(:group) do let(:group) do
create :group do |group| create :group do |group|
......
...@@ -6225,4 +6225,31 @@ RSpec.describe User do ...@@ -6225,4 +6225,31 @@ RSpec.describe User do
expect(described_class.get_ids_by_username([user_name])).to match_array([user_id]) expect(described_class.get_ids_by_username([user_name])).to match_array([user_id])
end end
end end
describe 'user_project' do
it 'returns users project matched by username and public visibility' do
user = create(:user)
public_project = create(:project, :public, path: user.username, namespace: user.namespace)
create(:project, namespace: user.namespace)
expect(user.user_project).to eq(public_project)
end
end
describe 'user_readme' do
it 'returns readme from user project' do
user = create(:user)
create(:project, :repository, :public, path: user.username, namespace: user.namespace)
expect(user.user_readme.name).to eq('README.md')
expect(user.user_readme.data).to include('testme')
end
it 'returns nil if project is private' do
user = create(:user)
create(:project, :repository, :private, path: user.username, namespace: user.namespace)
expect(user.user_readme).to be(nil)
end
end
end end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment