mirror of
https://github.com/tdurieux/anonymous_github.git
synced 2026-09-12 13:48:58 +02:00
184 lines
12 KiB
HTML
184 lines
12 KiB
HTML
<div class="container paper-page paper-settings">
|
|
<div class="paper-crumbs"><a href="/dashboard">My work</a> / <span class="here">Settings</span></div>
|
|
<h1 class="paper-page-title">Your <em>settings</em></h1>
|
|
<p class="paper-page-lede">Defaults applied to every new anonymization, your quotas, and your account.</p>
|
|
|
|
<div class="paper-settings-body">
|
|
<aside class="paper-settings-toc" v-paper-scrollspy>
|
|
<div class="paper-settings-toc-head">On this page</div>
|
|
<nav>
|
|
<a href="#settings-quota">Quota</a>
|
|
<a href="#settings-terms">Terms to redact</a>
|
|
<a href="#settings-display">Display</a>
|
|
<a href="#settings-features">Features</a>
|
|
<a href="#settings-expiration">Expiration</a>
|
|
<a href="#settings-account">Account</a>
|
|
</nav>
|
|
</aside>
|
|
|
|
<div class="paper-settings-main">
|
|
<!-- Quota: same markup as the dashboard, driven by quotaService -->
|
|
<section id="settings-quota" class="paper-settings-section">
|
|
<div class="paper-section-eyebrow">Quota</div>
|
|
<p class="paper-section-copy">What you are using across all your anonymizations. Conferences can lift these limits during a review window.</p>
|
|
<div class="quota-row" v-if="quota">
|
|
<div class="quota-item" v-for="(q, index) in [
|
|
{key: 'repository', label: 'Repositories', kind: 'count'},
|
|
{key: 'storage', label: 'Storage', kind: 'bytes'},
|
|
{key: 'file', label: 'Files', kind: 'count'}
|
|
]">
|
|
<div class="quota-header">
|
|
<span class="quota-label">{{ q?.label }}</span>
|
|
<span class="quota-value" v-if="q?.kind === 'count'">
|
|
{{ fmt?.number(quota[q?.key].used) }}<span v-if="!quota[q?.key].unlimited"> / {{ fmt?.number(quota[q?.key].total) }}</span>
|
|
<span class="quota-unlimited-tag" v-if="quota[q?.key].unlimited">Unlimited</span>
|
|
</span>
|
|
<span class="quota-value" v-if="q?.kind === 'bytes'">
|
|
{{ fmt?.humanFileSize(quota[q?.key].used) }}<span v-if="!quota[q?.key].unlimited"> / {{ fmt?.humanFileSize(quota[q?.key].total) }}</span>
|
|
<span class="quota-unlimited-tag" v-if="quota[q?.key].unlimited">Unlimited</span>
|
|
</span>
|
|
</div>
|
|
<div class="quota-track" role="progressbar" aria-valuemin="0" :class="'quota-' + quota[q?.key].level" :aria-label="(q?.label) + " quota"" :aria-valuenow="(quota[q?.key].used)" :aria-valuemax="(quota[q?.key].unlimited ? quota[q?.key].used : quota[q?.key].total)" :aria-valuetext="(quota[q?.key].unlimited ? 'unlimited' : fmt.number(quota[q?.key].percent, 0) + '% used')">
|
|
<div class="quota-fill" v-if="!quota[q?.key].unlimited" :style="{width: quota[q?.key].percent + '%'}"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="quota-row" aria-hidden="true" v-if="!quota">
|
|
<div class="quota-item" v-for="(i, index) in [1, 2, 3]">
|
|
<div class="quota-header"><span class="skeleton skeleton-line" style="width: 40%"></span><span class="skeleton skeleton-line" style="width: 25%"></span></div>
|
|
<div class="quota-track"></div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<form class="form needs-validation" name="defaultsForm" novalidate v-form="viewState" @submit.prevent="submitForm($event, () => { saveDefault($event) })">
|
|
<section id="settings-terms" class="paper-settings-section">
|
|
<div class="paper-section-eyebrow">Terms to redact</div>
|
|
<p class="paper-section-copy">Pre-filled in every new anonymization. You can still edit the list per anonymization.</p>
|
|
<div class="form-group">
|
|
<label class="paper-field-label" for="terms">Default terms</label>
|
|
<textarea class="form-control" id="terms" name="terms" rows="4" placeholder="Jane Smith
|
|
MIT CSAIL
|
|
jane.smith@example.org" v-field="{ state: viewState, set: value => { terms = value }, value: terms, form: "defaultsForm", options: { debounce: 250 } }"></textarea>
|
|
<small id="termsHelp" class="form-text text-muted">
|
|
One term per line (regex allowed). Each match is replaced by
|
|
<code>{{ site_options?.ANONYMIZATION_MASK || 'XXX' }}-[N]</code>, or use <code>term=>replacement</code> to pick your own.
|
|
</small>
|
|
<div class="invalid-feedback" v-show="defaultsForm?.terms?.errors?.format">
|
|
Terms are in an invalid format
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<section id="settings-display" class="paper-settings-section">
|
|
<div class="paper-section-eyebrow">Display</div>
|
|
<p class="paper-section-copy">What reviewers can see inside an anonymized repository.</p>
|
|
<div class="form-group paper-check-list">
|
|
<div class="form-check">
|
|
<input class="form-check-input" type="checkbox" id="link" name="link" v-field="{ state: viewState, set: value => { options.link = value }, value: options?.link, form: "defaultsForm", options: {} }">
|
|
<label class="form-check-label" for="link">Keep links</label>
|
|
<small class="form-text text-muted">Unchecked, every link in text files is removed.</small>
|
|
</div>
|
|
<div class="form-check">
|
|
<input class="form-check-input" type="checkbox" id="image" name="image" v-field="{ state: viewState, set: value => { options.image = value }, value: options?.image, form: "defaultsForm", options: {} }">
|
|
<label class="form-check-label" for="image">Display images</label>
|
|
<small class="form-text text-muted">Images are shown as is. They are not anonymized.</small>
|
|
</div>
|
|
<div class="form-check">
|
|
<input class="form-check-input" type="checkbox" id="pdf" name="pdf" v-field="{ state: viewState, set: value => { options.pdf = value }, value: options?.pdf, form: "defaultsForm", options: {} }">
|
|
<label class="form-check-label" for="pdf">Display PDFs</label>
|
|
<small class="form-text text-muted">PDFs are shown as is. They are not anonymized.</small>
|
|
</div>
|
|
<div class="form-check">
|
|
<input class="form-check-input" type="checkbox" id="notebook" name="notebook" v-field="{ state: viewState, set: value => { options.notebook = value }, value: options?.notebook, form: "defaultsForm", options: {} }">
|
|
<label class="form-check-label" for="notebook">Display notebooks</label>
|
|
<small class="form-text text-muted">Render Jupyter notebooks instead of raw JSON.</small>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<section id="settings-features" class="paper-settings-section">
|
|
<div class="paper-section-eyebrow">Features</div>
|
|
<div class="form-group paper-check-list">
|
|
<div class="form-check">
|
|
<input class="form-check-input" type="checkbox" id="page" name="page" v-field="{ state: viewState, set: value => { options.page = value }, value: options?.page, form: "defaultsForm", options: {} }">
|
|
<label class="form-check-label" for="page">GitHub Pages</label>
|
|
<small class="form-text text-muted">Serve an anonymized copy of the repository's GitHub Pages site. Only pages built from the anonymized branch are supported.</small>
|
|
</div>
|
|
<div class="form-check">
|
|
<input class="form-check-input" type="checkbox" id="loc" name="loc" v-field="{ state: viewState, set: value => { options.loc = value }, value: options?.loc, form: "defaultsForm", options: {} }">
|
|
<label class="form-check-label" for="loc">Lines of code</label>
|
|
<small class="form-text text-muted">Show the line count of the repository in the explorer.</small>
|
|
</div>
|
|
<div class="form-check">
|
|
<input class="form-check-input" type="checkbox" id="update" name="update" v-field="{ state: viewState, set: value => { options.update = value }, value: options?.update, form: "defaultsForm", options: {} }">
|
|
<label class="form-check-label" for="update">Auto-update from GitHub</label>
|
|
<small class="form-text text-muted">Follow the branch and pull the latest commit automatically, at most once an hour.</small>
|
|
</div>
|
|
</div>
|
|
<div class="form-group">
|
|
<label class="paper-field-label" for="mode">Fetch mode</label>
|
|
<select class="form-control" id="mode" name="mode" v-field="{ state: viewState, set: value => { options.mode = value }, value: options?.mode, form: "defaultsForm", options: {} }">
|
|
<option value="GitHubStream" selected>Stream from GitHub on demand</option>
|
|
<option value="GitHubDownload">Download to Anonymous GitHub</option>
|
|
</select>
|
|
<small class="form-text text-muted">
|
|
Download is faster for reviewers and enables every feature. Streaming is the only option above
|
|
{{ fmt?.humanFileSize(site_options?.MAX_REPO_SIZE * 1024) }}.
|
|
</small>
|
|
</div>
|
|
</section>
|
|
|
|
<section id="settings-expiration" class="paper-settings-section">
|
|
<div class="paper-section-eyebrow">Expiration</div>
|
|
<div class="form-group">
|
|
<label class="paper-field-label" for="expiration">When an anonymization expires</label>
|
|
<select class="form-control" id="expiration" name="expiration" v-field="{ state: viewState, set: value => { options.expirationMode = value }, value: options?.expirationMode, form: "defaultsForm", options: {} }">
|
|
<option value="redirect">Redirect visitors to the GitHub repository</option>
|
|
<option value="remove" selected>Remove the anonymized repository</option>
|
|
</select>
|
|
<small class="form-text text-muted">
|
|
The expiration date itself is chosen per anonymization. Redirecting reveals the source repository, so pick it only once the review is over.
|
|
</small>
|
|
</div>
|
|
</section>
|
|
|
|
<div class="paper-settings-footer">
|
|
<div class="paper-inline-alert paper-inline-alert-error" role="alert" v-if="error"><i class="fas fa-exclamation-circle" aria-hidden="true"></i> <span v-text="error"></span></div>
|
|
<div class="paper-settings-actions">
|
|
<button id="save" type="submit" class="btn btn-ink" :disabled="saving">
|
|
<i class="fas fa-check mr-1" aria-hidden="true" v-if="message"></i>{{ saving ? 'Saving…' : (message ? 'Saved' : 'Save defaults') }}
|
|
</button>
|
|
<span class="paper-settings-hint">Applies to new anonymizations only. Existing ones keep their settings.</span>
|
|
</div>
|
|
</div>
|
|
</form>
|
|
|
|
<section id="settings-account" class="paper-settings-section">
|
|
<div class="paper-section-eyebrow">Account</div>
|
|
<div class="paper-account-card">
|
|
<img width="40" height="40" class="rounded-circle" alt="" v-if="user?.photo" :src="safeUrl((user?.photo))">
|
|
<div class="paper-account-identity">
|
|
<div class="paper-account-name">@{{ user?.username }}</div>
|
|
<div class="paper-account-meta">Signed in with GitHub. Anonymous GitHub only reads your repositories.</div>
|
|
</div>
|
|
<a class="btn btn-outline-ink" href="/api/user/logout" target="_self">Sign out</a>
|
|
</div>
|
|
|
|
<div class="paper-danger-zone">
|
|
<div class="paper-danger-head">Delete account</div>
|
|
<p class="paper-danger-copy">
|
|
Removes all your anonymized repositories, gists, and pull requests,
|
|
revokes the application's access to your GitHub account, and erases
|
|
your personal data (username, email, access tokens). This cannot be undone.
|
|
</p>
|
|
<div class="paper-inline-alert paper-inline-alert-error" role="alert" v-if="deleteError"><i class="fas fa-exclamation-circle" aria-hidden="true"></i> <span v-text="deleteError"></span></div>
|
|
<button type="button" class="btn btn-outline-danger" @click="deleteAccount()" :disabled="deletingAccount">
|
|
<i class="fas fa-trash-alt mr-1" aria-hidden="true"></i>{{ deletingAccount ? 'Deleting…' : 'Delete my account' }}
|
|
</button>
|
|
</div>
|
|
</section>
|
|
</div>
|
|
</div>
|
|
</div>
|