Skip to content
quarry Artifact [cdb1ea542c]
Login

Artifact [cdb1ea542c]

Artifact cdb1ea542ce851de6d76139174326d49412c24a592d0578ca6606aed5b5e5a1e:


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, no site
// admin flag — which is who loses access when it goes private. 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 a.admin = 0 AND 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()
}