package oltp
// Visibility (RFC 0008 §Visibility) is the two category strings read
// as a preset, and the repo table's visibility column is kept equal to
// "both strings blank" so the read gate's cache and the listing badge
// keep telling the truth RFC 0004 §Private already states.
// fleetRepo is the repository name the fleet's defaults live under: a
// category row here is what a repository without its own row gets.
const fleetRepo = ""
// VisibilityName is the preset a repository's categories are, or
// "Custom".
func (d *DB) VisibilityName(repo string) string {
return VisibilityOf(d.CategoryCaps(repo, "nobody"), d.CategoryCaps(repo, "anonymous"))
}
// SetVisibility writes a preset's two strings. Unknown names are an
// error, not a blanking: the wrong word must not take a repo private.
func (d *DB) SetVisibility(repo, name string) error {
for _, v := range Visibilities {
if v.Name == name {
if err := d.SetCategoryCaps(repo, "nobody", v.Nobody); err != nil {
return err
}
return d.SetCategoryCaps(repo, "anonymous", v.Anonymous)
}
}
return Refusal{"no visibility called " + name}
}
// FleetVisibility is what a new repository starts as.
func (d *DB) FleetVisibility() string { return d.VisibilityName(fleetRepo) }
// SetFleetVisibility sets what a new repository starts as.
func (d *DB) SetFleetVisibility(name string) error { return d.SetVisibility(fleetRepo, name) }
// seedFleetDefault gives a forge that has never chosen a fleet default
// RFC 0008's Public project, once. A forge that wants fossil's stock
// strings sets them on the fleet page and they stay.
func (d *DB) seedFleetDefault() error {
var n int
if err := d.db.QueryRow(`SELECT count(*) FROM repo_category_caps WHERE repo = ''`).Scan(&n); err != nil {
return err
}
if n > 0 {
return nil
}
return d.SetFleetVisibility(Visibilities[0].Name)
}
// syncVisibilityColumn keeps repo.visibility equal to "nobody and
// anonymous are both blank", which is the read gate's fast answer.
func (d *DB) syncVisibilityColumn(repo string) error {
if repo == fleetRepo {
return nil
}
vis := Public
if d.CategoryCaps(repo, "nobody") == "" && d.CategoryCaps(repo, "anonymous") == "" {
vis = Private
}
_, err := d.w.Exec(`UPDATE repo SET visibility = ? WHERE name = ?`, vis, repo)
return err
}
// ReachOnlyByCategory lists the accounts that reach a repository
// through the categories alone — no string of their own — which is who
// loses access when it goes private. A site admin is one of them when
// they hold no role here (#318). The take-private wizard offers each a
// role first.
func (d *DB) ReachOnlyByCategory(repo string) ([]string, error) {
rows, err := d.db.Query(`SELECT a.name FROM account a
LEFT JOIN repo_caps c ON c.account = a.name AND c.repo = ?
WHERE COALESCE(c.caps, '') = '' ORDER BY a.name`, repo)
if err != nil {
return nil, err
}
defer rows.Close()
var out []string
for rows.Next() {
var n string
if err := rows.Scan(&n); err != nil {
return nil, err
}
out = append(out, n)
}
return out, rows.Err()
}