Polish website UX: unify dashboards, clean up layout, modernize styling (#668)

This commit is contained in:
Thomas Durieux
2026-04-15 09:30:19 +02:00
committed by GitHub
parent 8198a4b44a
commit 261eaa8d79
9 changed files with 1378 additions and 1925 deletions
+1 -1
View File
File diff suppressed because one or more lines are too long
+357 -31
View File
@@ -193,18 +193,97 @@ a:hover {
} }
.rgba-gradient { .rgba-gradient {
background: -moz-linear-gradient(45deg, background: linear-gradient(135deg, var(--primary-bg), #2a2d4a);
rgba(51, 51, 51, 0.82),
rgba(13, 17, 198, 0.69) 100%);
background: -webkit-linear-gradient(45deg,
rgba(51, 51, 51, 0.82),
rgba(13, 17, 198, 0.69) 100%);
background: linear-gradient(to 45deg,
rgba(51, 51, 51, 0.82),
rgba(13, 17, 198, 0.69) 100%);
color: var(--header-color); color: var(--header-color);
} }
/* Hero section */
.hero-title {
font-size: 2.8rem;
font-weight: 700;
letter-spacing: -0.02em;
margin-bottom: 0;
}
.hero-subtitle {
font-size: 1.15rem;
opacity: 0.85;
max-width: 500px;
margin-left: auto;
margin-right: auto;
line-height: 1.5;
}
.btn-hero {
border: 2px solid rgba(255, 255, 255, 0.8);
color: #fff;
padding: 10px 28px;
font-size: 1rem;
font-weight: 500;
border-radius: 6px;
transition: background 0.2s, border-color 0.2s;
}
.btn-hero:hover {
background: rgba(255, 255, 255, 0.12);
border-color: #fff;
color: #fff;
}
/* Home content */
.home-content {
max-width: 900px;
}
.usage-steps {
font-size: 1.05rem;
line-height: 1.8;
padding-left: 1.2em;
}
.usage-example {
font-size: 0.9rem;
opacity: 0.7;
margin-top: 8px;
}
.featurette-lead {
font-size: 1.05rem;
line-height: 1.7;
opacity: 0.85;
}
/* Metrics grid */
.metrics-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 16px;
margin-bottom: 48px;
}
.metric-card {
text-align: center;
padding: 24px 16px;
border: 1px solid var(--border-color);
border-radius: 8px;
background: var(--admin-stat-bg);
}
.metric-value {
font-size: 2rem;
font-weight: 700;
line-height: 1.2;
letter-spacing: -0.02em;
}
.metric-label {
font-size: 0.8rem;
text-transform: uppercase;
letter-spacing: 0.5px;
opacity: 0.6;
margin-top: 4px;
}
.dropdown-menu, .dropdown-menu,
.body, .body,
.file-content, .file-content,
@@ -220,7 +299,11 @@ a:hover {
} }
.badge { .badge {
font-size: 45%; font-size: 75%;
font-weight: 500;
padding: 3px 8px;
border-radius: 10px;
vertical-align: middle;
} }
.warning-feedback { .warning-feedback {
@@ -549,11 +632,13 @@ loc .lang {
/* Space out the Bootstrap <hr> more */ /* Space out the Bootstrap <hr> more */
} }
/* Thin out the marketing headings */ /* Featurette headings */
.featurette-heading { .featurette-heading {
font-weight: 300; font-weight: 600;
line-height: 1; font-size: 1.5rem;
letter-spacing: -0.05rem; line-height: 1.3;
letter-spacing: -0.02em;
margin-bottom: 12px;
} }
.dark-mode .shadow { .dark-mode .shadow {
@@ -577,15 +662,7 @@ loc .lang {
} }
.dark-mode .rgba-gradient { .dark-mode .rgba-gradient {
background: -moz-linear-gradient(45deg, background: linear-gradient(135deg, #2a2d4a, #151728);
rgb(45 45 64 / 82%),
rgb(23 26 49) 100%);
background: -webkit-linear-gradient(45deg,
rgb(45 45 64 / 82%),
rgb(23 26 49) 100%);
background: linear-gradient(to 45deg,
rgb(45 45 64 / 82%),
rgb(23 26 49) 100%);
} }
@@ -626,6 +703,236 @@ code {
background: rgba(200, 100, 100, 0.5); background: rgba(200, 100, 100, 0.5);
} }
/* ===== Dashboard & List Styles ===== */
.dashboard-page {
max-width: 960px;
}
.dashboard-title {
font-size: 1.4rem;
font-weight: 600;
letter-spacing: -0.01em;
}
/* Quota bars */
.quota-row {
display: flex;
gap: 16px;
margin-bottom: 16px;
padding: 12px 16px;
background: var(--admin-stat-bg);
border: 1px solid var(--border-color);
border-radius: 8px;
}
.quota-item {
flex: 1;
min-width: 0;
}
.quota-header {
display: flex;
justify-content: space-between;
align-items: baseline;
margin-bottom: 4px;
}
.quota-label {
font-size: 0.75rem;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.3px;
opacity: 0.7;
}
.quota-value {
font-size: 0.8rem;
font-weight: 500;
font-variant-numeric: tabular-nums;
}
.quota-progress {
height: 6px;
border-radius: 3px;
background: var(--hover-bg-color);
}
.quota-progress .progress-bar {
border-radius: 3px;
}
/* Filter chips */
.filter-chip {
display: inline-flex;
align-items: center;
gap: 4px;
padding: 3px 10px;
font-size: 0.8rem;
font-weight: 500;
background: var(--primary-bg);
color: var(--primary-color);
border-radius: 12px;
line-height: 1.4;
}
.filter-chip-close {
display: inline-flex;
align-items: center;
justify-content: center;
background: none;
border: none;
color: inherit;
opacity: 0.7;
cursor: pointer;
padding: 0;
margin-left: 2px;
font-size: 1rem;
line-height: 1;
}
.filter-chip-close:hover {
opacity: 1;
}
/* Repository/PR list */
.repo-list {
list-style: none;
padding: 0;
margin: 0;
}
.repo-list-item {
display: flex;
align-items: flex-start;
justify-content: space-between;
padding: 16px 0;
border-bottom: 1px solid var(--border-color);
gap: 12px;
}
.repo-list-item:first-child {
border-top: 1px solid var(--border-color);
}
.repo-list-item.repo-inactive {
opacity: 0.6;
}
.repo-list-item-content {
flex: 1;
min-width: 0;
}
.repo-list-item-header {
display: flex;
align-items: center;
gap: 8px;
flex-wrap: wrap;
margin-bottom: 4px;
}
.repo-name {
font-size: 1.1rem;
font-weight: 600;
color: var(--link-color);
word-break: break-word;
}
.repo-name:hover {
color: var(--link-hover-color);
}
.repo-source {
font-size: 0.85rem;
opacity: 0.8;
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 6px;
}
.repo-date {
opacity: 0.7;
}
.repo-meta {
display: flex;
flex-wrap: wrap;
gap: 12px;
margin-top: 8px;
font-size: 0.8rem;
opacity: 0.7;
}
.repo-meta span {
display: inline-flex;
align-items: center;
gap: 4px;
white-space: nowrap;
}
.repo-list-item-actions {
flex-shrink: 0;
padding-top: 2px;
}
.repo-list-empty {
display: flex;
flex-direction: column;
align-items: center;
gap: 8px;
padding: 48px 20px;
opacity: 0.5;
font-size: 0.95rem;
list-style: none;
border-bottom: 1px solid var(--border-color);
border-top: 1px solid var(--border-color);
}
.repo-list-empty i {
font-size: 1.8rem;
}
/* Type badges (Repo / PR) */
.type-badge {
display: inline-flex;
align-items: center;
gap: 3px;
font-size: 0.7rem;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.3px;
padding: 2px 8px;
border-radius: 4px;
white-space: nowrap;
}
.type-badge.type-repo {
background: rgba(74, 80, 123, 0.12);
color: var(--primary-bg);
}
.type-badge.type-pr {
background: rgba(40, 167, 69, 0.12);
color: #28a745;
}
.dark-mode .type-badge.type-repo {
background: rgba(84, 155, 245, 0.15);
color: #549bf5;
}
.dark-mode .type-badge.type-pr {
background: rgba(40, 167, 69, 0.15);
color: #5cb85c;
}
/* Type filter button group */
.btn-group .btn:not(.btn-primary) {
background: var(--hover-bg-color);
}
/* ===== Anonymize Page Layout ===== */ /* ===== Anonymize Page Layout ===== */
.anonymize-page .sidePanel { .anonymize-page .sidePanel {
@@ -730,12 +1037,19 @@ code {
h3 { font-size: 1.15rem; } h3 { font-size: 1.15rem; }
/* Dashboard list items */ /* Dashboard list items */
.col-12.d-flex.px-0.py-3 { .repo-list-item {
flex-direction: column; flex-direction: column;
} }
.col-12.d-flex.px-0.py-3 > .d-flex:last-child { .repo-list-item-actions {
margin-top: 8px; align-self: flex-start;
}
/* Quota row stacks on mobile */
.quota-row {
flex-direction: column;
gap: 10px;
padding: 10px 12px;
} }
/* Dropdown menus right-aligned on mobile */ /* Dropdown menus right-aligned on mobile */
@@ -788,8 +1102,7 @@ code {
/* Badge readability */ /* Badge readability */
.badge { .badge {
font-size: 55%; font-size: 70%;
padding: 3px 6px;
} }
/* Search & filter toolbar */ /* Search & filter toolbar */
@@ -810,9 +1123,22 @@ code {
display: none !important; display: none !important;
} }
/* Metrics cards on home */ /* Metrics cards on mobile */
.col-md-4 h3 { .metrics-grid {
font-size: 1.1rem; grid-template-columns: 1fr;
gap: 10px;
}
.metric-value {
font-size: 1.5rem;
}
.hero-title {
font-size: 1.8rem;
}
.hero-subtitle {
font-size: 0.95rem;
} }
/* ---- Anonymize page mobile ---- */ /* ---- Anonymize page mobile ---- */
+220 -347
View File
@@ -1,382 +1,217 @@
<div class="container-fluid h-100 anonymize-page"> <div class="container-fluid h-100 anonymize-page">
<div class="row h-100 flex-column flex-md-row"> <div class="row h-100 flex-column flex-md-row">
<div <div class="col-md sidePanel shadow overflow-auto anonymize-form-col">
class="col-md sidePanel shadow overflow-auto anonymize-form-col" <div class="p-0 py-2 m-auto" ng-class="{'card': !sourceUrl, 'container': sourceUrl}">
> <form class="form needs-validation" ng-class="{'card-body': !sourceUrl}" name="anonymize" novalidate>
<div <h3 class="card-title mb-3">Anonymize</h3>
class="p-0 py-2 m-auto"
ng-class="{'card': !repoUrl,'container': repoUrl}" <!-- Source URL (auto-detects repo vs PR) -->
> <div class="form-group mb-2">
<form
class="form needs-validation"
ng-class="{'card-body': !repoUrl}"
name="anonymize"
novalidate
>
<h3 class="card-title mb-3">Anonymize your repository</h3>
<!-- repoUrl -->
<div class="form-group mb-0">
<input <input
type="text" type="text"
class="form-control" class="form-control"
name="repoUrl" name="sourceUrl"
id="repoUrl" id="sourceUrl"
ng-class="{'is-invalid': anonymize.repoUrl.$invalid}" ng-class="{'is-invalid': anonymize.sourceUrl.$invalid}"
ng-model="repoUrl" ng-model="sourceUrl"
placeholder="URL of your GitHub repository" placeholder="Paste a GitHub repo or pull request URL"
ng-model-options="{ debounce: {default: 1000, blur: 0, click: 0}, updateOn: 'default blur click' }" ng-model-options="{ debounce: {default: 1000, blur: 0, click: 0}, updateOn: 'default blur click' }"
ng-change="repoSelected()" ng-change="urlSelected()"
/> />
<div <div class="invalid-feedback" ng-show="anonymize.sourceUrl.$error.github">
class="invalid-feedback" Please provide a valid GitHub URL, e.g., https://github.com/owner/repo or https://github.com/owner/repo/pull/123
ng-show="anonymize.repoUrl.$error.github"
>
Please provide a valid Github url, e.g.,
https://github.com/owner/repo.
</div> </div>
<div <div class="invalid-feedback" ng-show="anonymize.sourceUrl.$error.access">
class="invalid-feedback" Not accessible. The organization may restrict access.
ng-show="anonymize.repoUrl.$error.access"
>
{{repoUrl}} is not accessible. Some organizations are restricting
the access to the repositories.
</div> </div>
<div <div class="invalid-feedback" ng-show="anonymize.sourceUrl.$error.missing">
class="invalid-feedback" Does not exist or is not accessible.
ng-show="anonymize.repoUrl.$error.missing"
>
{{repoUrl}} does not exist or is not accessible
</div> </div>
<div <div class="invalid-feedback" ng-show="anonymize.sourceUrl.$error.used">
class="invalid-feedback" Already anonymized.
ng-show="anonymize.repoUrl.$error.used"
>
{{repoUrl}} is already anonymized
</div> </div>
<small class="form-text text-muted" ng-hide="sourceUrl">
Paste a repository URL to anonymize a repo, or a pull request URL to anonymize a PR.
</small>
</div> </div>
<div ng-show="repoUrl">
<!-- Branch --> <!-- Type indicator -->
<div class="form-group"> <div class="mb-3" ng-show="detectedType && sourceUrl">
<label for="branch">Branch</label> <span class="type-badge" ng-class="{'type-repo': detectedType === 'repo', 'type-pr': detectedType === 'pr'}">
<div class="input-group mb-1"> <i ng-class="{'fas fa-code-branch': detectedType === 'repo', 'fas fa-code-merge': detectedType === 'pr'}"></i>
<select {{detectedType === 'repo' ? 'Repository' : 'Pull Request'}}
class="form-control" </span>
id="branch" </div>
name="branch"
ng-model="source.branch" <div ng-show="sourceUrl">
> <!-- ==== REPO-SPECIFIC FIELDS ==== -->
<option <div ng-show="detectedType === 'repo'">
ng-repeat="b in branches" <div class="form-group">
ng-bind="b.name" <label for="branch">Branch</label>
value="{{b.name}}" <div class="input-group mb-1">
></option> <select class="form-control" id="branch" name="branch" ng-model="source.branch">
</select> <option ng-repeat="b in branches" ng-bind="b.name" value="{{b.name}}"></option>
<div class="input-group-append"> </select>
<button <div class="input-group-append">
class="btn btn-outline-secondary" <button class="btn btn-outline-secondary" ng-click="getBranches(true)" title="Refresh" data-toggle="tooltip" data-placement="bottom">
ng-click="getBranches(true)" <i class="fa fa-undo"></i>
title="Refresh!" </button>
data-toggle="tooltip" </div>
data-placement="bottom"
>
<i class="fa fa-undo"></i>
</button>
</div> </div>
<small class="form-text text-muted">The branch to anonymize</small>
</div> </div>
<small class="form-text text-muted">
The branch to anonymize
</small>
</div> </div>
<!-- Auto update (both types) -->
<div class="form-group"> <div class="form-group">
<div class="form-check"> <div class="form-check">
<input <input class="form-check-input" type="checkbox" id="update" name="update" ng-model="options.update" />
class="form-check-input"
type="checkbox"
id="update"
name="update"
ng-model="options.update"
/>
<label class="form-check-label" for="update">Auto update</label> <label class="form-check-label" for="update">Auto update</label>
<small id="updateHelp" class="form-text text-muted" <small class="form-text text-muted">Automatically update with the latest changes.</small>
>Automatically update the anonymized repository with the
latest commit of the repository. The repository is updated
once per hour maximum.</small
>
</div> </div>
</div> </div>
<!-- Commit -->
<div class="form-group"> <!-- Commit (repo only) -->
<div class="form-group" ng-show="detectedType === 'repo'">
<label for="commit">Commit</label> <label for="commit">Commit</label>
<input <input class="form-control" id="commit" name="commit" pattern="[a-fA-Z0-9]{6,}" ng-model="source.commit" required ng-class="{'is-invalid': anonymize.commit.$invalid}" />
class="form-control" <small class="form-text text-muted">The SHA of the commit to anonymize.</small>
id="commit" <div class="invalid-feedback" ng-show="anonymize.commit.$error.pattern || anonymize.commit.$error.required">
name="commit" The commit SHA is not valid.
pattern="[a-fA-Z0-9]{6,}"
ng-model="source.commit"
required
ng-class="{'is-invalid': anonymize.commit.$invalid}"
/>
<small class="form-text text-muted"
>The SHA of the commit to anonymize.</small
>
<div
class="invalid-feedback"
ng-show="anonymize.commit.$error.pattern || anonymize.commit.$error.required"
>
The commit SHA is not valid. It should respect this pattern
[a-fA-Z0-9]{6,}.
</div> </div>
</div> </div>
<h5 class="anonymize-section-title"> <h5 class="anonymize-section-title">
<i class="fas fa-chalkboard-teacher"></i> Conference ID <i class="fas fa-chalkboard-teacher"></i> Conference ID
</h5> </h5>
<!-- Conference -->
<div class="form-group"> <div class="form-group">
<label for="conference" <label for="conference">Conference ID <span class="text-muted">(Optional)</span></label>
>Conference ID <span class="text-muted">(Optional)</span></label <input class="form-control" id="conference" name="conference" ng-model="conference" ng-class="{'is-invalid': anonymize.conference.$invalid}" />
> <small class="form-text text-muted" ng-show="conference_data">
<input <a ng-href="{{conference_data.url}}" target="_blank">{{conference_data.name}}</a> will expire on {{conference_data.endDate | date}}.
class="form-control"
id="conference"
name="conference"
ng-model="conference"
ng-class="{'is-invalid': anonymize.conference.$invalid}"
/>
<small class="form-text text-muted" ng-show="conference_data"
><a ng-href="{{conference_data.url}}" target="_target"
>{{conference_data.name}}</a
>
will expire on {{conference_data.endDate | date}}.</small
>
<div
class="invalid-feedback"
ng-show="anonymize.conference.$error.activated"
>
The conference is not activated.
</div>
<small class="form-text text-muted" ng-show="!conference_data">
Use the Conference ID that your conference provided you. This
will update automatically the anonymization options based on the
conference preferences.
</small> </small>
<div class="invalid-feedback" ng-show="anonymize.conference.$error.activated">The conference is not activated.</div>
<small class="form-text text-muted" ng-show="!conference_data">Conference ID updates anonymization options automatically.</small>
</div> </div>
<h5 class="anonymize-section-title"> <h5 class="anonymize-section-title">
<i class="fas fa-shield-alt"></i> Anonymization Options <i class="fas fa-shield-alt"></i> Anonymization Options
</h5> </h5>
<!-- Repo ID -->
<div class="form-group"> <!-- Anonymized ID: repo -->
<label for="repoId">Anonymized repository id</label> <div class="form-group" ng-show="detectedType === 'repo'">
<input <label for="repoId">Anonymized repository ID</label>
type="text" <input type="text" class="form-control" name="repoId" id="repoId" ng-class="{'is-invalid': anonymize.repoId.$invalid}" ng-model="repoId" ng-model-options="{ debounce: {default: 1000, blur: 0, click: 0}, updateOn: 'default blur click' }" />
class="form-control" <small class="form-text text-muted">URL: https://anonymous.4open.science/r/{{repoId}}</small>
name="repoId" <div class="invalid-feedback" ng-show="anonymize.repoId.$error.format">ID can only contain letters and numbers.</div>
id="repoId" <div class="invalid-feedback" ng-show="anonymize.repoId.$error.used">{{repoId}} is already used.</div>
ng-class="{'is-invalid': anonymize.repoId.$invalid}"
ng-model="repoId"
ng-model-options="{ debounce: {default: 1000, blur: 0, click: 0}, updateOn: 'default blur click' }"
/>
<small id="idHelp" class="form-text text-muted"
>Id used in the url:
https://anonymous.4open.science/r/{{repoId}}</small
>
<div
class="invalid-feedback"
ng-show="anonymize.repoId.$error.format"
>
Repository id can only contain letters and numbers
</div>
<div
class="invalid-feedback"
ng-show="anonymize.repoId.$error.used"
>
{{repoId}} is already used
</div>
</div> </div>
<!-- Anonymized ID: PR -->
<div class="form-group" ng-show="detectedType === 'pr'">
<label for="pullRequestId">Anonymized pull request ID</label>
<input type="text" class="form-control" name="pullRequestId" id="pullRequestId" ng-class="{'is-invalid': anonymize.pullRequestId.$invalid}" ng-model="pullRequestId" ng-model-options="{ debounce: {default: 1000, blur: 0, click: 0}, updateOn: 'default blur click' }" />
<small class="form-text text-muted">URL: https://anonymous.4open.science/pr/{{pullRequestId}}</small>
<div class="invalid-feedback" ng-show="anonymize.pullRequestId.$error.format">ID can only contain letters and numbers.</div>
<div class="invalid-feedback" ng-show="anonymize.pullRequestId.$error.used">{{pullRequestId}} is already used.</div>
</div>
<!-- Terms --> <!-- Terms -->
<div class="form-group"> <div class="form-group">
<label for="terms">Terms to anonymize</label> <label for="terms">Terms to anonymize</label>
<textarea <textarea class="form-control" id="terms" name="terms" rows="3" ng-model="terms" ng-model-options="{ debounce: 250 }" ng-class="{'is-invalid': anonymize.terms.$invalid}"></textarea>
class="form-control" <small class="form-text text-muted">One term per line (regex). Each term will be replaced by {{site_options.ANONYMIZATION_MASK}}-[N].</small>
id="terms" <div class="warning-feedback" ng-show="anonymize.terms.$error.regex">Regex characters detected. Escape them if unintentional.</div>
name="terms" <div class="invalid-feedback" ng-show="anonymize.terms.$error.format">Terms are in an invalid format.</div>
rows="3"
ng-model="terms"
ng-model-options="{ debounce: 250 }"
ng-class="{'is-invalid': anonymize.terms.$invalid}"
></textarea>
<small id="termsHelp" class="form-text text-muted">
One term per line. A term is a RegEx! Each term will be replaced
by {{site_options.ANONYMIZATION_MASK}}-[Line Number].
</small>
<div
class="warning-feedback"
ng-show="anonymize.terms.$error.regex"
>
We identify that you are using some regex characters, if it was
not on purpose, please escape them.
</div>
<div
class="invalid-feedback"
ng-show="anonymize.terms.$error.format"
>
Terms are in an invalid format.
</div>
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="expiration">Expiration strategy</label> <label for="expiration">Expiration strategy</label>
<select <select class="form-control" id="expiration" name="expiration" ng-model="options.expirationMode">
class="form-control"
id="expiration"
name="expiration"
ng-model="options.expirationMode"
>
<option value="never" selected>Never expire</option> <option value="never" selected>Never expire</option>
<option value="redirect"> <option value="redirect">Redirect to GitHub when expired</option>
Redirect to GitHub when expired
</option>
<option value="remove">Remove when expired</option> <option value="remove">Remove when expired</option>
</select> </select>
<small class="form-text text-muted"
>Define the expiration strategy for the anonymized
repository.</small
>
</div> </div>
<div <div class="form-group" ng-hide="options.expirationMode=='never'">
class="form-group" <label for="expirationDate">Expiration date</label>
id="expiration-date-form" <input class="form-control" type="date" name="expirationDate" id="expirationDate" ng-model="options.expirationDate" />
ng-hide="options.expirationMode=='never'" <small class="form-text text-muted" ng-show="options.expirationMode=='remove'">After {{options.expirationDate | date}}, the content will be removed.</small>
> <small class="form-text text-muted" ng-show="options.expirationMode=='redirect'">After {{options.expirationDate | date}}, visitors will be redirected to GitHub.</small>
<label for="expirationDate"
>Expiration date of the anonymized repository</label
>
<input
class="form-control"
type="date"
name="expirationDate"
id="expirationDate"
ng-model="options.expirationDate"
/>
<small
class="form-text text-muted"
ng-show="options.expirationMode=='remove'"
>After {{options.expirationDate | date}}, the repository will be
removed and the visitor will not be able to see the content of
the repository.</small
>
<small
class="form-text text-muted"
ng-show="options.expirationMode=='redirect'"
>After {{options.expirationDate | date}}, the visitors of the
anonymized repository will be redirected to {{repoUrl}}.</small
>
</div> </div>
<div class="accordion mb-3" id="options"> <!-- Advanced options -->
<div class="accordion mb-3" id="advancedOptions">
<div class="card"> <div class="card">
<div class="card-header" id="headingOne"> <div class="card-header" id="headingAdvanced">
<h2 class="mb-0"> <h2 class="mb-0">
<button <button class="btn btn-block text-left" type="button" data-toggle="collapse" data-target="#collapseAdvanced" aria-expanded="true" aria-controls="collapseAdvanced">
class="btn btn-block text-left"
type="button"
data-toggle="collapse"
data-target="#collapseOne"
aria-expanded="true"
aria-controls="collapseOne"
>
<i class="fas fa-cog mr-1"></i> Advanced options <i class="fas fa-cog mr-1"></i> Advanced options
</button> </button>
</h2> </h2>
</div> </div>
<div id="collapseAdvanced" class="collapse show" aria-labelledby="headingAdvanced" data-parent="#advancedOptions">
<div
id="collapseOne"
class="collapse show"
aria-labelledby="headingOne"
data-parent="#options"
>
<div class="card-body"> <div class="card-body">
<div class="form-group"> <!-- Shared options -->
<div class="form-group mb-0">
<div class="form-check"> <div class="form-check">
<input <input class="form-check-input" type="checkbox" id="link" name="link" ng-model="options.link" />
class="form-check-input" <label class="form-check-label" for="link">Keep links</label>
type="checkbox" <small class="form-text text-muted">Keep or remove all the links.</small>
id="link"
name="link"
ng-model="options.link"
/>
<label class="form-check-label" for="link"
>Keep links</label
>
<small id="linkHelp" class="form-text text-muted"
>Keep or remove all the links.</small
>
</div> </div>
<div class="form-check"> <div class="form-check">
<input <input class="form-check-input" type="checkbox" id="image" name="image" ng-model="options.image" />
class="form-check-input" <label class="form-check-label" for="image">Display images</label>
type="checkbox" <small class="form-text text-muted">Images are not anonymized.</small>
id="image"
name="image"
ng-model="options.image"
/>
<label class="form-check-label" for="image"
>Display images</label
>
<small id="imageHelp" class="form-text text-muted"
>Images are not anonymized</small
>
</div> </div>
<div class="form-check">
<input <!-- Repo-specific options -->
class="form-check-input" <div ng-show="detectedType === 'repo'">
type="checkbox" <div class="form-check">
id="pdf" <input class="form-check-input" type="checkbox" id="pdf" name="pdf" ng-model="options.pdf" />
name="pdf" <label class="form-check-label" for="pdf">Display PDFs</label>
ng-model="options.pdf" </div>
/> <div class="form-check">
<label class="form-check-label" for="pdf" <input class="form-check-input" type="checkbox" id="notebook" name="notebook" ng-model="options.notebook" />
>Display PDFs</label <label class="form-check-label" for="notebook">Display Notebooks</label>
> </div>
<small id="pdfHelp" class="form-text text-muted" <div class="form-check">
>PDF are not anonymized</small <input class="form-check-input" type="checkbox" id="page" name="page" ng-model="options.page" ng-disabled="!details.hasPage" />
> <label class="form-check-label" for="page">GitHub Pages</label>
<small class="form-text text-muted">Enable anonymized GitHub Pages at https://anonymous.4open.science/w/{{repoId}}</small>
</div>
</div> </div>
<div class="form-check">
<input <!-- PR-specific options -->
class="form-check-input" <div ng-show="detectedType === 'pr'">
type="checkbox" <div class="form-check">
id="notebook" <input class="form-check-input" type="checkbox" id="date" name="date" ng-model="options.date" />
name="notebook" <label class="form-check-label" for="date">Display dates</label>
ng-model="options.notebook" </div>
/> <div class="form-check">
<label class="form-check-label" for="notebook" <input class="form-check-input" type="checkbox" id="username" name="username" ng-model="options.username" />
>Display Notebooks</label <label class="form-check-label" for="username">Display username</label>
> </div>
</div> <div class="form-check">
</div> <input class="form-check-input" type="checkbox" id="comments" name="comments" ng-model="options.comments" />
<div class="form-group"> <label class="form-check-label" for="comments">Display comments</label>
<div class="form-check"> </div>
<input <div class="form-check">
class="form-check-input" <input class="form-check-input" type="checkbox" id="diff" name="diff" ng-model="options.diff" />
type="checkbox" <label class="form-check-label" for="diff">Display diff</label>
id="page" </div>
name="page" <div class="form-check">
ng-model="options.page" <input class="form-check-input" type="checkbox" id="origin" name="origin" ng-model="options.origin" />
ng-disabled="!details.hasPage" <label class="form-check-label" for="origin">Display project name</label>
/> </div>
<label class="form-check-label" for="page" <div class="form-check">
>Github page</label <input class="form-check-input" type="checkbox" id="title" name="title" ng-model="options.title" />
> <label class="form-check-label" for="title">Display PR title</label>
<small id="pageHelp" class="form-text text-muted" </div>
>Enable anonymized Github pages. It currently only <div class="form-check">
supported for Github pages that are defined in the <input class="form-check-input" type="checkbox" id="body" name="body" ng-model="options.body" />
same branch. It will be available at <label class="form-check-label" for="body">Display PR body</label>
https://anonymous.4open.science/w/{{repoId}}</small </div>
>
</div> </div>
</div> </div>
</div> </div>
@@ -384,39 +219,77 @@
</div> </div>
</div> </div>
</div> </div>
<div
class="alert alert-danger" <div class="alert alert-danger" role="alert" ng-if="error" ng-bind="error"></div>
role="alert"
ng-if="error" <div class="anonymize-submit-bar" ng-show="sourceUrl && detectedType">
ng-bind="error" <!-- Repo submit -->
></div> <button type="submit" class="btn btn-primary btn-block" ng-click="anonymizeRepo($event)" ng-if="detectedType === 'repo' && !isUpdate">
<div class="anonymize-submit-bar" ng-show="repoUrl"> <i class="fas fa-user-secret mr-1"></i> Anonymize Repository
<button
id="submit"
type="submit"
class="btn btn-primary btn-block"
ng-click="anonymizeRepo($event)"
ng-if="!isUpdate"
>
<i class="fas fa-user-secret mr-1"></i> Anonymize
</button> </button>
<button <button type="submit" class="btn btn-primary btn-block" ng-click="anonymizeRepo($event)" ng-if="detectedType === 'repo' && isUpdate">
id="submit" <i class="fas fa-save mr-1"></i> Update Repository
type="submit" </button>
class="btn btn-primary btn-block" <!-- PR submit -->
ng-click="updateRepo($event)" <button type="submit" class="btn btn-primary btn-block" ng-click="anonymizePullRequest($event)" ng-if="detectedType === 'pr' && !isUpdate">
ng-if="isUpdate" <i class="fas fa-user-secret mr-1"></i> Anonymize Pull Request
> </button>
<i class="fas fa-save mr-1"></i> Update <button type="submit" class="btn btn-primary btn-block" ng-click="anonymizePullRequest($event)" ng-if="detectedType === 'pr' && isUpdate">
<i class="fas fa-save mr-1"></i> Update Pull Request
</button> </button>
</div> </div>
</form> </form>
</div> </div>
</div> </div>
<div
class="col-md-8 p-2 overflow-auto markdown-body body anonymize-preview-col" <!-- Preview: repo README -->
ng-bind-html="html_readme" <div class="col-md-8 p-2 overflow-auto markdown-body body anonymize-preview-col" ng-bind-html="html_readme" ng-if="detectedType === 'repo' && html_readme"></div>
ng-if="html_readme"
></div> <!-- Preview: PR content -->
<div class="col-md-8 p-2 overflow-auto anonymize-preview-col" ng-if="detectedType === 'pr' && details">
<div class="d-flex w-100 justify-content-between align-items-center flex-wrap">
<h2 class="pr-title mb-1">
<span ng-if="options.title">{{anonymizePrContent(details.pullRequest.title)}}</span>
<span class="badge" ng-class="{'badge-success':details.pullRequest.merged, 'badge-warning':details.pullRequest.state=='open', 'badge-danger':details.pullRequest.state=='closed' && !details.pullRequest.merged}">
{{details.pullRequest.merged ? "merged" : details.pullRequest.state | title}}
</span>
</h2>
<small ng-bind="details.pullRequest.updatedDate | date" ng-if="options.date"></small>
</div>
<small ng-if="options.origin">Pull Request on {{details.pullRequest.baseRepositoryFullName}}</small>
<div class="pr-body shadow-sm p-3 mb-4 rounded" style="background: var(--sidebar-bg-color)" ng-if="options.body">
<markdown content="anonymizePrContent(details.pullRequest.body)" options="options" terms="terms"></markdown>
</div>
<ul class="nav nav-tabs" id="prTabs" role="tablist">
<li class="nav-item" role="presentation" ng-if="options.diff">
<button class="nav-link active" id="pills-diff-tab" data-toggle="pill" data-target="#pills-diff" type="button" role="tab">Diff</button>
</li>
<li class="nav-item" role="presentation" ng-if="options.comments">
<button class="nav-link" ng-class="{'active':!options.diff}" id="pills-comments-tab" data-toggle="pill" data-target="#pills-comments" type="button" role="tab">
<ng-pluralize count="details.pullRequest.comments.length" when="{'0': 'No comment', 'one': 'One Comment', 'other': '{} Comments'}"></ng-pluralize>
</button>
</li>
</ul>
<div class="tab-content" id="pills-tabContent">
<div class="tab-pane show active" id="pills-diff" role="tabpanel" ng-if="options.diff">
<div class="pr-diff shadow-sm p-3 mb-4 rounded" style="background: var(--sidebar-bg-color)">
<pre style="overflow-x: auto"><code ng-bind-html="anonymizePrContent(details.pullRequest.diff) | diff"></code></pre>
</div>
</div>
<div class="tab-pane" ng-class="{'show active':!options.diff}" id="pills-comments" role="tabpanel" ng-if="options.comments">
<ul class="pr-comments list-group">
<li class="pr-comment list-group-item" ng-repeat="comment in details.pullRequest.comments">
<div class="d-flex w-100 justify-content-between flex-wrap">
<h5 class="mb-1" ng-if="options.username">@{{anonymizePrContent(comment.author)}}</h5>
<small ng-bind="comment.updatedDate | date" ng-if="options.date"></small>
</div>
<p class="mb-1">
<markdown class="pr-comment-body" ng-if="options.body" content="anonymizePrContent(comment.body)" options="options" terms="terms"></markdown>
</p>
</li>
</ul>
</div>
</div>
</div>
</div> </div>
</div> </div>
+219 -352
View File
@@ -1,413 +1,280 @@
<div class="container page"> <div class="container page dashboard-page">
<div class="row"> <div class="row">
<span class="border-bottom color-border-secondary py-3 w-100"> <div class="w-100 py-3">
<div class="d-flex align-items-center w-100"> <!-- Header row: title + action button -->
<form class="w-100" aria-label="Repositories" accept-charset="UTF-8"> <div class="d-flex align-items-center justify-content-between mb-3">
<div class=""> <h2 class="dashboard-title mb-0">Dashboard</h2>
<div class="w-100 mb-2"> <a href="/anonymize" class="btn btn-primary btn-sm">
<input <i class="fa fa-plus-circle" aria-hidden="true"></i> Anonymize
type="search" </a>
id="search" </div>
class="form-control"
aria-label="Find a repository…" <!-- Quota bars -->
placeholder="Find a repository…" <div class="quota-row" ng-show="quota">
autocomplete="off" <div class="quota-item">
ng-model="search" <div class="quota-header">
/> <span class="quota-label">Repositories</span>
<span class="quota-value" ng-show="quota">{{quota.repository.used | number}}/{{quota.repository.total}}</span>
</div>
<div class="progress quota-progress">
<div
class="progress-bar"
ng-class="{'bg-success': quota.repository.percent < 25 || quota.repository.total == 0, 'bg-danger': quota.repository.percent > 95 && quota.repository.total > 0, 'bg-warning': quota.repository.percent > 75 && quota.repository.total > 0 }"
role="progressbar"
style="width: {{quota.repository.percent}}%;"
aria-valuenow="{{quota.repository.used}}"
aria-valuemin="0"
aria-valuemax="{{quota.repository.total}}"
></div>
</div>
</div>
<div class="quota-item">
<div class="quota-header">
<span class="quota-label">Storage</span>
<span class="quota-value" ng-show="quota">{{quota.storage.used | humanFileSize}}/{{quota.storage.total | humanFileSize}}</span>
</div>
<div class="progress quota-progress">
<div
class="progress-bar"
ng-class="{'bg-success': quota.storage.percent < 25 || quota.storage.total == 0, 'bg-danger': quota.storage.percent > 95 && quota.storage.total > 0, 'bg-warning': quota.storage.percent > 75 && quota.storage.total > 0 }"
role="progressbar"
style="width: {{quota.storage.percent}}%;"
aria-valuenow="{{quota.storage.used}}"
aria-valuemin="0"
aria-valuemax="{{quota.storage.total}}"
></div>
</div>
</div>
<div class="quota-item">
<div class="quota-header">
<span class="quota-label">Files</span>
<span class="quota-value" ng-show="quota">{{quota.file.used | number}}/{{quota.file.total || "&#8734;"}}</span>
</div>
<div class="progress quota-progress">
<div
class="progress-bar"
ng-class="{'bg-success': quota.file.percent < 25 || quota.file.total == 0, 'bg-danger': quota.file.percent > 95 && quota.file.total > 0, 'bg-warning': quota.file.percent > 75 && quota.file.total > 0 }"
role="progressbar"
style="width: {{quota.file.percent}}%;"
aria-valuenow="{{quota.file.used}}"
aria-valuemin="0"
aria-valuemax="{{quota.file.total}}"
></div>
</div>
</div>
</div>
<!-- Search + filters row -->
<form class="w-100" aria-label="Dashboard" accept-charset="UTF-8">
<div class="d-flex flex-column flex-md-row align-items-md-center" style="gap: 8px">
<div class="flex-grow-1">
<input
type="search"
id="search"
class="form-control"
aria-label="Search..."
placeholder="Search..."
autocomplete="off"
ng-model="search"
/>
</div>
<div class="d-flex flex-wrap" style="gap: 6px">
<!-- Type filter -->
<div class="btn-group btn-group-sm" role="group">
<button type="button" class="btn" ng-class="{'btn-primary': typeFilter === 'all'}" ng-click="typeFilter = 'all'">All</button>
<button type="button" class="btn" ng-class="{'btn-primary': typeFilter === 'repo'}" ng-click="typeFilter = 'repo'">Repos</button>
<button type="button" class="btn" ng-class="{'btn-primary': typeFilter === 'pr'}" ng-click="typeFilter = 'pr'">PRs</button>
</div> </div>
<div class="d-flex flex-wrap"> <div class="dropdown">
<div class="dropdown mt-1 mt-lg-0 mr-1"> <button
<button class="btn btn-sm dropdown-toggle"
class="btn btn-secondary dropdown-toggle" type="button"
type="button" id="dropdownSort"
id="dropdownSort" data-toggle="dropdown"
data-toggle="dropdown" aria-haspopup="true"
aria-haspopup="true" aria-expanded="false"
aria-expanded="false" >
> Sort
Sort </button>
</button> <div class="dropdown-menu" aria-labelledby="dropdownSort">
<div class="dropdown-menu" aria-labelledby="dropdownSort"> <h6 class="dropdown-header">Select order</h6>
<h6 class="dropdown-header">Select order</h6> <div class="form-check dropdown-item">
<div class="form-check dropdown-item"> <input class="form-check-input" type="radio" name="sort" id="sortFullName" value="_name" ng-model="orderBy" />
<input <label class="form-check-label" for="sortFullName">Name</label>
class="form-check-input" </div>
type="radio" <div class="form-check dropdown-item">
name="sort" <input class="form-check-input" type="radio" name="sort" id="sortAnonymizeDate" value="-anonymizeDate" ng-model="orderBy" />
id="sortFullName" <label class="form-check-label" for="sortAnonymizeDate">Anonymize Date</label>
value="fullName" </div>
ng-model="orderBy" <div class="form-check dropdown-item">
/> <input class="form-check-input" type="radio" name="sort" id="sortStatus" value="-status" ng-model="orderBy" />
<label class="form-check-label" for="sortFullName"> <label class="form-check-label" for="sortStatus">Status</label>
Repository </div>
</label> <div class="form-check dropdown-item">
</div> <input class="form-check-input" type="radio" name="sort" id="sortLastView" value="-lastView" ng-model="orderBy" />
<div class="form-check dropdown-item"> <label class="form-check-label" for="sortLastView">Last View</label>
<input </div>
class="form-check-input" <div class="form-check dropdown-item">
type="radio" <input class="form-check-input" type="radio" name="sort" id="sortPageView" value="-pageView" ng-model="orderBy" />
name="sort" <label class="form-check-label" for="sortPageView">Page View</label>
id="sortAnonymizeDate"
value="-anonymizeDate"
ng-model="orderBy"
/>
<label class="form-check-label" for="sortAnonymizeDate">
Anonymize Date
</label>
</div>
<div class="form-check dropdown-item">
<input
class="form-check-input"
type="radio"
name="sort"
id="sortStatus"
value="-status"
ng-model="orderBy"
/>
<label class="form-check-label" for="sortStatus">
Status
</label>
</div>
<div class="form-check dropdown-item">
<input
class="form-check-input"
type="radio"
name="sort"
id="sortLastView"
value="-lastView"
ng-model="orderBy"
/>
<label class="form-check-label" for="sortLastView">
Last View
</label>
</div>
<div class="form-check dropdown-item">
<input
class="form-check-input"
type="radio"
name="sort"
id="sortPageView"
value="-pageView"
ng-model="orderBy"
/>
<label class="form-check-label" for="sortPageView">
Page View
</label>
</div>
</div> </div>
</div> </div>
</div>
<div class="dropdown mt-1 mt-lg-0 mr-1"> <div class="dropdown">
<button <button
class="btn btn-secondary dropdown-toggle" class="btn btn-sm dropdown-toggle"
type="button" type="button"
id="dropdownStatus" id="dropdownStatus"
data-toggle="dropdown" data-toggle="dropdown"
aria-haspopup="true" aria-haspopup="true"
aria-expanded="false" aria-expanded="false"
> >
Status Status
</button> </button>
<div class="dropdown-menu" aria-labelledby="dropdownStatus"> <div class="dropdown-menu" aria-labelledby="dropdownStatus">
<h6 class="dropdown-header">Select status</h6> <h6 class="dropdown-header">Select status</h6>
<div class="form-check dropdown-item"> <div class="form-check dropdown-item">
<input <input class="form-check-input" type="checkbox" id="statusReady" value="ready" ng-model="filters.status.ready" />
class="form-check-input" <label class="form-check-label" for="statusReady">Ready</label>
type="checkbox"
name="sort"
id="statusReady"
value="ready"
ng-model="filters.status.ready"
/>
<label class="form-check-label" for="statusReady">
Ready
</label>
</div>
<div class="form-check dropdown-item">
<input
class="form-check-input"
type="checkbox"
name="sort"
id="statusExpired"
value="expired"
ng-model="filters.status.expired"
/>
<label class="form-check-label" for="statusExpired">
Expired
</label>
</div>
<div class="form-check dropdown-item">
<input
class="form-check-input"
type="checkbox"
name="sort"
id="statusRemoved"
value="removed"
ng-model="filters.status.removed"
/>
<label class="form-check-label" for="statusRemoved">
Removed
</label>
</div>
</div> </div>
</div> <div class="form-check dropdown-item">
<input class="form-check-input" type="checkbox" id="statusExpired" value="expired" ng-model="filters.status.expired" />
<div class="mt-1 mr-1" style="margin-top: -0.25rem !important"> <label class="form-check-label" for="statusExpired">Expired</label>
<strong>Repository</strong>
<div
class="progress position-relative"
style="min-width: 150px"
>
<div
class="progress-bar"
ng-class="{'progress-bar-striped progress-bar-animated w-100 bg-dark': !quota, 'bg-success': quota.repository.percent < 25 || quota.repository.total == 0, 'bg-danger': quota.repository.percent > 95 && quota.repository.total > 0, 'bg-warning': quota.repository.percent > 75 && quota.repository.total > 0 }"
role="progressbar"
style="width: {{quota.repository.percent}}%;"
aria-valuenow="{{quota.repository.used}}"
aria-valuemin="0"
aria-valuemax="{{quota.repository.total}}"
></div>
<span
class="justify-content-center d-flex position-absolute w-100"
ng-show="quota"
>{{quota.repository.used |
number}}/{{quota.repository.total}}</span
>
</div> </div>
</div> <div class="form-check dropdown-item">
<input class="form-check-input" type="checkbox" id="statusRemoved" value="removed" ng-model="filters.status.removed" />
<div class="mt-1 mr-1" style="margin-top: -0.25rem !important"> <label class="form-check-label" for="statusRemoved">Removed</label>
<strong>Storage</strong>
<div
class="progress position-relative"
style="min-width: 150px"
>
<div
class="progress-bar"
ng-class="{'progress-bar-striped progress-bar-animated w-100 bg-dark': !quota, 'bg-success': quota.storage.percent < 25 || quota.storage.total == 0, 'bg-danger': quota.storage.percent > 95 && quota.storage.total > 0, 'bg-warning': quota.storage.percent > 75 && quota.storage.total > 0 }"
role="progressbar"
style="width: {{quota.storage.percent}}%;"
aria-valuenow="{{quota.storage.used}}"
aria-valuemin="0"
aria-valuemax="{{quota.storage.total}}"
></div>
<span
ng-show="quota"
class="justify-content-center d-flex position-absolute w-100"
>{{quota.storage.used |
humanFileSize}}/{{quota.storage.total| humanFileSize}}</span
>
</div>
</div>
<div class="mt-1 mr-1" style="margin-top: -0.25rem !important">
<strong>File</strong>
<div
class="progress position-relative"
style="min-width: 150px"
>
<div
class="progress-bar"
ng-class="{'progress-bar-striped progress-bar-animated w-100 bg-dark': !quota, 'bg-success': quota.file.percent < 25 || quota.file.total == 0, 'bg-danger': quota.file.percent > 95 && quota.file.total > 0, 'bg-warning': quota.file.percent > 75 && quota.file.total > 0 }"
role="progressbar"
style="width: {{quota.file.percent}}%;"
aria-valuenow="{{quota.file.used}}"
aria-valuemin="0"
aria-valuemax="{{quota.file.total}}"
></div>
<span
class="justify-content-center d-flex position-absolute w-100"
ng-show="quota"
>{{quota.file.used | number}}/{{quota.file.total ||
"∞"}}</span
>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</form>
<div class="d-flex flex-wrap mt-2 mt-md-0" style="gap: 6px">
<a href="/anonymize" class="text-center btn btn-primary btn-sm">
<i class="fa fa-plus-circle" aria-hidden="true"></i> Anonymize Repo
</a>
<a
href="/pull-request-anonymize"
class="text-center btn btn-primary btn-sm"
>
<i class="fa fa-plus-circle" aria-hidden="true"></i> Anonymize PR
</a>
</div> </div>
</div> </form>
<div class="d-flex d-inline-flex mt-2">
<div class="alert alert-info alert-dismissible my-0 ml-1" ng-show="!v" role="alert" ng-repeat="(f, v) in filters.status">
<strong>{{f | title}}</strong>
<button type="button" class="close" data-dismiss="alert" aria-label="Close" ng-click="filters.status[f] = true;">
<span aria-hidden="true">&times;</span>
</button>
</div>
</div>
<!-- Active filter chips -->
<div class="d-flex flex-wrap mt-2" style="gap: 6px" ng-show="filters.status.ready === false || filters.status.expired === false || filters.status.removed === false">
<span class="filter-chip" ng-show="!v" ng-repeat="(f, v) in filters.status">
{{f | title}}
<button type="button" class="filter-chip-close" aria-label="Remove filter" ng-click="filters.status[f] = true;">
&times;
</button>
</span>
</div>
</div> </div>
<ul class="p-0 m-0 w-100">
<!-- Unified item list -->
<ul class="repo-list w-100">
<li <li
class="col-12 d-flex px-0 py-3 border-bottom color-border-secondary" class="repo-list-item"
ng-class="{'expired': repo.status == 'expired','removed': repo.status == 'removed','error': repo.status == 'error' }" ng-class="{'repo-inactive': item.status == 'expired' || item.status == 'removed' || item.status == 'error'}"
ng-repeat="repo in repositories| filter:repoFiler| orderBy:orderBy as filteredRepositories" ng-repeat="item in items | filter:itemFilter | orderBy:orderBy as filteredItems"
> >
<div class="w-100"> <div class="repo-list-item-content">
<div class=""> <div class="repo-list-item-main">
<h3> <div class="repo-list-item-header">
<a ng-href="/r/{{repo.repoId}}" ng-bind="repo.repoId"></a> <span class="type-badge" ng-class="{'type-repo': item._type === 'repo', 'type-pr': item._type === 'pr'}">
<i ng-class="{'fas fa-code-branch': item._type === 'repo', 'fas fa-code-merge': item._type === 'pr'}"></i>
{{item._type === 'repo' ? 'Repo' : 'PR'}}
</span>
<a ng-href="{{item._viewUrl}}" class="repo-name" ng-bind="item._name"></a>
<span <span
class="badge" class="status-badge"
ng-class="{'badge-warning': repo.status == 'removed' || repo.status == 'expired' || repo.status == 'removing' || repo.status == 'expiring', 'badge-info': repo.status == 'preparing' || repo.status == 'download', 'badge-success': repo.status == 'ready', 'badge-danger': repo.status == 'error'}" ng-class="{'status-removed': item.status == 'removed' || item.status == 'expired' || item.status == 'removing' || item.status == 'expiring', 'status-preparing': item.status == 'preparing' || item.status == 'download', 'status-ready': item.status == 'ready', 'status-error': item.status == 'error'}"
><span ng-bind="repo.status | title"></span> ><span ng-bind="item.status | title"></span><span
<span ng-if="item.status == 'error' && item.statusMessage"
ng-if="repo.status == 'error'" ng-bind="': ' + item.statusMessage"
ng-bind="': ' + repo.statusMessage" ></span></span>
></span </div>
></span> <div class="repo-source">
</h3> <span>
<span class="color-text-secondary mb-1">
<span class="repository">
<i class="fab fa-github" aria-hidden="true"></i> <i class="fab fa-github" aria-hidden="true"></i>
<a <a
href="https://github.com/{{repo.source.fullName}}/" ng-if="item._type === 'repo'"
class="fullName" href="https://github.com/{{item.source.fullName}}/"
ng-bind="repo.source.fullName" ng-bind="item.source.fullName"
></a>
<a
ng-if="item._type === 'pr'"
href="https://github.com/{{item.source.repositoryFullName}}/pull/{{item.source.pullRequestId}}"
ng-bind="item._source"
></a> ></a>
</span> </span>
<span class="branch" ng-if="repo.options.update"> <span ng-if="item._type === 'repo' && item.options.update">
<i class="fas fa-code-branch" aria-hidden="true"></i> <i class="fas fa-code-branch" aria-hidden="true"></i>
<a <a
href="https://github.com/{{repo.source.fullName}}/tree/{{repo.source.branch}}" href="https://github.com/{{item.source.fullName}}/tree/{{item.source.branch}}"
class="branch" ng-bind="item.source.branch"
ng-bind="repo.source.branch"
></a> ></a>
</span> </span>
<span class="commit" ng-if="!repo.options.update"> <span ng-if="item._type === 'repo' && !item.options.update">
@<a @<a
href="https://github.com/{{repo.source.fullName}}/tree/{{repo.source.commit}}" href="https://github.com/{{item.source.fullName}}/tree/{{item.source.commit}}"
class="commit" ng-bind="item.source.commit.substring(0, 8)"
ng-bind="repo.source.commit.substring(0, 8)"
></a> ></a>
</span> </span>
anonymized {{repo.anonymizeDate | humanTime}} <span class="repo-date">anonymized {{item.anonymizeDate | humanTime}}</span>
</span> </div>
</div> </div>
<div class="color-text-secondary mt-2"> <div class="repo-meta">
<span class="ml-0 mr-3" ng-if="::repo.conference"> <span ng-if="item.conference" title="Conference">
<i class="fas fa-chalkboard-teacher"></i> <i class="fas fa-chalkboard-teacher"></i> {{item.conference}}
{{repo.conference}}
</span> </span>
<span <span title="Terms" data-toggle="tooltip" data-placement="bottom">
class="ml-0 mr-3" <i class="fas fa-shield-alt"></i> {{item.options.terms.length | number}}
class="terms"
title="Terms: {{::repo.options.terms.join(', ')}}"
data-toggle="tooltip"
data-placement="bottom"
>
<i class="fas fa-shield-alt"></i>
{{::repo.options.terms.length | number}}
</span> </span>
<span <span ng-if="item._type === 'repo' && item.size" title="Size" data-toggle="tooltip" data-placement="bottom">
class="ml-0 mr-3" <i class="fas fa-database"></i> {{item.size.storage | humanFileSize}}
title="Size: {{::repo.size.storage | humanFileSize}}" </span>
data-toggle="tooltip" <span title="Views" data-toggle="tooltip" data-placement="bottom">
data-placement="bottom" <i class="far fa-eye"></i> {{item.pageView | number}}
> </span>
<i class="fas fa-database"></i> {{::repo.size.storage | <span title="Last view" data-toggle="tooltip" data-placement="bottom">
humanFileSize}}</span <i class="far fa-calendar-alt"></i> {{item.lastView | humanTime}}
> </span>
<span <span ng-if="item.options.expirationMode !== 'never' && item.status == 'ready'">
class="ml-0 mr-3" <i class="far fa-clock"></i> Expire: {{item.options.expirationDate | humanTime}}
title="View: {{::repo.pageView | number}}"
data-toggle="tooltip"
data-placement="bottom"
>
<i class="far fa-eye" aria-hidden="true"></i>
{{::repo.pageView | number}}
</span> </span>
<span
class="ml-0 mr-3"
title="Last view: {{::repo.lastView | date}}"
data-toggle="tooltip"
data-placement="bottom"
>
<i class="far fa-calendar-alt" aria-hidden="true"></i>
Last view: {{::repo.lastView | humanTime}}</span
>
<span
class="ml-0 mr-3"
ng-if="repo.options.expirationMode!='never' && repo.status == 'ready'"
>
<i class="far fa-clock" aria-hidden="true"></i>
Expire: {{repo.options.expirationDate | humanTime}}</span
>
</div> </div>
</div> </div>
<div class="d-flex"> <div class="repo-list-item-actions">
<div class="dropdown"> <div class="dropdown">
<button <button
class="btn black_border dropdown-toggle btn-sm" class="btn btn-sm dropdown-toggle"
type="button" type="button"
id="dropdownMenuButton"
data-toggle="dropdown" data-toggle="dropdown"
aria-haspopup="true" aria-haspopup="true"
aria-expanded="false" aria-expanded="false"
> >
Actions Actions
</button> </button>
<div class="dropdown-menu" aria-labelledby="dropdownMenuButton"> <div class="dropdown-menu dropdown-menu-right">
<a class="dropdown-item" href="/anonymize/{{repo.repoId}}"> <a class="dropdown-item" ng-href="{{item._editUrl}}">
<i class="far fa-edit" aria-hidden="true"></i> Edit <i class="far fa-edit" aria-hidden="true"></i> Edit
</a> </a>
<a <a class="dropdown-item" href="#" ng-show="item.status == 'ready' || item.status == 'error'" ng-click="refreshItem(item)">
class="dropdown-item"
href="#"
ng-show="repo.status == 'ready' || repo.status == 'error'"
ng-click="updateRepository(repo)"
>
<i class="fas fa-sync"></i> Force update <i class="fas fa-sync"></i> Force update
</a> </a>
<a <a class="dropdown-item" href="#" ng-show="item.status == 'removed'" ng-click="refreshItem(item)">
class="dropdown-item" <i class="fas fa-check-circle"></i> Enable
href="#"
ng-show="repo.status == 'removed'"
ng-click="updateRepository(repo)"
>
<i class="fas fa-check-circle"></i>
Enable
</a> </a>
<a <a class="dropdown-item" href="#" ng-show="item.status == 'ready'" ng-click="removeItem(item)">
class="dropdown-item"
href="#"
ng-show="repo.status == 'ready'"
ng-click="removeRepository(repo)"
>
<i class="fas fa-trash-alt"></i> Remove <i class="fas fa-trash-alt"></i> Remove
</a> </a>
<a class="dropdown-item" href="/r/{{repo.repoId}}/"> <a class="dropdown-item" ng-href="{{item._viewUrl}}">
<i class="fa fa-eye" aria-hidden="true"></i> View Repo <i class="fa fa-eye" aria-hidden="true"></i> View
</a> </a>
<a <a class="dropdown-item" href="/w/{{item.repoId}}/" target="_self" ng-if="item._type === 'repo' && item.options.page && item.status == 'ready'">
class="dropdown-item"
href="/w/{{repo.repoId}}/"
target="_self"
ng-if="repo.options.page && repo.status == 'ready'"
>
<i class="fas fa-globe"></i> View Page <i class="fas fa-globe"></i> View Page
</a> </a>
</div> </div>
</div> </div>
</div> </div>
</li> </li>
<li <li class="repo-list-empty" ng-if="filteredItems.length == 0">
class="col-12 d-flex px-0 py-3 border-bottom color-border-secondary" <i class="fas fa-inbox"></i>
ng-if="filteredRepositories.length == 0" <span>Nothing to display.</span>
>
There is no repository to display.
</li> </li>
</ul> </ul>
</div> </div>
+6 -26
View File
@@ -28,18 +28,18 @@
ng-class="{'active': path == '/dashboard'}" ng-class="{'active': path == '/dashboard'}"
href="/dashboard" href="/dashboard"
> >
<i class="fas fa-code-branch d-lg-none mr-1"></i> <i class="fas fa-th-large d-lg-none mr-1"></i>
Repositories Dashboard
</a> </a>
</li> </li>
<li class="nav-item" ng-if="user"> <li class="nav-item" ng-if="user">
<a <a
class="nav-link" class="nav-link"
ng-class="{'active': path == '/pr-dashboard'}" ng-class="{'active':path == '/anonymize' || path == '/pull-request-anonymize'}"
href="/pr-dashboard" href="/anonymize"
> >
<i class="fas fa-code-branch d-lg-none mr-1"></i> <i class="fas fa-user-secret d-lg-none mr-1"></i>
Pull Requests Anonymize
</a> </a>
</li> </li>
<li class="nav-item" ng-if="user"> <li class="nav-item" ng-if="user">
@@ -52,26 +52,6 @@
Conferences Conferences
</a> </a>
</li> </li>
<li class="nav-item" ng-if="user">
<a
class="nav-link"
ng-class="{'active':path == '/anonymize'}"
href="/anonymize"
>
<i class="fas fa-user-secret d-lg-none mr-1"></i>
Anonymize
</a>
</li>
<li class="nav-item" ng-if="user">
<a
class="nav-link"
ng-class="{'active':path == '/pull-request-anonymize'}"
href="/pull-request-anonymize"
>
<i class="fas fa-user-secret d-lg-none mr-1"></i>
Anonymize PR
</a>
</li>
<li class="nav-item" ng-if="user && user.isAdmin"> <li class="nav-item" ng-if="user && user.isAdmin">
<a <a
class="nav-link" class="nav-link"
+67 -97
View File
@@ -3,77 +3,68 @@
id="home" id="home"
class="row view rgba-gradient d-flex align-self-stretch justify-content-center align-items-center" class="row view rgba-gradient d-flex align-self-stretch justify-content-center align-items-center"
> >
<!-- Content -->
<div class="container px-md-3 px-sm-0"> <div class="container px-md-3 px-sm-0">
<!--Grid row--> <div class="row fadeIn main-options">
<form action="" method="post"> <div class="col-md-12 mb-4 white-text text-center fadeIn">
<div class="row fadeIn main-options"> <h1 class="hero-title pt-5">
<!--Grid column--> Anonymous GitHub
<div class="col-md-12 mb-4 white-text text-center fadeIn"> </h1>
<h3 class="display-4 font-weight-bold white-text mb-0 pt-5"> <p class="hero-subtitle mt-3 mb-4">
Anonymous GitHub Anonymize your repository in 5 minutes &mdash; join {{stat.nbUsers | number}} users.
</h3> </p>
<hr class="hr-light my-4 w-75" /> <span ng-if="!user">
<h4 class="subtext-header mt-2 mb-4"> <a
Anonymize your repository in 5 min as {{stat.nbUsers | number}} href="/github/login"
users already did. target="_self"
</h4> class="btn btn-hero"
<span ng-if="!user"> ><i class="fab fa-github mr-2"></i>Login with GitHub</a
<a >
href="/github/login" </span>
target="_self" <span ng-if="user">
class="btn p-2 white_border" <a
>Login with GitHub to anonymize</a href="/anonymize"
> class="btn btn-hero"
</span> ><i class="fa fa-plus-circle mr-2"></i>Anonymize a Repository</a
</div> >
<!--Grid column--> </span>
</div> </div>
</form> </div>
<!--Grid row-->
</div> </div>
<!-- Content -->
</div> </div>
<main> <main>
<div class="container"> <div class="container home-content">
<!--Grid row-->
<div class="row pt-5"> <div class="row pt-5">
<div class="col-md-12"> <div class="col-md-12">
<h2 id="usage">Usage</h2> <h2 id="usage">How it works</h2>
<div class="card-text mb-auto"> <ol class="usage-steps">
<ol> <li>
<li> <a href="/github/login" target="_self">Login</a> with GitHub to access your dashboard and anonymize your repositories.
<a href="/github/login" target="_self">Login</a> with Github </li>
access your dashboard and anonymize your repositories. <li>Configure which terms, links, and images to anonymize.</li>
</li> <li>Share the anonymous link in your double-blind paper submission.</li>
<li>Complete the list of terms that will be anonymized.</li> </ol>
<li>Anonymize and share your link in your double-anonymized paper.</li> <p class="usage-example">
</ol> Example:
Example of an anonymized repository:
<a <a
target="_self" target="_self"
href="https://anonymous.4open.science/r/840c8c57-3c32-451e-bf12-0e20be300389/" href="https://anonymous.4open.science/r/840c8c57-3c32-451e-bf12-0e20be300389/"
>https://anonymous.4open.science/r/840c8c57-3c32-451e-bf12-0e20be300389/</a >https://anonymous.4open.science/r/840c8c57-3c32-451e-bf12-0e20be300389/</a
>. >
</div> </p>
</div> </div>
</div> </div>
<hr class="featurette-divider" /> <hr class="featurette-divider" />
<div class="row featurette"> <div class="row featurette align-items-center">
<div class="col-md-7 order-md-2"> <div class="col-md-7 order-md-2">
<h2 class="featurette-heading"> <h2 class="featurette-heading">
Double-anonymous Double-anonymous
<span class="text-muted">Anonymize your repositories.</span>
</h2> </h2>
<p class="lead"> <p class="featurette-lead">
Anonymous Github allows you to simply anonymize your Github Anonymize your GitHub repository with options to remove links,
repository. Several anonymization options are available to ensure images, or specific terms. Keep full control &mdash; set an
that you do not break the double-anonymize such as removing links, expiration date to make your repository unavailable after review.
images or specific terms. You still keep control of your repository,
define an expiration date to make your repository unavailable after
the review.
</p> </p>
</div> </div>
<div class="col-md-5 order-md-1"> <div class="col-md-5 order-md-1">
@@ -87,17 +78,16 @@
<hr class="featurette-divider" /> <hr class="featurette-divider" />
<div class="row featurette"> <div class="row featurette align-items-center">
<div class="col-md-7"> <div class="col-md-7">
<h2 class="featurette-heading"> <h2 class="featurette-heading">
Explorer <span class="text-muted">Navigate the content.</span> Explorer
</h2> </h2>
<p class="lead"> <p class="featurette-lead">
The reviewers can explore your repository with ease, the source code Reviewers can browse your repository with highlighted source code,
is highlighted, PDFs, images, Notebook are rendered. The goal is to rendered PDFs, images, and notebooks.
make is as easy as possible for the reviewer to explore and review <a href="https://pages.github.com">GitHub Pages</a>
the repository. <a href="https://pages.github.com">GitHub Pages</a> is also supported.
is also supported, but not static site generators, such as Jekyll.
</p> </p>
</div> </div>
<div class="col-md-5"> <div class="col-md-5">
@@ -108,19 +98,17 @@
/> />
</div> </div>
</div> </div>
<hr class="featurette-divider" /> <hr class="featurette-divider" />
<div class="row featurette"> <div class="row featurette align-items-center">
<div class="col-md-7 order-md-2"> <div class="col-md-7 order-md-2">
<h2 class="featurette-heading"> <h2 class="featurette-heading">
Manage Manage
<span class="text-muted">Keep an eyes on your repositories.</span>
</h2> </h2>
<p class="lead"> <p class="featurette-lead">
Keep a view in your anonymized repositories. Edit your anonymization Monitor views, edit configuration, remove or update your repository &mdash;
configuration, remove your repository and update the content of your all from a clean dashboard.
repository. You can also monitor the access and the number of views
of your repository.
</p> </p>
</div> </div>
<div class="col-md-5 order-md-1"> <div class="col-md-5 order-md-1">
@@ -131,42 +119,24 @@
/> />
</div> </div>
</div> </div>
<hr class="featurette-divider" /> <hr class="featurette-divider" />
<h2 id="about">Metrics</h2>
<div class="row pb-5"> <h2 id="about" class="mb-4">Metrics</h2>
<!--Grid column--> <div class="metrics-grid">
<div class="col-md-4"> <div class="metric-card">
<div <div class="metric-value">{{stat.nbUsers | number}}</div>
class="row no-gutters border rounded overflow-hidden flex-md-row mb-4 shadow-sm h-md-250 position-relative" <div class="metric-label">Users</div>
>
<div class="col p-4 d-flex flex-column position-static">
<h3 class="mb-auto text-center">{{stat.nbUsers|number}} Users</h3>
</div>
</div>
</div> </div>
<div class="col-md-4"> <div class="metric-card">
<div <div class="metric-value">{{stat.nbRepositories | number}}</div>
class="row no-gutters border rounded overflow-hidden flex-md-row mb-4 shadow-sm h-md-250 position-relative" <div class="metric-label">Repositories</div>
>
<div class="col p-4 d-flex flex-column position-static">
<h3 class="mb-auto text-center">
{{stat.nbRepositories|number}} Repositories
</h3>
</div>
</div>
</div> </div>
<div class="col-md-4"> <div class="metric-card">
<div <div class="metric-value">{{stat.nbPageViews | number}}</div>
class="row no-gutters border rounded overflow-hidden flex-md-row mb-4 shadow-sm h-md-250 position-relative" <div class="metric-label">Page Views</div>
>
<div class="col p-4 d-flex flex-column position-static">
<h3 class="mb-auto text-center">{{stat.nbPageViews|number}} Page views</h3>
</div>
</div>
</div> </div>
<!--Grid column-->
</div> </div>
<!--Grid row-->
</div> </div>
</main> </main>
</div> </div>
+162 -276
View File
@@ -1,168 +1,14 @@
<div class="container page"> <div class="container page dashboard-page">
<div class="row"> <div class="row">
<div class="border-bottom color-border-secondary py-3 w-100"> <div class="w-100 py-3">
<div class="d-flex align-items-center w-100"> <!-- Header row: title + action buttons -->
<form class="w-100" aria-label="Pull Requests" accept-charset="UTF-8"> <div class="d-flex align-items-center justify-content-between mb-3">
<div class="d-flex flex-column flex-lg-row flex-auto"> <h2 class="dashboard-title mb-0">Pull Requests</h2>
<div class="mb-1 mb-md-0 mr-md-3"> <div class="d-flex flex-wrap" style="gap: 6px">
<input <a href="/anonymize" class="btn btn-primary btn-sm">
type="search"
id="search"
class="form-control"
aria-label="Find a pull request…"
placeholder="Find a pull request…"
autocomplete="off"
ng-model="search"
/>
</div>
<div class="d-flex flex-wrap">
<div class="dropdown mt-1 mt-lg-0 mr-1">
<button
class="btn btn-secondary dropdown-toggle"
type="button"
id="dropdownSort"
data-toggle="dropdown"
aria-haspopup="true"
aria-expanded="false"
>
Sort
</button>
<div class="dropdown-menu" aria-labelledby="dropdownSort">
<h6 class="dropdown-header">Select order</h6>
<div class="form-check dropdown-item">
<input
class="form-check-input"
type="radio"
name="sort"
id="sortFullName"
value="fullName"
ng-model="orderBy"
/>
<label class="form-check-label" for="sortFullName">
Pull Request
</label>
</div>
<div class="form-check dropdown-item">
<input
class="form-check-input"
type="radio"
name="sort"
id="sortAnonymizeDate"
value="-anonymizeDate"
ng-model="orderBy"
/>
<label class="form-check-label" for="sortAnonymizeDate">
Anonymize Date
</label>
</div>
<div class="form-check dropdown-item">
<input
class="form-check-input"
type="radio"
name="sort"
id="sortStatus"
value="-status"
ng-model="orderBy"
/>
<label class="form-check-label" for="sortStatus">
Status
</label>
</div>
<div class="form-check dropdown-item">
<input
class="form-check-input"
type="radio"
name="sort"
id="sortLastView"
value="-lastView"
ng-model="orderBy"
/>
<label class="form-check-label" for="sortLastView">
Last View
</label>
</div>
<div class="form-check dropdown-item">
<input
class="form-check-input"
type="radio"
name="sort"
id="sortPageView"
value="-pageView"
ng-model="orderBy"
/>
<label class="form-check-label" for="sortPageView">
Page View
</label>
</div>
</div>
</div>
<div class="dropdown mt-1 mt-lg-0 mr-1">
<button
class="btn btn-secondary dropdown-toggle"
type="button"
id="dropdownStatus"
data-toggle="dropdown"
aria-haspopup="true"
aria-expanded="false"
>
Status
</button>
<div class="dropdown-menu" aria-labelledby="dropdownStatus">
<h6 class="dropdown-header">Select status</h6>
<div class="form-check dropdown-item">
<input
class="form-check-input"
type="checkbox"
name="sort"
id="statusReady"
value="ready"
ng-model="filters.status.ready"
/>
<label class="form-check-label" for="statusReady">
Ready
</label>
</div>
<div class="form-check dropdown-item">
<input
class="form-check-input"
type="checkbox"
name="sort"
id="statusExpired"
value="expired"
ng-model="filters.status.expired"
/>
<label class="form-check-label" for="statusExpired">
Expired
</label>
</div>
<div class="form-check dropdown-item">
<input
class="form-check-input"
type="checkbox"
name="sort"
id="statusRemoved"
value="removed"
ng-model="filters.status.removed"
/>
<label class="form-check-label" for="statusRemoved">
Removed
</label>
</div>
</div>
</div>
</div>
</div>
</form>
<div class="d-flex flex-wrap mt-2 mt-md-0" style="gap: 6px">
<a href="/anonymize" class="text-center btn btn-primary btn-sm">
<i class="fa fa-plus-circle" aria-hidden="true"></i> Anonymize Repo <i class="fa fa-plus-circle" aria-hidden="true"></i> Anonymize Repo
</a> </a>
<a <a href="/pull-request-anonymize" class="btn btn-primary btn-sm">
href="/pull-request-anonymize"
class="text-center btn btn-primary btn-sm"
>
<i class="fa fa-plus-circle" aria-hidden="true"></i> Anonymize PR <i class="fa fa-plus-circle" aria-hidden="true"></i> Anonymize PR
</a> </a>
<a <a
@@ -170,138 +16,180 @@
data-toggle="tooltip" data-toggle="tooltip"
data-placement="bottom" data-placement="bottom"
href="/claim" href="/claim"
class="text-center btn btn-secondary btn-sm" class="btn btn-sm"
> >
Claim Claim
</a> </a>
</div> </div>
</div> </div>
</div>
<ul class="p-0 m-0 w-100"> <!-- Search + filters row -->
<li <form class="w-100" aria-label="Pull Requests" accept-charset="UTF-8">
class="col-12 d-flex px-0 py-3 border-bottom color-border-secondary" <div class="d-flex flex-column flex-md-row align-items-md-center" style="gap: 8px">
ng-class="{'expired': pr.status == 'expired','removed': pr.status == 'removed','error': pr.status == 'error' }" <div class="flex-grow-1">
ng-repeat="pr in pullRequests| filter:pullRequestFilter| orderBy:orderBy as filteredPullRequests" <input
> type="search"
<div class="w-100"> id="search"
<div class=""> class="form-control"
<h3> aria-label="Find a pull request..."
<a placeholder="Find a pull request..."
ng-href="/pr/{{pr.pullRequestId}}" autocomplete="off"
ng-bind="pr.pullRequestId" ng-model="search"
></a> />
<span
class="badge"
ng-class="{'badge-warning': pr.status == 'removed' || pr.status == 'expired' || pr.status == 'removing' || pr.status == 'expiring', 'badge-info': pr.status == 'preparing' || pr.status == 'download', 'badge-success': pr.status == 'ready', 'badge-danger': pr.status == 'error'}"
><span ng-bind="pr.status | title"></span>
<span
ng-if="pr.status == 'error'"
ng-bind="': ' + pr.statusMessage"
></span
></span>
</h3>
</div> </div>
<div class="color-text-secondary mt-t"> <div class="d-flex flex-wrap" style="gap: 6px">
<span class="pull-request"> <div class="dropdown">
<i class="fab fa-github" aria-hidden="true"></i> <button
<a class="btn btn-sm dropdown-toggle"
href="https://github.com/{{pr.source.repositoryFullName}}/pull/{{pr.source.pullRequestId}}" type="button"
class="fullName" id="dropdownSort"
>{{pr.source.repositoryFullName}}@{{pr.source.pullRequestId}}</a data-toggle="dropdown"
aria-haspopup="true"
aria-expanded="false"
> >
</span> Sort
<span </button>
class="badge" <div class="dropdown-menu" aria-labelledby="dropdownSort">
ng-class="{'badge-success':pr.merged, 'badge-warning':pr.state=='open', 'badge-danger':pr.state=='closed' &&!pr.merged}" <h6 class="dropdown-header">Select order</h6>
> <div class="form-check dropdown-item">
{{pr.merged?"merged":pr.state | title}} <input class="form-check-input" type="radio" name="sort" id="sortFullName" value="fullName" ng-model="orderBy" />
</span> <label class="form-check-label" for="sortFullName">Pull Request</label>
anonymized {{pr.anonymizeDate | humanTime}} </div>
</div> <div class="form-check dropdown-item">
<div class="color-text-secondary mt-2"> <input class="form-check-input" type="radio" name="sort" id="sortAnonymizeDate" value="-anonymizeDate" ng-model="orderBy" />
<span class="ml-0 mr-3" ng-if="::pr.conference"> <label class="form-check-label" for="sortAnonymizeDate">Anonymize Date</label>
<i class="fas fa-chalkboard-teacher"></i> </div>
{{pr.conference}} <div class="form-check dropdown-item">
</span> <input class="form-check-input" type="radio" name="sort" id="sortStatus" value="-status" ng-model="orderBy" />
<span <label class="form-check-label" for="sortStatus">Status</label>
class="ml-0 mr-3" </div>
class="terms" <div class="form-check dropdown-item">
title="Terms: {{::pr.options.terms.join(', ')}}" <input class="form-check-input" type="radio" name="sort" id="sortLastView" value="-lastView" ng-model="orderBy" />
data-toggle="tooltip" <label class="form-check-label" for="sortLastView">Last View</label>
data-placement="bottom" </div>
> <div class="form-check dropdown-item">
<i class="fas fa-shield-alt"></i> <input class="form-check-input" type="radio" name="sort" id="sortPageView" value="-pageView" ng-model="orderBy" />
{{::pr.options.terms.length | number}} <label class="form-check-label" for="sortPageView">Page View</label>
</span> </div>
<span </div>
class="ml-0 mr-3" </div>
title="View: {{::pr.pageView | number}}"
data-toggle="tooltip" <div class="dropdown">
data-placement="bottom" <button
> class="btn btn-sm dropdown-toggle"
<i class="far fa-eye" aria-hidden="true"></i> type="button"
{{::pr.pageView | number}} id="dropdownStatus"
</span> data-toggle="dropdown"
<span aria-haspopup="true"
class="ml-0 mr-3" aria-expanded="false"
title="Last view: {{::pr.lastView | date}}" >
data-toggle="tooltip" Status
data-placement="bottom" </button>
> <div class="dropdown-menu" aria-labelledby="dropdownStatus">
<i class="far fa-calendar-alt" aria-hidden="true"></i> <h6 class="dropdown-header">Select status</h6>
Last view: {{::pr.lastView | humanTime}}</span <div class="form-check dropdown-item">
> <input class="form-check-input" type="checkbox" name="sort" id="statusReady" value="ready" ng-model="filters.status.ready" />
<span <label class="form-check-label" for="statusReady">Ready</label>
class="ml-0 mr-3" </div>
ng-if="pr.options.expirationMode!='never' && pr.status == 'ready'" <div class="form-check dropdown-item">
> <input class="form-check-input" type="checkbox" name="sort" id="statusExpired" value="expired" ng-model="filters.status.expired" />
<i class="far fa-clock" aria-hidden="true"></i> <label class="form-check-label" for="statusExpired">Expired</label>
Expire: {{pr.options.expirationDate | humanTime}}</span </div>
> <div class="form-check dropdown-item">
<input class="form-check-input" type="checkbox" name="sort" id="statusRemoved" value="removed" ng-model="filters.status.removed" />
<label class="form-check-label" for="statusRemoved">Removed</label>
</div>
</div>
</div>
</div> </div>
</div> </div>
<div class="d-flex"> </form>
<!-- Active filter chips -->
<div class="d-flex flex-wrap mt-2" style="gap: 6px" ng-show="filters.status.ready === false || filters.status.expired === false || filters.status.removed === false">
<span class="filter-chip" ng-show="!v" ng-repeat="(f, v) in filters.status">
{{f | title}}
<button type="button" class="filter-chip-close" aria-label="Remove filter" ng-click="filters.status[f] = true;">
&times;
</button>
</span>
</div>
</div>
<!-- Pull request list -->
<ul class="repo-list w-100">
<li
class="repo-list-item"
ng-class="{'repo-inactive': pr.status == 'expired' || pr.status == 'removed' || pr.status == 'error'}"
ng-repeat="pr in pullRequests| filter:pullRequestFilter| orderBy:orderBy as filteredPullRequests"
>
<div class="repo-list-item-content">
<div class="repo-list-item-main">
<div class="repo-list-item-header">
<a ng-href="/pr/{{pr.pullRequestId}}" class="repo-name" ng-bind="pr.pullRequestId"></a>
<span
class="status-badge"
ng-class="{'status-removed': pr.status == 'removed' || pr.status == 'expired' || pr.status == 'removing' || pr.status == 'expiring', 'status-preparing': pr.status == 'preparing' || pr.status == 'download', 'status-ready': pr.status == 'ready', 'status-error': pr.status == 'error'}"
><span ng-bind="pr.status | title"></span><span
ng-if="pr.status == 'error'"
ng-bind="': ' + pr.statusMessage"
></span></span>
</div>
<div class="repo-source">
<span>
<i class="fab fa-github" aria-hidden="true"></i>
<a href="https://github.com/{{pr.source.repositoryFullName}}/pull/{{pr.source.pullRequestId}}">{{pr.source.repositoryFullName}}#{{pr.source.pullRequestId}}</a>
</span>
<span
class="status-badge"
ng-class="{'status-ready': pr.merged, 'status-removed': pr.state=='open', 'status-error': pr.state=='closed' && !pr.merged}"
style="font-size: 10px;"
>
{{pr.merged ? "merged" : pr.state | title}}
</span>
<span class="repo-date">anonymized {{pr.anonymizeDate | humanTime}}</span>
</div>
</div>
<div class="repo-meta">
<span ng-if="::pr.conference" title="Conference">
<i class="fas fa-chalkboard-teacher"></i> {{pr.conference}}
</span>
<span title="Terms: {{::pr.options.terms.join(', ')}}" data-toggle="tooltip" data-placement="bottom">
<i class="fas fa-shield-alt"></i> {{::pr.options.terms.length | number}}
</span>
<span title="Views: {{::pr.pageView | number}}" data-toggle="tooltip" data-placement="bottom">
<i class="far fa-eye"></i> {{::pr.pageView | number}}
</span>
<span title="Last view: {{::pr.lastView | date}}" data-toggle="tooltip" data-placement="bottom">
<i class="far fa-calendar-alt"></i> {{::pr.lastView | humanTime}}
</span>
<span ng-if="pr.options.expirationMode!='never' && pr.status == 'ready'">
<i class="far fa-clock"></i> Expire: {{pr.options.expirationDate | humanTime}}
</span>
</div>
</div>
<div class="repo-list-item-actions">
<div class="dropdown"> <div class="dropdown">
<button <button
class="btn black_border dropdown-toggle btn-sm" class="btn btn-sm dropdown-toggle"
type="button" type="button"
id="dropdownMenuButton"
data-toggle="dropdown" data-toggle="dropdown"
aria-haspopup="true" aria-haspopup="true"
aria-expanded="false" aria-expanded="false"
> >
Actions Actions
</button> </button>
<div class="dropdown-menu" aria-labelledby="dropdownMenuButton"> <div class="dropdown-menu dropdown-menu-right">
<a <a class="dropdown-item" href="/pull-request-anonymize/{{pr.pullRequestId}}">
class="dropdown-item"
href="/pull-request-anonymize/{{pr.pullRequestId}}"
>
<i class="far fa-edit" aria-hidden="true"></i> Edit <i class="far fa-edit" aria-hidden="true"></i> Edit
</a> </a>
<a <a class="dropdown-item" href="#" ng-show="pr.status == 'ready' || pr.status == 'error'" ng-click="updatePullRequest(pr)">
class="dropdown-item"
href="#"
ng-show="pr.status == 'ready' || pr.status == 'error'"
ng-click="updatePullRequest(pr)"
>
<i class="fas fa-sync"></i> Force update <i class="fas fa-sync"></i> Force update
</a> </a>
<a <a class="dropdown-item" href="#" ng-show="pr.status == 'removed'" ng-click="updatePullRequest(pr)">
class="dropdown-item" <i class="fas fa-check-circle"></i> Enable
href="#"
ng-show="pr.status == 'removed'"
ng-click="updatePullRequest(pr)"
>
<i class="fas fa-check-circle"></i>
Enable
</a> </a>
<a <a class="dropdown-item" href="#" ng-show="pr.status == 'ready'" ng-click="removePullRequest(pr)">
class="dropdown-item"
href="#"
ng-show="pr.status == 'ready'"
ng-click="removePullRequest(pr)"
>
<i class="fas fa-trash-alt"></i> Remove <i class="fas fa-trash-alt"></i> Remove
</a> </a>
<a class="dropdown-item" href="/pr/{{pr.pullRequestId}}/"> <a class="dropdown-item" href="/pr/{{pr.pullRequestId}}/">
@@ -311,11 +199,9 @@
</div> </div>
</div> </div>
</li> </li>
<li <li class="repo-list-empty" ng-if="filteredPullRequests.length == 0">
class="col-12 d-flex px-0 py-3 border-bottom color-border-secondary" <i class="fas fa-inbox"></i>
ng-if="filteredPullRequests.length == 0" <span>No pull requests to display.</span>
>
There is no pull request to display.
</li> </li>
</ul> </ul>
</div> </div>
+344 -793
View File
File diff suppressed because it is too large Load Diff
+2 -2
View File
File diff suppressed because one or more lines are too long