@@ -1462,46 +1462,18 @@ func getParticipantsByIssueID(e Engine, issueID int64) ([]*User, error) {
14621462 return users , e .In ("id" , userIDs ).Find (& users )
14631463}
14641464
1465- // UpdateIssueMentions extracts mentioned people from content and
1466- // updates issue-user relations for them.
1467- func UpdateIssueMentions (e Engine , issueID int64 , mentions []string ) error {
1465+ // UpdateIssueMentions updates issue-user relations for mentioned users.
1466+ func UpdateIssueMentions (e Engine , issueID int64 , mentions []* User ) error {
14681467 if len (mentions ) == 0 {
14691468 return nil
14701469 }
1471-
1472- for i := range mentions {
1473- mentions [i ] = strings .ToLower (mentions [i ])
1474- }
1475- users := make ([]* User , 0 , len (mentions ))
1476-
1477- if err := e .In ("lower_name" , mentions ).Asc ("lower_name" ).Find (& users ); err != nil {
1478- return fmt .Errorf ("find mentioned users: %v" , err )
1470+ ids := make ([]int64 , len (mentions ))
1471+ for i , u := range mentions {
1472+ ids [i ] = u .ID
14791473 }
1480-
1481- ids := make ([]int64 , 0 , len (mentions ))
1482- for _ , user := range users {
1483- ids = append (ids , user .ID )
1484- if ! user .IsOrganization () || user .NumMembers == 0 {
1485- continue
1486- }
1487-
1488- memberIDs := make ([]int64 , 0 , user .NumMembers )
1489- orgUsers , err := getOrgUsersByOrgID (e , user .ID )
1490- if err != nil {
1491- return fmt .Errorf ("GetOrgUsersByOrgID [%d]: %v" , user .ID , err )
1492- }
1493-
1494- for _ , orgUser := range orgUsers {
1495- memberIDs = append (memberIDs , orgUser .ID )
1496- }
1497-
1498- ids = append (ids , memberIDs ... )
1499- }
1500-
15011474 if err := UpdateIssueUsersByMentions (e , issueID , ids ); err != nil {
15021475 return fmt .Errorf ("UpdateIssueUsersByMentions: %v" , err )
15031476 }
1504-
15051477 return nil
15061478}
15071479
@@ -1854,3 +1826,120 @@ func (issue *Issue) updateClosedNum(e Engine) (err error) {
18541826 }
18551827 return
18561828}
1829+
1830+ // ResolveMentionsByVisibility returns the users mentioned in an issue, removing those that
1831+ // don't have access to reading it. Teams are expanded into their users, but organizations are ignored.
1832+ func (issue * Issue ) ResolveMentionsByVisibility (e Engine , doer * User , mentions []string ) (users []* User , err error ) {
1833+ if len (mentions ) == 0 {
1834+ return
1835+ }
1836+ if err = issue .loadRepo (e ); err != nil {
1837+ return
1838+ }
1839+ resolved := make (map [string ]bool , 20 )
1840+ names := make ([]string , 0 , 20 )
1841+ resolved [doer .LowerName ] = true
1842+ for _ , name := range mentions {
1843+ name := strings .ToLower (name )
1844+ if _ , ok := resolved [name ]; ok {
1845+ continue
1846+ }
1847+ resolved [name ] = false
1848+ names = append (names , name )
1849+ }
1850+
1851+ if err := issue .Repo .getOwner (e ); err != nil {
1852+ return nil , err
1853+ }
1854+
1855+ if issue .Repo .Owner .IsOrganization () {
1856+ // Since there can be users with names that match the name of a team,
1857+ // if the team exists and can read the issue, the team takes precedence.
1858+ teams := make ([]* Team , 0 , len (names ))
1859+ if err := e .
1860+ Join ("INNER" , "team_repo" , "team_repo.team_id = team.id" ).
1861+ Where ("team_repo.repo_id=?" , issue .Repo .ID ).
1862+ In ("team.lower_name" , names ).
1863+ Find (& teams ); err != nil {
1864+ return nil , fmt .Errorf ("find mentioned teams: %v" , err )
1865+ }
1866+ if len (teams ) != 0 {
1867+ checked := make ([]int64 , 0 , len (teams ))
1868+ unittype := UnitTypeIssues
1869+ if issue .IsPull {
1870+ unittype = UnitTypePullRequests
1871+ }
1872+ for _ , team := range teams {
1873+ if team .Authorize >= AccessModeOwner {
1874+ checked = append (checked , team .ID )
1875+ resolved [team .LowerName ] = true
1876+ continue
1877+ }
1878+ has , err := e .Get (& TeamUnit {OrgID : issue .Repo .Owner .ID , TeamID : team .ID , Type : unittype })
1879+ if err != nil {
1880+ return nil , fmt .Errorf ("get team units (%d): %v" , team .ID , err )
1881+ }
1882+ if has {
1883+ checked = append (checked , team .ID )
1884+ resolved [team .LowerName ] = true
1885+ }
1886+ }
1887+ if len (checked ) != 0 {
1888+ teamusers := make ([]* User , 0 , 20 )
1889+ if err := e .
1890+ Join ("INNER" , "team_user" , "team_user.uid = `user`.id" ).
1891+ In ("`team_user`.team_id" , checked ).
1892+ And ("`user`.is_active = ?" , true ).
1893+ And ("`user`.prohibit_login = ?" , false ).
1894+ Find (& teamusers ); err != nil {
1895+ return nil , fmt .Errorf ("get teams users: %v" , err )
1896+ }
1897+ if len (teamusers ) > 0 {
1898+ users = make ([]* User , 0 , len (teamusers ))
1899+ for _ , user := range teamusers {
1900+ if already , ok := resolved [user .LowerName ]; ! ok || ! already {
1901+ users = append (users , user )
1902+ resolved [user .LowerName ] = true
1903+ }
1904+ }
1905+ }
1906+ }
1907+ }
1908+
1909+ // Remove names already in the list to avoid querying the database if pending names remain
1910+ names = make ([]string , 0 , len (resolved ))
1911+ for name , already := range resolved {
1912+ if ! already {
1913+ names = append (names , name )
1914+ }
1915+ }
1916+ if len (names ) == 0 {
1917+ return
1918+ }
1919+ }
1920+
1921+ unchecked := make ([]* User , 0 , len (names ))
1922+ if err := e .
1923+ Where ("`user`.is_active = ?" , true ).
1924+ And ("`user`.prohibit_login = ?" , false ).
1925+ In ("`user`.lower_name" , names ).
1926+ Find (& unchecked ); err != nil {
1927+ return nil , fmt .Errorf ("find mentioned users: %v" , err )
1928+ }
1929+ for _ , user := range unchecked {
1930+ if already := resolved [user .LowerName ]; already || user .IsOrganization () {
1931+ continue
1932+ }
1933+ // Normal users must have read access to the referencing issue
1934+ perm , err := getUserRepoPermission (e , issue .Repo , user )
1935+ if err != nil {
1936+ return nil , fmt .Errorf ("getUserRepoPermission [%d]: %v" , user .ID , err )
1937+ }
1938+ if ! perm .CanReadIssuesOrPulls (issue .IsPull ) {
1939+ continue
1940+ }
1941+ users = append (users , user )
1942+ }
1943+
1944+ return
1945+ }
0 commit comments