-
-
Notifications
You must be signed in to change notification settings - Fork 5.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Pin repos on profile #30961
base: main
Are you sure you want to change the base?
Pin repos on profile #30961
Conversation
- Created the pin model, and basic CRUD operations. - Implemented checks for lost of visibility of owner, to automatically delete the repository. - Implemented check for the lack of visibility of a viewer, when requesting the repos of another user, excluding those repositories from the list that is returned. - Added the deletion of all the pins of a repository when it is deleted. - Implemented the ability for a user to pin on the profile of an org user, checking if the user is admin of the org, and the org is owner of the repo. Co-authored-by: Daniel Carvalho <[email protected]>
- Added list and count of pinned repos to the context data of the routes of a user and org profile. - Added a template for the list of pinned repo cards. - Included said template in the user and org profile templates. Co-authored-by: Daniel Carvalho <[email protected]>
- Added the unpin octicon svg from a previous attempt to implement this feature. - Added the template for the button/set of buttons for pinning/unpinning a repo. - Added the use of said template in the header of a repo main page. - Added the routes for the POST requests for pinning/unpinning to user/org. Co-authored-by: Daniel Carvalho <[email protected]>
- Created unit tests that check the basic functionality of the model functions - Created integration tests that check the functionality of the POST routes, verifying the effects directly on the database. Co-authored-by: Daniel Carvalho <[email protected]>
{{if .IsPrivate}} | ||
<span class="ui basic label">{{ctx.Locale.Tr "repo.desc.private"}}</span> | ||
{{end}} | ||
{{end}} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we have this label sub-templated yet? If not, I suggest creating templates/shared/repo/label.tmpl
which accepts a Repo as its only argument.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think that sub-template exists yet. At least the repo main page's header does not use any template for that part and I don't find any label template related to repos. I will create it then.
Co-authored-by: silverwind <[email protected]>
… used it with the already present octicon-pin-slash
@silverwind I managed to reduce the space between the cards to half the size on the latest commit, but I don't know if my changes follow the best practices. Can you comment on that? |
} | ||
|
||
// CleanupPins iterates over the repos pinned by a user and removes | ||
// the invalid pins. (Needs to be called everytime before we read/write a pin) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(Needs to be called everytime before we read/write a pin)
This function seems a bit strange. I think it is not the correct way to handle this problem.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This approach where we clean the invalid pins only when it is needed was suggested in the original issue thread (#10375 (comment)), so we just followed that suggestion since it seemed to be agreed upon. If people want to discuss this further, we can implement what is desired later, after a consensus is reached.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is something like this #26530
At first, we do not have permission system, and stored all these permission related data in DB.
Actually, it should be generated dynamically or there will be more and more bugs.
The comment you mentioned is about 2 years ago, which is a solution for this problem, but I don't think it is a good solution.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe we should wait for the maintainers to give their opinion. I guess they are currently focused on 1.22.0, but after the release they could comment on this.
func HasPermsToPin(ctx *context.Context, u *user_model.User, r *repo_model.Repository) bool { | ||
// If user is an organization, it can only pin its own repos | ||
if u.IsOrganization() { | ||
return r.OwnerID == u.ID |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the user here is doer
not the owner of the repo, also in CanPin
, it is better to rename u
to doer
so u.IsOrganization()
will always be false here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure that I'm understanding what you are saying here. Since there are two pin actions (pin to the doer's profile and pin to the owner org's profile), the user passed can be an organization. Also, this function is called in CleanupPins
to check if any repo pinned to the org's profile has been moved to another org.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
doer is the logined user, and organization user is also defined as user in source codes, but we can not login as an organization user. So doer is always an individual user and u.IsOrganization() will always be false here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, the thing is that this function is not only called with a Doer. It's just a function that checks for the base requirements that a user needs to fulfill to have the repository pinned to their profile. When a user tries to pin a repository to their own profile, the user in this function will be the Doer. But that user can also pin the repo to an organization (meaning that the user in this function will be the org, since that is the target profile for the pin). This function is also used later to check if the user has lost visibility to the repo, or (in this case for organizations) if the repo has been transferred to another org.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But that user can also pin the repo to an organization (meaning that the user in this function will be the org, since that is the target profile for the pin).
This is not a permission check.
The permission check should be
(1) whether doer (the user tries to pin the repo to an org) is the owner/admin of the org (2) which should be the repo's owner.
You only checked the second one, so if there's a public org and a public repo (belongs to this org), the user who is not the member of this org, can access this repo's, and can pin this repo to the org, which should not happen.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, maybe that is a naming issue. This function was intended only to check if a given user has perms to have a given repository pinned to their profile. The check that the Doer has perms on the org to pin in its profile is done in assertUserOrgPerms
which is only called if the action done is pinning/unpinning on the org profile. Maybe this code can be refactored or at least the functions can be renamed.
Thank you for your contribution. I will do a review after 1.22.0 is released. |
Can you also upload a screenshot of organization's home(profile) page? |
Yes, we also thought about that and changed it so the pinned repo cards are inside the repositories tab. |
Is there a way to customize the order? |
|
||
func loadPinData(ctx *context.Context) error { | ||
// First, cleanup any pins that are no longer valid | ||
err := user_service.CleanupPins(ctx, ctx.Doer) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why cleanup pins when user home page be visited? Looks wired.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We are cleaning the pins to avoid an edge case.
If we have a user1
, with 6 pins, being one of those pins a repo belonging to user2
. If user 2
changes the repo visibility to private, user1
can no longer have it as a pin. If user1
now visited a new repo page, without visiting his own homepage, the pin
button would be disabled because he already has the maximum number of pins. However, if we clean the pins, the pin to the repo of user2
will be deleted, and the button will be enabled.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When changing the visibility and user has no permission to access this pin, means user2 forcibly unpinned it, so this should be handled when user2
changes the repo visibility, not the moment user visiting the repo home page.
Also for other cases, like remove the user as the member of an organization and so on.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is still a concern. Cleaning should not be done when a page is visiting.
// Checks if the user has permission to have the repo pinned in it's profile. | ||
func HasPermsToPin(ctx *context.Context, u *user_model.User, r *repo_model.Repository) bool { | ||
// If user is an organization, it can only pin its own repos | ||
if u.IsOrganization() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think u
should be renamed doer
. This should check whether the doer can pin the repository into doer's profile? Or we need another parameter which is the pinning target user/org.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the best approach would be to add a parameter that is the target and move all checks for permissions to this function.
@JakobDev |
I think ordering is a basic feature that belongs to this PR before merging |
We also need to handle user/org deletions. |
We forgot about that. Now it's done. |
I think such cards are prime candiate for |
@carlosfelgueiras could you fix the merge conflict? Then I will do the refactor. |
Done. Thanks in advance. |
|
||
type Pin struct { | ||
ID int64 `xorm:"pk autoincr"` | ||
UID int64 `xorm:"UNIQUE(s)"` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why not UserID
? i do not think UID is a good name. It looks like UUID
probably need to recheck if there are conflicts since the main branch has moved on |
Fixes #10375
This PR implements the ability for users and organizations to pin repositories to their profiles.
At a given time, each user or organization can have, at most, 6 repositories pinned. A user can pin any repository that it has read permissions to. Organizations can only have pinned to their profiles repositories owned by themselves, only administrators of an organization can pin repositories to the org's profile.
The pins are stored on a table called
repo_pin
. When a repository is deleted, all its pins are automatically deleted. When a user loses visibility to a repository, the pin is not automatically removed. Instead, invalid pins will automatically be removed the next time someone does a read or write operation to said user's pins.When a different user is visiting someone's profile, only the repositories that the visiting user can see are shown on the pins, to avoid leaking private information.
Used assets (unpin icon and pined repo cards) from #19831.