mirror of
https://github.com/tdurieux/anonymous_github.git
synced 2026-09-12 13:48:58 +02:00
433 lines
32 KiB
HTML
433 lines
32 KiB
HTML
<div class="anonymize-page h-100">
|
|
<!-- ===== STATE 1: No URL — centered input ===== -->
|
|
<div class="anonymize-landing" v-show="!(sourceUrl)">
|
|
<div class="anonymize-landing-inner">
|
|
<div class="paper-crumbs">My work / <span class="here">New anonymization</span></div>
|
|
<h1 class="paper-page-title">New <em>anonymization</em></h1>
|
|
<p class="paper-page-lede">
|
|
Paste a GitHub repository, pull-request, or gist URL. We’ll fetch
|
|
it, strip every trace of identity, and hand you back a stable link.
|
|
</p>
|
|
<div class="form-group mt-4 mb-2">
|
|
<label class="paper-field-label" for="sourceUrl-landing">Source URL</label>
|
|
<input id="sourceUrl-landing" type="text" class="form-control form-control-lg" placeholder="https://github.com/owner/repository" v-field="{ state: viewState, set: value => { sourceUrl = value }, value: sourceUrl, form: null, options: { debounce: {default: 1000, blur: 0, click: 0}, updateOn: 'default blur click' }, change: () => { urlSelected() } }">
|
|
</div>
|
|
<small class="form-text" style="color: var(--ink-muted);">
|
|
Repository, pull request (…/pull/42) and gist (gist.github.com/…) URLs are all accepted.
|
|
</small>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- ===== STATE 2: URL provided — form (left) + preview (right) ===== -->
|
|
<div class="anonymize-workspace" v-show="sourceUrl">
|
|
<header class="anonymize-topbar">
|
|
<div class="anonymize-topbar-inner">
|
|
<div class="paper-crumbs">
|
|
<a href="/dashboard">My work</a> /
|
|
<span class="here">{{ isUpdate ? 'Edit anonymization' : 'New anonymization' }}</span>
|
|
</div>
|
|
<div class="anonymize-topbar-head">
|
|
<h1 class="paper-page-title anonymize-topbar-title">
|
|
<span v-if="!isUpdate">New <em>anonymization</em></span>
|
|
<span v-if="isUpdate">Edit <em>anonymization</em></span>
|
|
</h1>
|
|
<span class="type-badge" v-show="detectedType" :class="{'type-repo': detectedType === 'repo', 'type-pr': detectedType === 'pr', 'type-gist': detectedType === 'gist'}">
|
|
{{ detectedType === 'repo' ? 'Repo' : detectedType === 'pr' ? 'PR' : 'Gist' }}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
</header>
|
|
|
|
<div class="anonymize-split">
|
|
<!-- Form column (left) -->
|
|
<div class="anonymize-form-col overflow-auto">
|
|
<form class="form needs-validation paper-settings-main" name="anonymize" novalidate v-form="viewState">
|
|
|
|
<section class="paper-settings-section">
|
|
<div class="paper-section-eyebrow">Source</div>
|
|
|
|
<div class="form-group">
|
|
<label class="paper-field-label" for="sourceUrl">GitHub URL</label>
|
|
<input type="text" class="form-control" name="sourceUrl" id="sourceUrl" :disabled="isUpdate && detectedType !== 'repo'" placeholder="Paste a GitHub repo or pull request URL" v-field="{ state: viewState, set: value => { sourceUrl = value }, value: sourceUrl, form: "anonymize", options: { debounce: {default: 1000, blur: 0, click: 0}, updateOn: 'default blur click' }, change: () => { urlSelected() } }" :class="{'is-invalid': anonymize?.sourceUrl?.invalid}">
|
|
<div class="invalid-feedback" v-show="anonymize?.sourceUrl?.errors?.github">
|
|
Please provide a valid GitHub URL.
|
|
</div>
|
|
<div class="invalid-feedback" v-show="anonymize?.sourceUrl?.errors?.access">
|
|
Not accessible. The organization may restrict access.
|
|
</div>
|
|
<div class="invalid-feedback" v-show="anonymize?.sourceUrl?.errors?.missing">
|
|
Does not exist or is not accessible.
|
|
</div>
|
|
<div class="invalid-feedback" v-show="anonymize?.sourceUrl?.errors?.used">
|
|
Already anonymized.
|
|
</div>
|
|
</div>
|
|
|
|
<div class="form-grid-2" v-show="detectedType === 'repo'">
|
|
<div class="form-group">
|
|
<label class="paper-field-label" for="branch">Branch</label>
|
|
<div class="input-group">
|
|
<select class="form-control" id="branch" name="branch" v-field="{ state: viewState, set: value => { source.branch = value }, value: source?.branch, form: "anonymize", options: {} }">
|
|
<option v-for="(b, index) in branches" v-text="b?.name" :value="(b?.name)"></option>
|
|
</select>
|
|
<div class="input-group-append">
|
|
<button class="btn" type="button" title="Refresh" data-toggle="tooltip" data-placement="bottom" @click="getBranches(true)">
|
|
<i class="fas fa-sync" aria-hidden="true"></i>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label class="paper-field-label" for="commit">Commit</label>
|
|
<input class="form-control" id="commit" :disabled="options.update" name="commit" pattern="[a-fA-Z0-9]{6,}" required v-field="{ state: viewState, set: value => { source.commit = value }, value: source?.commit, form: "anonymize", options: {} }" :class="{'is-invalid': anonymize?.commit?.invalid}">
|
|
<div class="invalid-feedback" v-show="anonymize?.commit?.errors?.pattern || anonymize?.commit?.errors?.required">
|
|
The commit SHA is not valid.
|
|
</div>
|
|
<div class="invalid-feedback" v-show="anonymize?.commit?.errors?.exists">
|
|
This commit no longer exists in the repository. Click refresh to get the latest.
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="form-check" v-show="detectedType">
|
|
<input class="form-check-input" type="checkbox" id="update" name="update" v-field="{ state: viewState, set: value => { options.update = value }, value: options?.update, form: "anonymize", 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>
|
|
</section>
|
|
|
|
<section class="paper-settings-section" v-show="detectedType">
|
|
<div class="paper-section-eyebrow">Identity</div>
|
|
|
|
<div class="form-group" v-show="detectedType === 'repo'">
|
|
<label class="paper-field-label" for="repoId">Anonymized repository ID</label>
|
|
<input type="text" class="form-control" name="repoId" id="repoId" :disabled="isUpdate" v-field="{ state: viewState, set: value => { repoId = value }, value: repoId, form: "anonymize", options: { debounce: {default: 1000, blur: 0, click: 0}, updateOn: 'default blur click' } }" :class="{'is-invalid': anonymize?.repoId?.invalid}">
|
|
<small class="form-text text-muted">Your share link will be <code>anonymous.4open.science/r/{{ repoId }}</code>.</small>
|
|
<div class="invalid-feedback" v-show="anonymize?.repoId?.errors?.format">ID can only contain letters and numbers.</div>
|
|
<div class="invalid-feedback" v-show="anonymize?.repoId?.errors?.used">{{ repoId }} is already used.</div>
|
|
</div>
|
|
|
|
<div class="form-group" v-show="detectedType === 'pr'">
|
|
<label class="paper-field-label" for="pullRequestId">Anonymized pull request ID</label>
|
|
<input type="text" class="form-control" name="pullRequestId" id="pullRequestId" :disabled="isUpdate" v-field="{ state: viewState, set: value => { pullRequestId = value }, value: pullRequestId, form: "anonymize", options: { debounce: {default: 1000, blur: 0, click: 0}, updateOn: 'default blur click' } }" :class="{'is-invalid': anonymize?.pullRequestId?.invalid}">
|
|
<small class="form-text text-muted">Your share link will be <code>anonymous.4open.science/pr/{{ pullRequestId }}</code>.</small>
|
|
<div class="invalid-feedback" v-show="anonymize?.pullRequestId?.errors?.format">ID can only contain letters and numbers.</div>
|
|
<div class="invalid-feedback" v-show="anonymize?.pullRequestId?.errors?.used">{{ pullRequestId }} is already used.</div>
|
|
</div>
|
|
|
|
<div class="form-group" v-show="detectedType === 'gist'">
|
|
<label class="paper-field-label" for="gistId">Anonymized gist ID</label>
|
|
<input type="text" class="form-control" name="gistId" id="gistId" :disabled="isUpdate" v-field="{ state: viewState, set: value => { gistId = value }, value: gistId, form: "anonymize", options: { debounce: {default: 1000, blur: 0, click: 0}, updateOn: 'default blur click' } }" :class="{'is-invalid': anonymize?.gistId?.invalid}">
|
|
<small class="form-text text-muted">Your share link will be <code>anonymous.4open.science/gist/{{ gistId }}</code>.</small>
|
|
<div class="invalid-feedback" v-show="anonymize?.gistId?.errors?.format">ID can only contain letters and numbers.</div>
|
|
<div class="invalid-feedback" v-show="anonymize?.gistId?.errors?.used">{{ gistId }} is already used.</div>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label class="paper-field-label" for="conference">Conference <span class="paper-optional">(optional)</span></label>
|
|
<input class="form-control" id="conference" name="conference" v-field="{ state: viewState, set: value => { conference = value }, value: conference, form: "anonymize", options: { debounce: { default: 800, blur: 0 }, updateOn: 'default blur' } }" :class="{'is-invalid': anonymize?.conference?.invalid}">
|
|
<small class="form-text text-muted" v-show="conference_data">
|
|
<a target="_blank" :href="safeUrl((conference_data?.url))">{{ conference_data?.name }}</a> expires {{ fmt?.date(conference_data?.endDate) }}.
|
|
</small>
|
|
<div class="invalid-feedback" v-show="anonymize?.conference?.errors?.activated">The conference is not activated.</div>
|
|
<small class="form-text text-muted" v-show="!conference_data">Link to a conference to apply its shared defaults.</small>
|
|
</div>
|
|
</section>
|
|
|
|
<section class="paper-settings-section" v-show="detectedType">
|
|
<div class="paper-section-eyebrow">Anonymization</div>
|
|
|
|
<div class="form-group">
|
|
<label class="paper-field-label" for="terms">Terms to redact</label>
|
|
<textarea class="form-control" id="terms" name="terms" rows="4" v-field="{ state: viewState, set: value => { terms = value }, value: terms, form: "anonymize", options: { debounce: 250 } }" :class="{'is-invalid': anonymize?.terms?.invalid}"></textarea>
|
|
<small class="form-text text-muted">One term per line (regex allowed). Replaced by <code>{{ site_options?.ANONYMIZATION_MASK }}-[N]</code>, or use <code>term=>replacement</code> to pick your own (e.g. <code>Anonymous=>ABC</code>).</small>
|
|
<div class="warning-feedback" v-show="termsRegexWarning">Regex characters detected. Escape them if unintentional.</div>
|
|
<div class="invalid-feedback" v-show="anonymize?.terms?.errors?.format">Terms are in an invalid format.</div>
|
|
</div>
|
|
</section>
|
|
|
|
<section class="paper-settings-section" v-show="detectedType">
|
|
<div class="paper-section-eyebrow">Display</div>
|
|
|
|
<div v-show="detectedType === 'repo'">
|
|
<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: "anonymize", options: {} }">
|
|
<label class="form-check-label" for="link">Keep links</label>
|
|
</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: "anonymize", options: {} }">
|
|
<label class="form-check-label" for="image">Display images</label>
|
|
</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: "anonymize", options: {} }">
|
|
<label class="form-check-label" for="pdf">Display PDFs</label>
|
|
</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: "anonymize", options: {} }">
|
|
<label class="form-check-label" for="notebook">Display Notebooks</label>
|
|
</div>
|
|
<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: "anonymize", options: {} }" :disabled="!details?.hasPage || (details?.pageSource && details?.pageSource?.branch !== source?.branch)">
|
|
<label class="form-check-label" for="page">GitHub Pages</label>
|
|
<small class="form-text text-muted d-block" v-show="!details?.hasPage">
|
|
{{ fmt?.translate('WARNINGS.page_not_enabled_on_repo') }}
|
|
</small>
|
|
<small class="form-text text-muted d-block" v-show="details?.hasPage && details?.pageSource && details?.pageSource?.branch !== source?.branch">
|
|
{{ fmt?.translate('WARNINGS.page_branch_mismatch', { pageBranch: details?.pageSource?.branch, selectedBranch: source?.branch }) }}
|
|
</small>
|
|
</div>
|
|
</div>
|
|
|
|
<div v-show="detectedType === 'gist'">
|
|
<div class="form-check">
|
|
<input class="form-check-input" type="checkbox" id="title-gist" name="title-gist" v-field="{ state: viewState, set: value => { options.title = value }, value: options?.title, form: "anonymize", options: {} }">
|
|
<label class="form-check-label" for="title-gist">Gist description</label>
|
|
</div>
|
|
<div class="form-check">
|
|
<input class="form-check-input" type="checkbox" id="content-gist" name="content-gist" v-field="{ state: viewState, set: value => { options.content = value }, value: options?.content, form: "anonymize", options: {} }">
|
|
<label class="form-check-label" for="content-gist">File contents</label>
|
|
</div>
|
|
<div class="form-check">
|
|
<input class="form-check-input" type="checkbox" id="comments-gist" name="comments-gist" v-field="{ state: viewState, set: value => { options.comments = value }, value: options?.comments, form: "anonymize", options: {} }">
|
|
<label class="form-check-label" for="comments-gist">Comments</label>
|
|
</div>
|
|
<div class="form-check">
|
|
<input class="form-check-input" type="checkbox" id="username-gist" name="username-gist" v-field="{ state: viewState, set: value => { options.username = value }, value: options?.username, form: "anonymize", options: {} }">
|
|
<label class="form-check-label" for="username-gist">Usernames</label>
|
|
</div>
|
|
<div class="form-check">
|
|
<input class="form-check-input" type="checkbox" id="date-gist" name="date-gist" v-field="{ state: viewState, set: value => { options.date = value }, value: options?.date, form: "anonymize", options: {} }">
|
|
<label class="form-check-label" for="date-gist">Dates</label>
|
|
</div>
|
|
<div class="form-check">
|
|
<input class="form-check-input" type="checkbox" id="origin-gist" name="origin-gist" v-field="{ state: viewState, set: value => { options.origin = value }, value: options?.origin, form: "anonymize", options: {} }">
|
|
<label class="form-check-label" for="origin-gist">Source gist ID</label>
|
|
</div>
|
|
</div>
|
|
|
|
<div v-show="detectedType === 'pr'">
|
|
<div class="form-check">
|
|
<input class="form-check-input" type="checkbox" id="title" name="title" v-field="{ state: viewState, set: value => { options.title = value }, value: options?.title, form: "anonymize", options: {} }">
|
|
<label class="form-check-label" for="title">PR title</label>
|
|
</div>
|
|
<div class="form-check">
|
|
<input class="form-check-input" type="checkbox" id="body" name="body" v-field="{ state: viewState, set: value => { options.body = value }, value: options?.body, form: "anonymize", options: {} }">
|
|
<label class="form-check-label" for="body">PR body</label>
|
|
</div>
|
|
<div class="form-check">
|
|
<input class="form-check-input" type="checkbox" id="diff" name="diff" v-field="{ state: viewState, set: value => { options.diff = value }, value: options?.diff, form: "anonymize", options: {} }">
|
|
<label class="form-check-label" for="diff">Diff</label>
|
|
</div>
|
|
<div class="form-check">
|
|
<input class="form-check-input" type="checkbox" id="comments" name="comments" v-field="{ state: viewState, set: value => { options.comments = value }, value: options?.comments, form: "anonymize", options: {} }">
|
|
<label class="form-check-label" for="comments">Comments</label>
|
|
</div>
|
|
<div class="form-check">
|
|
<input class="form-check-input" type="checkbox" id="username" name="username" v-field="{ state: viewState, set: value => { options.username = value }, value: options?.username, form: "anonymize", options: {} }">
|
|
<label class="form-check-label" for="username">Usernames</label>
|
|
</div>
|
|
<div class="form-check">
|
|
<input class="form-check-input" type="checkbox" id="date" name="date" v-field="{ state: viewState, set: value => { options.date = value }, value: options?.date, form: "anonymize", options: {} }">
|
|
<label class="form-check-label" for="date">Dates</label>
|
|
</div>
|
|
<div class="form-check">
|
|
<input class="form-check-input" type="checkbox" id="origin" name="origin" v-field="{ state: viewState, set: value => { options.origin = value }, value: options?.origin, form: "anonymize", options: {} }">
|
|
<label class="form-check-label" for="origin">Project name</label>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<section class="paper-settings-section" v-show="detectedType">
|
|
<div class="paper-section-eyebrow">Expiration</div>
|
|
|
|
<div class="form-grid-2">
|
|
<div class="form-group">
|
|
<label class="paper-field-label" for="expiration">Strategy</label>
|
|
<select class="form-control" id="expiration" name="expiration" v-field="{ state: viewState, set: value => { options.expirationMode = value }, value: options?.expirationMode, form: "anonymize", options: {} }">
|
|
<option value="redirect">Redirect to GitHub when expired</option>
|
|
<option value="remove" selected>Remove when expired</option>
|
|
</select>
|
|
</div>
|
|
<div class="form-group">
|
|
<label class="paper-field-label" for="expirationDate">Expiration date</label>
|
|
<input class="form-control" type="date" name="expirationDate" id="expirationDate" required v-field="{ state: viewState, set: value => { options.expirationDate = value }, value: options?.expirationDate, form: "anonymize", options: {} }" :class="{'is-invalid': anonymize?.expirationDate?.invalid}" :min="(minExpirationDate)" :max="(maxExpirationDate)">
|
|
<div class="invalid-feedback" v-show="anonymize?.expirationDate?.errors?.min">Pick a date in the future.</div>
|
|
<div class="invalid-feedback" v-show="anonymize?.expirationDate?.errors?.max">Pick a date on or before {{ fmt?.date(maxExpirationDate) }}.</div>
|
|
<div class="invalid-feedback" v-show="anonymize?.expirationDate?.errors?.required || anonymize?.expirationDate?.errors?.date">Enter a valid expiration date.</div>
|
|
</div>
|
|
</div>
|
|
<small class="form-text text-muted" v-show="options?.expirationMode=='remove' && options?.expirationDate">After {{ fmt?.date(options?.expirationDate) }}, the content will be removed.</small>
|
|
<small class="form-text text-muted" v-show="options?.expirationMode=='redirect' && options?.expirationDate">After {{ fmt?.date(options?.expirationDate) }}, visitors will be redirected to GitHub.</small>
|
|
</section>
|
|
|
|
<section class="paper-settings-section" v-show="isUpdate && detectedType === 'repo'">
|
|
<div class="paper-section-eyebrow">Co-authors</div>
|
|
<p class="form-text text-muted" style="margin-bottom: 8px;">
|
|
Co-authors can view and edit these settings. They cannot delete the anonymization or manage co-authors.
|
|
</p>
|
|
|
|
<div class="form-group" v-show="role === 'owner' || role === 'admin'">
|
|
<label class="paper-field-label" for="coauthorSearch">Add a GitHub user</label>
|
|
<div style="position: relative;">
|
|
<input type="text" id="coauthorSearch" class="form-control" placeholder="Search GitHub username…" autocomplete="off" v-field="{ state: viewState, set: value => { coauthorSearch = value }, value: coauthorSearch, form: "anonymize", options: { debounce: 300 }, change: () => { searchCoauthors() } }">
|
|
<div class="dropdown-menu show" style="display: block; max-height: 220px; overflow-y: auto; width: 100%;" v-show="coauthorResults?.length > 0">
|
|
<a href="#" class="dropdown-item d-flex align-items-center" v-for="(u, index) in coauthorResults" @click.prevent="addCoauthor(u, $event)">
|
|
<img alt="" style="width: 22px; height: 22px; border-radius: 50%; margin-right: 8px;" :src="safeUrl((u?.photo))">
|
|
<span v-text="u?.username"></span>
|
|
</a>
|
|
</div>
|
|
</div>
|
|
<small class="form-text text-muted" v-show="coauthorError" v-text="coauthorError"></small>
|
|
</div>
|
|
|
|
<div class="coauthor-list">
|
|
<div class="coauthor-row d-flex align-items-center" style="padding: 6px 0; gap: 8px;" v-for="(c, index) in coauthors">
|
|
<img alt="" style="width: 24px; height: 24px; border-radius: 50%;" v-if="c?.photo" :src="safeUrl((c?.photo))">
|
|
<a target="_blank" v-text="c?.username" :href="safeUrl("https://github.com/" + (c?.username))"></a>
|
|
<span class="type-badge type-coauthor">Co-author</span>
|
|
<button type="button" class="btn btn-sm" style="margin-left: auto;" title="Remove co-author" v-show="role === 'owner' || role === 'admin'" @click="removeCoauthor(c)">
|
|
<i class="fas fa-times"></i>
|
|
</button>
|
|
</div>
|
|
<div class="form-text text-muted" v-show="!coauthors || coauthors?.length === 0">
|
|
No co-authors yet.
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<div class="alert alert-danger" role="alert" v-if="error" v-text="error"></div>
|
|
|
|
<div class="anonymize-submit-bar" v-show="detectedType">
|
|
<button type="submit" class="btn btn-ink" v-if="detectedType === 'repo' && !isUpdate" @click.prevent="submitForm($event, () => { anonymizeRepo($event) })">
|
|
<i class="fas fa-user-secret mr-1"></i> Anonymize Repository
|
|
</button>
|
|
<button type="submit" class="btn btn-ink" v-if="detectedType === 'repo' && isUpdate" @click.prevent="submitForm($event, () => { anonymizeRepo($event) })">
|
|
<i class="fas fa-save mr-1"></i> Update Repository
|
|
</button>
|
|
<button type="submit" class="btn btn-ink" v-if="detectedType === 'pr' && !isUpdate" @click.prevent="submitForm($event, () => { anonymizePullRequest($event) })">
|
|
<i class="fas fa-user-secret mr-1"></i> Anonymize Pull Request
|
|
</button>
|
|
<button type="submit" class="btn btn-ink" v-if="detectedType === 'pr' && isUpdate" @click.prevent="submitForm($event, () => { anonymizePullRequest($event) })">
|
|
<i class="fas fa-save mr-1"></i> Update Pull Request
|
|
</button>
|
|
<button type="submit" class="btn btn-ink" v-if="detectedType === 'gist' && !isUpdate" @click.prevent="submitForm($event, () => { anonymizeGist($event) })">
|
|
<i class="fas fa-user-secret mr-1"></i> Anonymize Gist
|
|
</button>
|
|
<button type="submit" class="btn btn-ink" v-if="detectedType === 'gist' && isUpdate" @click.prevent="submitForm($event, () => { anonymizeGist($event) })">
|
|
<i class="fas fa-save mr-1"></i> Update Gist
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
|
|
<!-- Preview column (right) -->
|
|
<div class="anonymize-preview-col" v-if="detectedType === 'repo' && html_readme">
|
|
<div class="anonymize-preview-head">
|
|
<span class="paper-eyebrow">Live preview</span>
|
|
<span class="anonymize-preview-sub">README with redactions applied</span>
|
|
</div>
|
|
<div class="anonymize-preview-body markdown-body body" v-html="sanitize(html_readme)"></div>
|
|
</div>
|
|
|
|
<div class="anonymize-preview-col" v-if="detectedType === 'gist' && details">
|
|
<div class="anonymize-preview-head">
|
|
<span class="paper-eyebrow">Live preview</span>
|
|
<span class="anonymize-preview-sub">Gist with redactions applied</span>
|
|
</div>
|
|
<div class="anonymize-preview-body">
|
|
<div class="d-flex w-100 justify-content-between align-items-center flex-wrap">
|
|
<h2 class="pr-title mb-1">
|
|
<span v-if="options?.title">{{ anonymizeGistContent(details?.gist?.description) || 'Untitled gist' }}</span>
|
|
<span class="badge" :class="{'badge-success': details?.gist?.isPublic, 'badge-secondary': !details?.gist?.isPublic}">
|
|
{{ details?.gist?.isPublic ? 'public' : 'secret' }}
|
|
</span>
|
|
</h2>
|
|
<small v-if="options?.date" v-text="fmt?.date(details?.gist?.updatedDate)"></small>
|
|
</div>
|
|
<small v-if="options?.origin">Gist ID: {{ details?.source?.gistId }}</small>
|
|
<small v-if="options?.username && details?.gist?.ownerLogin">By @{{ anonymizeGistContent(details?.gist?.ownerLogin) }}</small>
|
|
<div v-if="options?.content && previewGistFiles?.length">
|
|
<ul class="pr-comments mt-3">
|
|
<li class="pr-comment" v-for="(file, index) in previewGistFiles">
|
|
<div class="pr-comment-head">
|
|
<strong v-text="file?.filename"></strong>
|
|
<span class="pr-comment-date" v-if="file?.language">{{ file?.language }}</span>
|
|
</div>
|
|
<gist-file :file="file" :terms="terms" :options="options"></gist-file>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
<div v-if="options?.comments && details?.gist?.comments && details?.gist?.comments?.length">
|
|
<h3 class="paper-section-eyebrow mt-3">Comments</h3>
|
|
<ul class="pr-comments">
|
|
<li class="pr-comment" v-for="(comment, index) in details?.gist?.comments">
|
|
<div class="pr-comment-head">
|
|
<span class="pr-comment-author" v-if="options?.username">
|
|
<i class="far fa-user"></i> @<span v-text="anonymizeGistContent(comment?.author)"></span>
|
|
</span>
|
|
<span class="pr-comment-date" v-if="options?.date" v-text="fmt?.date(comment?.updatedDate)"></span>
|
|
</div>
|
|
<div class="pr-comment-body" v-if="options?.body">
|
|
<markdown :content="anonymizeGistContent(comment?.body)" :options="options" :terms="terms"></markdown>
|
|
</div>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="anonymize-preview-col" v-if="detectedType === 'pr' && details">
|
|
<div class="anonymize-preview-head">
|
|
<span class="paper-eyebrow">Live preview</span>
|
|
<span class="anonymize-preview-sub">Pull request with redactions applied</span>
|
|
</div>
|
|
<div class="anonymize-preview-body">
|
|
<div class="d-flex w-100 justify-content-between align-items-center flex-wrap">
|
|
<h2 class="pr-title mb-1">
|
|
<span v-if="options?.title">{{ anonymizePrContent(details?.pullRequest?.title) }}</span>
|
|
<span class="badge" :class="{'badge-success':details?.pullRequest?.merged, 'badge-warning':details?.pullRequest?.state=='open', 'badge-danger':details?.pullRequest?.state=='closed' && !details?.pullRequest?.merged}">
|
|
{{ fmt?.title(details?.pullRequest?.merged ? "merged" : details?.pullRequest?.state) }}
|
|
</span>
|
|
</h2>
|
|
<small v-if="options?.date" v-text="fmt?.date(details?.pullRequest?.updatedDate)"></small>
|
|
</div>
|
|
<small v-if="options?.origin">Pull Request on {{ details?.pullRequest?.baseRepositoryFullName }}</small>
|
|
<div class="pr-body shadow-sm p-3 mb-4 rounded" style="background: var(--paper-bg-alt)" v-if="options?.body">
|
|
<markdown :content="anonymizePrContent(details?.pullRequest?.body)" :options="options" :terms="terms"></markdown>
|
|
</div>
|
|
<nav class="paper-tabs" role="tablist" v-if="options?.diff || options?.comments">
|
|
<button class="paper-tab" type="button" role="tab" v-if="options?.diff" :class="{'active': prTabState?.active == 'diff'}" @click="prTabState.active = 'diff'">
|
|
<i class="fas fa-code"></i> Diff
|
|
</button>
|
|
<button class="paper-tab" type="button" role="tab" v-if="options?.comments" :class="{'active': prTabState?.active == 'comments'}" @click="prTabState.active = 'comments'">
|
|
<i class="far fa-comment-dots"></i>
|
|
<span v-text="fmt.plural(details?.pullRequest?.comments?.length, {'0': 'No comments', 'one': '1 comment', 'other': '{} comments'})"></span>
|
|
</button>
|
|
</nav>
|
|
<div class="paper-tab-content">
|
|
<div v-if="options?.diff && prTabState?.active == 'diff'">
|
|
<div class="pr-diff" v-html="sanitize(fmt?.diff(anonymizePrContent(details?.pullRequest?.diff)))"></div>
|
|
</div>
|
|
<div v-if="options?.comments && prTabState?.active == 'comments'">
|
|
<ul class="pr-comments">
|
|
<li class="pr-comment" v-for="(comment, index) in details?.pullRequest?.comments">
|
|
<div class="pr-comment-head">
|
|
<span class="pr-comment-author" v-if="options?.username">
|
|
<i class="far fa-user"></i> @<span v-text="anonymizePrContent(comment?.author)"></span>
|
|
</span>
|
|
<span class="pr-comment-date" v-if="options?.date" v-text="fmt?.date(comment?.updatedDate)"></span>
|
|
</div>
|
|
<div class="pr-comment-body" v-if="options?.body">
|
|
<markdown :content="anonymizePrContent(comment?.body)" :options="options" :terms="terms"></markdown>
|
|
</div>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|