mirror of
https://github.com/tdurieux/anonymous_github.git
synced 2026-05-15 06:30:26 +02:00
update design
This commit is contained in:
Generated
+5914
-6140
File diff suppressed because it is too large
Load Diff
@@ -10,6 +10,7 @@
|
||||
"lint": "eslint .",
|
||||
"start": "node --inspect=5858 -r ts-node/register ./src/server/index.ts",
|
||||
"dev": "nodemon --transpile-only ./src/server/index.ts",
|
||||
"dev:ui": "node scripts/dev-proxy.js",
|
||||
"build": "rm -rf build && tsc && gulp",
|
||||
"knip": "knip"
|
||||
},
|
||||
@@ -88,6 +89,7 @@
|
||||
"gulp-concat": "^2.6.1",
|
||||
"gulp-order": "^1.2.0",
|
||||
"gulp-uglify": "^3.0.2",
|
||||
"http-proxy-middleware": "^3.0.5",
|
||||
"knip": "^5.1.0",
|
||||
"mocha": "^10.8.2",
|
||||
"nodemon": "^3.1.0",
|
||||
|
||||
Vendored
+1
-1
File diff suppressed because one or more lines are too long
+2141
-254
File diff suppressed because it is too large
Load Diff
+7
-1
@@ -31,8 +31,14 @@
|
||||
<link rel="manifest" href="/favicon/site.webmanifest" />
|
||||
<link rel="shortcut icon" href="/favicon.ico" />
|
||||
<meta name="msapplication-config" content="/favicon/browserconfig.xml" />
|
||||
<meta name="theme-color" content="#ffffff" />
|
||||
<meta name="theme-color" content="#FAF9F6" />
|
||||
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=Instrument+Serif:ital@0;1&family=JetBrains+Mono:wght@400;500&display=swap"
|
||||
/>
|
||||
<link rel="stylesheet" href="/css/all.min.css" />
|
||||
</head>
|
||||
<body keypress-events class="d-flex flex-column">
|
||||
|
||||
+16
-4
@@ -1,5 +1,17 @@
|
||||
<div class="container d-flex h-100">
|
||||
<h1 class=" m-auto text-center">
|
||||
<div class="display-1">404</div><small class="text-muted">Page not found</small>
|
||||
</h1>
|
||||
<div class="paper-empty">
|
||||
<div class="paper-empty-inner">
|
||||
<div class="paper-eyebrow">Error · 404</div>
|
||||
<h1 class="paper-empty-title">
|
||||
This link<br /><em>isn’t here.</em>
|
||||
</h1>
|
||||
<p class="paper-empty-lede">
|
||||
The anonymous mirror you’re looking for has expired, been removed
|
||||
by its owner, or never existed. If you received this URL from an
|
||||
author, ask them to re-issue it.
|
||||
</p>
|
||||
<div class="paper-empty-cta">
|
||||
<a href="/" class="btn-hero">Back to home</a>
|
||||
<a href="/faq" class="btn-hero-ghost">Read the FAQ</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,187 +1,142 @@
|
||||
<div class="container page">
|
||||
<!-- Admin Navigation -->
|
||||
<div class="container paper-page">
|
||||
<div class="paper-crumbs">Admin / <span class="here">Conferences</span></div>
|
||||
<h1 class="paper-page-title">All <em>conferences</em></h1>
|
||||
<p class="paper-page-lede">Every venue configured on the platform.</p>
|
||||
|
||||
<nav class="admin-nav">
|
||||
<a href="/admin/">
|
||||
<i class="fas fa-code-branch"></i> Repositories
|
||||
</a>
|
||||
<a href="/admin/users">
|
||||
<i class="fas fa-users"></i> Users
|
||||
</a>
|
||||
<a href="/admin/conferences" class="active">
|
||||
<i class="fas fa-chalkboard-teacher"></i> Conferences
|
||||
</a>
|
||||
<a href="/admin/queues">
|
||||
<i class="fas fa-tasks"></i> Queues
|
||||
</a>
|
||||
<a href="/admin/"><i class="fas fa-code-branch"></i> Repositories</a>
|
||||
<a href="/admin/users"><i class="fas fa-users"></i> Users</a>
|
||||
<a href="/admin/conferences" class="active"><i class="fas fa-chalkboard-teacher"></i> Conferences</a>
|
||||
<a href="/admin/queues"><i class="fas fa-tasks"></i> Queues</a>
|
||||
</nav>
|
||||
|
||||
<!-- Summary Stats -->
|
||||
<div class="admin-stats">
|
||||
<div class="admin-stat-card">
|
||||
<div class="stat-value" ng-bind="total >= 0 ? (total | number) : '...'"></div>
|
||||
<div class="stat-label">Total Conferences</div>
|
||||
<div class="stat-label">Total conferences</div>
|
||||
</div>
|
||||
<div class="admin-stat-card">
|
||||
<div class="stat-value">{{query.page}}/{{totalPage || '...'}}</div>
|
||||
<div class="stat-label">Current Page</div>
|
||||
<div class="stat-label">Current page</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Toolbar -->
|
||||
<div class="admin-toolbar">
|
||||
<input
|
||||
type="search"
|
||||
class="form-control"
|
||||
aria-label="Search conferences..."
|
||||
placeholder="Search conferences..."
|
||||
autocomplete="off"
|
||||
ng-model="query.search"
|
||||
/>
|
||||
|
||||
<div class="pagination-compact">
|
||||
<button class="btn btn-sm" ng-click="query.page = Math.max(1, query.page - 1)" ng-disabled="query.page <= 1">
|
||||
<i class="fas fa-chevron-left"></i>
|
||||
</button>
|
||||
<form class="w-100 dashboard-filter-row" aria-label="Conferences" accept-charset="UTF-8">
|
||||
<div class="search-wrap">
|
||||
<input
|
||||
type="number"
|
||||
class="form-control form-control-sm"
|
||||
ng-model="query.page"
|
||||
min="1"
|
||||
max="{{totalPage}}"
|
||||
type="search"
|
||||
class="form-control"
|
||||
aria-label="Search conferences"
|
||||
placeholder="Search conferences…"
|
||||
autocomplete="off"
|
||||
ng-model="query.search"
|
||||
/>
|
||||
<span>/{{totalPage}}</span>
|
||||
<button class="btn btn-sm" ng-click="query.page = Math.min(totalPage, query.page + 1)" ng-disabled="query.page >= totalPage">
|
||||
<i class="fas fa-chevron-right"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="dropdown">
|
||||
<button
|
||||
class="btn btn-sm dropdown-toggle"
|
||||
type="button"
|
||||
id="dropdownSort"
|
||||
data-toggle="dropdown"
|
||||
>
|
||||
<i class="fas fa-sort"></i> Sort
|
||||
</button>
|
||||
<div class="dropdown-menu" aria-labelledby="dropdownSort">
|
||||
<h6 class="dropdown-header">Sort by</h6>
|
||||
<a class="dropdown-item" href="#" ng-click="query.sort = 'source.conferenceName'">
|
||||
<i class="fas fa-check" ng-show="query.sort == 'source.conferenceName'"></i> Conference
|
||||
</a>
|
||||
<a class="dropdown-item" href="#" ng-click="query.sort = 'anonymizeDate'">
|
||||
<i class="fas fa-check" ng-show="query.sort == 'anonymizeDate'"></i> Anonymize Date
|
||||
</a>
|
||||
<a class="dropdown-item" href="#" ng-click="query.sort = 'status'">
|
||||
<i class="fas fa-check" ng-show="query.sort == 'status'"></i> Status
|
||||
</a>
|
||||
<a class="dropdown-item" href="#" ng-click="query.sort = 'lastView'">
|
||||
<i class="fas fa-check" ng-show="query.sort == 'lastView'"></i> Last View
|
||||
</a>
|
||||
<a class="dropdown-item" href="#" ng-click="query.sort = 'pageView'">
|
||||
<i class="fas fa-check" ng-show="query.sort == 'pageView'"></i> Page Views
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="dropdown">
|
||||
<button
|
||||
class="btn btn-sm dropdown-toggle"
|
||||
type="button"
|
||||
id="dropdownStatus"
|
||||
data-toggle="dropdown"
|
||||
>
|
||||
<i class="fas fa-filter"></i> Status
|
||||
</button>
|
||||
<div class="dropdown-menu" aria-labelledby="dropdownStatus">
|
||||
<h6 class="dropdown-header">Filter by status</h6>
|
||||
<label class="dropdown-item mb-0">
|
||||
<input type="checkbox" ng-model="query.ready" /> Ready
|
||||
</label>
|
||||
<label class="dropdown-item mb-0">
|
||||
<input type="checkbox" ng-model="query.preparing" /> Preparing
|
||||
</label>
|
||||
<label class="dropdown-item mb-0">
|
||||
<input type="checkbox" ng-model="query.expired" /> Expired
|
||||
</label>
|
||||
<label class="dropdown-item mb-0">
|
||||
<input type="checkbox" ng-model="query.removed" /> Removed
|
||||
</label>
|
||||
<label class="dropdown-item mb-0">
|
||||
<input type="checkbox" ng-model="query.error" /> Error
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Conference List -->
|
||||
<div
|
||||
class="admin-list-item"
|
||||
ng-repeat="conference in conferences | filter:conferenceFiler | orderBy:orderBy as filteredConferences"
|
||||
>
|
||||
<div class="item-main">
|
||||
<div class="item-title">
|
||||
<a
|
||||
ng-href="/conference/{{conference.conferenceID}}"
|
||||
ng-bind="conference.name"
|
||||
></a>
|
||||
<span
|
||||
class="status-badge"
|
||||
ng-class="'status-' + conference.status"
|
||||
>{{conference.status | title}}</span>
|
||||
</div>
|
||||
<div class="item-meta">
|
||||
<span>
|
||||
<i class="fas fa-fingerprint"></i> ID: {{conference.conferenceID}}
|
||||
</span>
|
||||
<span>
|
||||
<i class="fas fa-table"></i> {{::conference.repositories.length || 0 | number}} repos
|
||||
</span>
|
||||
<span>
|
||||
<i class="fas fa-euro-sign"></i> {{conference.price || 0 | number}} €
|
||||
</span>
|
||||
<span>
|
||||
<i class="fas fa-calendar-alt"></i>
|
||||
{{conference.startDate | date}} – {{conference.endDate | date}}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="item-actions">
|
||||
<div class="dropdown">
|
||||
<button
|
||||
class="btn dropdown-toggle btn-sm"
|
||||
type="button"
|
||||
data-toggle="dropdown"
|
||||
>
|
||||
Actions
|
||||
<div class="d-flex flex-wrap" style="gap: 8px; align-items: center;">
|
||||
<div class="pagination-compact">
|
||||
<button class="btn btn-sm" type="button" ng-click="query.page = Math.max(1, query.page - 1)" ng-disabled="query.page <= 1">
|
||||
<i class="fas fa-chevron-left"></i>
|
||||
</button>
|
||||
<div class="dropdown-menu dropdown-menu-right">
|
||||
<a class="dropdown-item" href="/conference/{{conference.conferenceID}}/edit">
|
||||
<i class="far fa-edit"></i> Edit
|
||||
<input type="number" class="form-control form-control-sm" ng-model="query.page" min="1" max="{{totalPage}}" />
|
||||
<span>/{{totalPage}}</span>
|
||||
<button class="btn btn-sm" type="button" ng-click="query.page = Math.min(totalPage, query.page + 1)" ng-disabled="query.page >= totalPage">
|
||||
<i class="fas fa-chevron-right"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="dropdown">
|
||||
<button class="btn dropdown-toggle" type="button" id="dropdownSort" data-toggle="dropdown">Sort</button>
|
||||
<div class="dropdown-menu" aria-labelledby="dropdownSort">
|
||||
<h6 class="dropdown-header">Sort by</h6>
|
||||
<a class="dropdown-item" href="#" ng-click="query.sort = 'source.conferenceName'">
|
||||
<i class="fas fa-check" ng-show="query.sort == 'source.conferenceName'"></i> Conference
|
||||
</a>
|
||||
<a class="dropdown-item" href="/conference/{{conference.conferenceID}}/">
|
||||
<i class="fa fa-eye"></i> View
|
||||
<a class="dropdown-item" href="#" ng-click="query.sort = 'anonymizeDate'">
|
||||
<i class="fas fa-check" ng-show="query.sort == 'anonymizeDate'"></i> Anonymize date
|
||||
</a>
|
||||
<div class="dropdown-divider"></div>
|
||||
<a
|
||||
class="dropdown-item text-danger"
|
||||
href="#"
|
||||
ng-show="conference.status != 'removed'"
|
||||
ng-click="removeConference(conference)"
|
||||
>
|
||||
<i class="fas fa-trash-alt"></i> Remove
|
||||
<a class="dropdown-item" href="#" ng-click="query.sort = 'status'">
|
||||
<i class="fas fa-check" ng-show="query.sort == 'status'"></i> Status
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="dropdown">
|
||||
<button class="btn dropdown-toggle" type="button" id="dropdownStatus" data-toggle="dropdown">Status</button>
|
||||
<div class="dropdown-menu" aria-labelledby="dropdownStatus">
|
||||
<h6 class="dropdown-header">Filter by status</h6>
|
||||
<div class="form-check dropdown-item">
|
||||
<input class="form-check-input" type="checkbox" id="adminConfStatusReady" ng-model="query.ready" />
|
||||
<label class="form-check-label" for="adminConfStatusReady">Ready</label>
|
||||
</div>
|
||||
<div class="form-check dropdown-item">
|
||||
<input class="form-check-input" type="checkbox" id="adminConfStatusPreparing" ng-model="query.preparing" />
|
||||
<label class="form-check-label" for="adminConfStatusPreparing">Preparing</label>
|
||||
</div>
|
||||
<div class="form-check dropdown-item">
|
||||
<input class="form-check-input" type="checkbox" id="adminConfStatusExpired" ng-model="query.expired" />
|
||||
<label class="form-check-label" for="adminConfStatusExpired">Expired</label>
|
||||
</div>
|
||||
<div class="form-check dropdown-item">
|
||||
<input class="form-check-input" type="checkbox" id="adminConfStatusRemoved" ng-model="query.removed" />
|
||||
<label class="form-check-label" for="adminConfStatusRemoved">Removed</label>
|
||||
</div>
|
||||
<div class="form-check dropdown-item">
|
||||
<input class="form-check-input" type="checkbox" id="adminConfStatusError" ng-model="query.error" />
|
||||
<label class="form-check-label" for="adminConfStatusError">Error</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div class="paper-table paper-table-conferences w-100" role="table" aria-label="Conferences">
|
||||
<div class="paper-table-head" role="row">
|
||||
<div role="columnheader">Conference</div>
|
||||
<div role="columnheader">Status</div>
|
||||
<div role="columnheader" class="num">Repos</div>
|
||||
<div role="columnheader">Window</div>
|
||||
<div role="columnheader" aria-label="Actions"></div>
|
||||
</div>
|
||||
<div
|
||||
class="paper-table-row"
|
||||
role="row"
|
||||
ng-repeat="conference in conferences | filter:conferenceFiler | orderBy:orderBy as filteredConferences"
|
||||
>
|
||||
<div class="cell-anon" role="cell">
|
||||
<span class="type-badge type-repo">Conf</span>
|
||||
<div class="anon-text">
|
||||
<a class="repo-name" ng-href="/conference/{{conference.conferenceID}}" ng-bind="conference.name"></a>
|
||||
<div class="anon-sub">
|
||||
<span>{{conference.conferenceID}}</span> · <span>{{conference.price || 0 | number}} €</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="cell-status" role="cell">
|
||||
<span class="status-dot" ng-class="{'status-removed': conference.status == 'removed' || conference.status == 'expired', 'status-ready': conference.status == 'ready'}"></span>
|
||||
<span ng-bind="conference.status | title"></span>
|
||||
</div>
|
||||
<div class="cell-views num" role="cell" ng-bind="::conference.repositories.length || 0 | number"></div>
|
||||
<div class="cell-expires" role="cell">{{conference.startDate | date}} – {{conference.endDate | date}}</div>
|
||||
<div class="cell-actions" role="cell">
|
||||
<div class="dropdown">
|
||||
<button class="btn btn-icon-dots" type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" aria-label="Actions">
|
||||
<i class="fas fa-ellipsis-h" aria-hidden="true"></i>
|
||||
</button>
|
||||
<div class="dropdown-menu dropdown-menu-right">
|
||||
<a class="dropdown-item" href="/conference/{{conference.conferenceID}}/edit"><i class="far fa-edit"></i> Edit</a>
|
||||
<a class="dropdown-item" href="/conference/{{conference.conferenceID}}/"><i class="fa fa-eye"></i> View</a>
|
||||
<div class="dropdown-divider"></div>
|
||||
<a class="dropdown-item text-danger" href="#" ng-show="conference.status != 'removed'" ng-click="removeConference(conference)"><i class="fas fa-trash-alt"></i> Remove</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="paper-table-empty" ng-if="filteredConferences.length == 0">
|
||||
<i class="fas fa-inbox"></i>
|
||||
<span>No conferences match the current filters.</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="empty-state" ng-if="filteredConferences.length == 0">
|
||||
<i class="fas fa-chalkboard-teacher"></i>
|
||||
No conferences match the current filters.
|
||||
</div>
|
||||
|
||||
<!-- Bottom pagination -->
|
||||
<div class="admin-toolbar" ng-if="totalPage > 1" style="justify-content: center; border-bottom: none;">
|
||||
<div class="pagination-compact">
|
||||
<button class="btn btn-sm" ng-click="query.page = Math.max(1, query.page - 1)" ng-disabled="query.page <= 1">
|
||||
|
||||
@@ -1,195 +1,126 @@
|
||||
<div class="container page">
|
||||
<!-- Admin Navigation -->
|
||||
<div class="container paper-page">
|
||||
<div class="paper-crumbs">Admin / <span class="here">Queues</span></div>
|
||||
<h1 class="paper-page-title">Background <em>queues</em></h1>
|
||||
<p class="paper-page-lede">Watch anonymization jobs as they move through the workers.</p>
|
||||
|
||||
<nav class="admin-nav">
|
||||
<a href="/admin/">
|
||||
<i class="fas fa-code-branch"></i> Repositories
|
||||
</a>
|
||||
<a href="/admin/users">
|
||||
<i class="fas fa-users"></i> Users
|
||||
</a>
|
||||
<a href="/admin/conferences">
|
||||
<i class="fas fa-chalkboard-teacher"></i> Conferences
|
||||
</a>
|
||||
<a href="/admin/queues" class="active">
|
||||
<i class="fas fa-tasks"></i> Queues
|
||||
</a>
|
||||
<a href="/admin/"><i class="fas fa-code-branch"></i> Repositories</a>
|
||||
<a href="/admin/users"><i class="fas fa-users"></i> Users</a>
|
||||
<a href="/admin/conferences"><i class="fas fa-chalkboard-teacher"></i> Conferences</a>
|
||||
<a href="/admin/queues" class="active"><i class="fas fa-tasks"></i> Queues</a>
|
||||
</nav>
|
||||
|
||||
<!-- Summary Stats -->
|
||||
<div class="admin-stats">
|
||||
<div class="admin-stat-card">
|
||||
<div class="stat-value">{{downloadJobs.length || 0}}</div>
|
||||
<div class="stat-label">Download Jobs</div>
|
||||
<div class="stat-label">Download jobs</div>
|
||||
</div>
|
||||
<div class="admin-stat-card">
|
||||
<div class="stat-value">{{removeJobs.length || 0}}</div>
|
||||
<div class="stat-label">Remove Jobs</div>
|
||||
<div class="stat-label">Remove jobs</div>
|
||||
</div>
|
||||
<div class="admin-stat-card">
|
||||
<div class="stat-value">{{removeCaches.length || 0}}</div>
|
||||
<div class="stat-label">Cache Jobs</div>
|
||||
<div class="stat-label">Cache jobs</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Download Jobs -->
|
||||
<div class="admin-section-header">
|
||||
<h2><i class="fas fa-download mr-1"></i> Download Jobs</h2>
|
||||
<h2><i class="fas fa-download"></i> Download jobs</h2>
|
||||
<span class="section-count">{{downloadJobs.length || 0}}</span>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="queue-job-card"
|
||||
ng-repeat="job in downloadJobs as filteredDownloadJobs"
|
||||
>
|
||||
<div class="queue-job-card" ng-repeat="job in downloadJobs as filteredDownloadJobs">
|
||||
<div class="job-header">
|
||||
<div class="job-id">
|
||||
<span class="status-dot" ng-class="{'status-ready': job.progress.status == 'ready', 'status-error': job.progress.status == 'error', 'status-preparing': job.progress.status == 'preparing', 'status-removed': job.progress.status == 'removed'}"></span>
|
||||
<a target="_blank" ng-href="/r/{{job.id}}" ng-bind="job.id"></a>
|
||||
<span
|
||||
class="status-badge"
|
||||
ng-class="'status-' + job.progress.status"
|
||||
>{{job.progress.status | title}}</span>
|
||||
<span ng-bind="job.progress.status | title" style="font-family: var(--font-sans); color: var(--ink-muted); font-size: 12px;"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="job-timestamps">
|
||||
<span ng-if="job.timestamp">
|
||||
<i class="fas fa-clock"></i> Created: {{job.timestamp | humanTime}}
|
||||
</span>
|
||||
<span ng-if="job.processedOn">
|
||||
<i class="fas fa-cog"></i> Processed: {{job.processedOn | humanTime}}
|
||||
</span>
|
||||
<span ng-if="job.finishedOn">
|
||||
<i class="fas fa-check"></i> Finished: {{job.finishedOn | humanTime}}
|
||||
</span>
|
||||
<span ng-if="job.timestamp"><i class="fas fa-clock"></i> Created: {{job.timestamp | humanTime}}</span>
|
||||
<span ng-if="job.processedOn"><i class="fas fa-cog"></i> Processed: {{job.processedOn | humanTime}}</span>
|
||||
<span ng-if="job.finishedOn"><i class="fas fa-check"></i> Finished: {{job.finishedOn | humanTime}}</span>
|
||||
</div>
|
||||
<div ng-if="job.stacktrace.length">
|
||||
<pre
|
||||
ng-repeat="stack in job.stacktrace track by $index"
|
||||
style="font-size: 0.8rem; max-height: 100px; overflow: auto; margin: 6px 0 0 0"
|
||||
><code ng-bind="stack"></code></pre>
|
||||
<pre ng-repeat="stack in job.stacktrace track by $index" style="font-size: 0.8rem; max-height: 100px; overflow: auto; margin: 6px 0 0 0"><code ng-bind="stack"></code></pre>
|
||||
</div>
|
||||
<div class="job-actions">
|
||||
<button class="btn btn-sm" ng-click="retryJob('download', job)">
|
||||
<i class="fas fa-sync"></i> Retry
|
||||
</button>
|
||||
<button class="btn btn-sm" ng-click="removeJob('download', job)">
|
||||
<i class="fas fa-trash-alt"></i> Remove
|
||||
</button>
|
||||
<a class="btn btn-sm" href="/anonymize/{{job.id}}">
|
||||
<i class="far fa-edit"></i> Edit
|
||||
</a>
|
||||
<button class="btn btn-sm" ng-click="retryJob('download', job)"><i class="fas fa-sync"></i> Retry</button>
|
||||
<button class="btn btn-sm" ng-click="removeJob('download', job)"><i class="fas fa-trash-alt"></i> Remove</button>
|
||||
<a class="btn btn-sm" href="/anonymize/{{job.id}}"><i class="far fa-edit"></i> Edit</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="empty-state" ng-if="filteredDownloadJobs.length == 0" style="padding: 20px">
|
||||
<div class="paper-table-empty" ng-if="filteredDownloadJobs.length == 0" style="border:1px solid var(--border-color);border-radius:10px;background:var(--paper-card);">
|
||||
<i class="fas fa-check-circle"></i>
|
||||
No download jobs in the queue.
|
||||
<span>No download jobs in the queue.</span>
|
||||
</div>
|
||||
|
||||
<!-- Remove Jobs -->
|
||||
<div class="admin-section-header">
|
||||
<h2><i class="fas fa-trash mr-1"></i> Remove Jobs</h2>
|
||||
<h2><i class="fas fa-trash"></i> Remove jobs</h2>
|
||||
<span class="section-count">{{removeJobs.length || 0}}</span>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="queue-job-card"
|
||||
ng-repeat="job in removeJobs as filteredRemoveJobs"
|
||||
>
|
||||
<div class="queue-job-card" ng-repeat="job in removeJobs as filteredRemoveJobs">
|
||||
<div class="job-header">
|
||||
<div class="job-id">
|
||||
<span class="status-dot" ng-class="{'status-ready': job.progress.status == 'ready', 'status-error': job.progress.status == 'error', 'status-preparing': job.progress.status == 'preparing', 'status-removed': job.progress.status == 'removed'}"></span>
|
||||
<a target="_blank" ng-href="/r/{{job.id}}" ng-bind="job.id"></a>
|
||||
<span
|
||||
class="status-badge"
|
||||
ng-class="'status-' + job.progress.status"
|
||||
>{{job.progress.status | title}}</span>
|
||||
<span ng-bind="job.progress.status | title" style="font-family: var(--font-sans); color: var(--ink-muted); font-size: 12px;"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="job-timestamps">
|
||||
<span ng-if="job.timestamp">
|
||||
<i class="fas fa-clock"></i> Created: {{job.timestamp | humanTime}}
|
||||
</span>
|
||||
<span ng-if="job.processedOn">
|
||||
<i class="fas fa-cog"></i> Processed: {{job.processedOn | humanTime}}
|
||||
</span>
|
||||
<span ng-if="job.finishedOn">
|
||||
<i class="fas fa-check"></i> Finished: {{job.finishedOn | humanTime}}
|
||||
</span>
|
||||
<span ng-if="job.timestamp"><i class="fas fa-clock"></i> Created: {{job.timestamp | humanTime}}</span>
|
||||
<span ng-if="job.processedOn"><i class="fas fa-cog"></i> Processed: {{job.processedOn | humanTime}}</span>
|
||||
<span ng-if="job.finishedOn"><i class="fas fa-check"></i> Finished: {{job.finishedOn | humanTime}}</span>
|
||||
</div>
|
||||
<div ng-if="job.stacktrace.length">
|
||||
<pre
|
||||
ng-repeat="stack in job.stacktrace track by $index"
|
||||
style="font-size: 0.8rem; max-height: 100px; overflow: auto; margin: 6px 0 0 0"
|
||||
><code ng-bind="stack"></code></pre>
|
||||
<pre ng-repeat="stack in job.stacktrace track by $index" style="font-size: 0.8rem; max-height: 100px; overflow: auto; margin: 6px 0 0 0"><code ng-bind="stack"></code></pre>
|
||||
</div>
|
||||
<div class="job-actions">
|
||||
<button class="btn btn-sm" ng-click="retryJob('remove', job)">
|
||||
<i class="fas fa-sync"></i> Retry
|
||||
</button>
|
||||
<button class="btn btn-sm" ng-click="removeJob('remove', job)">
|
||||
<i class="fas fa-trash-alt"></i> Remove
|
||||
</button>
|
||||
<a class="btn btn-sm" href="/anonymize/{{job.id}}">
|
||||
<i class="far fa-edit"></i> Edit
|
||||
</a>
|
||||
<button class="btn btn-sm" ng-click="retryJob('remove', job)"><i class="fas fa-sync"></i> Retry</button>
|
||||
<button class="btn btn-sm" ng-click="removeJob('remove', job)"><i class="fas fa-trash-alt"></i> Remove</button>
|
||||
<a class="btn btn-sm" href="/anonymize/{{job.id}}"><i class="far fa-edit"></i> Edit</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="empty-state" ng-if="filteredRemoveJobs.length == 0" style="padding: 20px">
|
||||
<div class="paper-table-empty" ng-if="filteredRemoveJobs.length == 0" style="border:1px solid var(--border-color);border-radius:10px;background:var(--paper-card);">
|
||||
<i class="fas fa-check-circle"></i>
|
||||
No remove jobs in the queue.
|
||||
<span>No remove jobs in the queue.</span>
|
||||
</div>
|
||||
|
||||
<!-- Cache Jobs -->
|
||||
<div class="admin-section-header">
|
||||
<h2><i class="fas fa-broom mr-1"></i> Cache Cleanup Jobs</h2>
|
||||
<h2><i class="fas fa-broom"></i> Cache cleanup jobs</h2>
|
||||
<span class="section-count">{{removeCaches.length || 0}}</span>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="queue-job-card"
|
||||
ng-repeat="job in removeCaches as filteredRemoveCache"
|
||||
>
|
||||
<div class="queue-job-card" ng-repeat="job in removeCaches as filteredRemoveCache">
|
||||
<div class="job-header">
|
||||
<div class="job-id">
|
||||
<span class="status-dot" ng-class="{'status-ready': job.progress.status == 'ready', 'status-error': job.progress.status == 'error', 'status-preparing': job.progress.status == 'preparing', 'status-removed': job.progress.status == 'removed'}"></span>
|
||||
<a target="_blank" ng-href="/r/{{job.id}}" ng-bind="job.id"></a>
|
||||
<span
|
||||
class="status-badge"
|
||||
ng-class="'status-' + job.progress.status"
|
||||
>{{job.progress.status | title}}</span>
|
||||
<span ng-bind="job.progress.status | title" style="font-family: var(--font-sans); color: var(--ink-muted); font-size: 12px;"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="job-timestamps">
|
||||
<span ng-if="job.timestamp">
|
||||
<i class="fas fa-clock"></i> Created: {{job.timestamp | humanTime}}
|
||||
</span>
|
||||
<span ng-if="job.processedOn">
|
||||
<i class="fas fa-cog"></i> Processed: {{job.processedOn | humanTime}}
|
||||
</span>
|
||||
<span ng-if="job.finishedOn">
|
||||
<i class="fas fa-check"></i> Finished: {{job.finishedOn | humanTime}}
|
||||
</span>
|
||||
<span ng-if="job.timestamp"><i class="fas fa-clock"></i> Created: {{job.timestamp | humanTime}}</span>
|
||||
<span ng-if="job.processedOn"><i class="fas fa-cog"></i> Processed: {{job.processedOn | humanTime}}</span>
|
||||
<span ng-if="job.finishedOn"><i class="fas fa-check"></i> Finished: {{job.finishedOn | humanTime}}</span>
|
||||
</div>
|
||||
<div ng-if="job.stacktrace.length">
|
||||
<pre
|
||||
ng-repeat="stack in job.stacktrace track by $index"
|
||||
style="font-size: 0.8rem; max-height: 100px; overflow: auto; margin: 6px 0 0 0"
|
||||
><code ng-bind="stack"></code></pre>
|
||||
<pre ng-repeat="stack in job.stacktrace track by $index" style="font-size: 0.8rem; max-height: 100px; overflow: auto; margin: 6px 0 0 0"><code ng-bind="stack"></code></pre>
|
||||
</div>
|
||||
<div class="job-actions">
|
||||
<button class="btn btn-sm" ng-click="retryJob('cache', job)">
|
||||
<i class="fas fa-sync"></i> Retry
|
||||
</button>
|
||||
<button class="btn btn-sm" ng-click="removeJob('cache', job)">
|
||||
<i class="fas fa-trash-alt"></i> Remove
|
||||
</button>
|
||||
<a class="btn btn-sm" href="/anonymize/{{job.id}}">
|
||||
<i class="far fa-edit"></i> Edit
|
||||
</a>
|
||||
<button class="btn btn-sm" ng-click="retryJob('cache', job)"><i class="fas fa-sync"></i> Retry</button>
|
||||
<button class="btn btn-sm" ng-click="removeJob('cache', job)"><i class="fas fa-trash-alt"></i> Remove</button>
|
||||
<a class="btn btn-sm" href="/anonymize/{{job.id}}"><i class="far fa-edit"></i> Edit</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="empty-state" ng-if="filteredRemoveCache.length == 0" style="padding: 20px">
|
||||
<div class="paper-table-empty" ng-if="filteredRemoveCache.length == 0" style="border:1px solid var(--border-color);border-radius:10px;background:var(--paper-card);">
|
||||
<i class="fas fa-check-circle"></i>
|
||||
No cache cleanup jobs in the queue.
|
||||
<span>No cache cleanup jobs in the queue.</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,251 +1,160 @@
|
||||
<div class="container page">
|
||||
<!-- Admin Navigation -->
|
||||
<div class="container paper-page">
|
||||
<div class="paper-crumbs">Admin / <span class="here">Repositories</span></div>
|
||||
<h1 class="paper-page-title">Anonymized <em>repositories</em></h1>
|
||||
<p class="paper-page-lede">Every anonymization across every user and conference.</p>
|
||||
|
||||
<nav class="admin-nav">
|
||||
<a href="/admin/" class="active">
|
||||
<i class="fas fa-code-branch"></i> Repositories
|
||||
</a>
|
||||
<a href="/admin/users">
|
||||
<i class="fas fa-users"></i> Users
|
||||
</a>
|
||||
<a href="/admin/conferences">
|
||||
<i class="fas fa-chalkboard-teacher"></i> Conferences
|
||||
</a>
|
||||
<a href="/admin/queues">
|
||||
<i class="fas fa-tasks"></i> Queues
|
||||
</a>
|
||||
<a href="/admin/" class="active"><i class="fas fa-code-branch"></i> Repositories</a>
|
||||
<a href="/admin/users"><i class="fas fa-users"></i> Users</a>
|
||||
<a href="/admin/conferences"><i class="fas fa-chalkboard-teacher"></i> Conferences</a>
|
||||
<a href="/admin/queues"><i class="fas fa-tasks"></i> Queues</a>
|
||||
</nav>
|
||||
|
||||
<!-- Summary Stats -->
|
||||
<div class="admin-stats">
|
||||
<div class="admin-stat-card">
|
||||
<div class="stat-value" ng-bind="total >= 0 ? (total | number) : '...'"></div>
|
||||
<div class="stat-label">Total Repos</div>
|
||||
<div class="stat-label">Total repos</div>
|
||||
</div>
|
||||
<div class="admin-stat-card">
|
||||
<div class="stat-value">{{query.page}}/{{totalPage || '...'}}</div>
|
||||
<div class="stat-label">Current Page</div>
|
||||
<div class="stat-label">Current page</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Toolbar -->
|
||||
<div class="admin-toolbar">
|
||||
<input
|
||||
type="search"
|
||||
class="form-control"
|
||||
aria-label="Search repositories..."
|
||||
placeholder="Search repositories..."
|
||||
autocomplete="off"
|
||||
ng-model="query.search"
|
||||
/>
|
||||
|
||||
<div class="pagination-compact">
|
||||
<button class="btn btn-sm" ng-click="query.page = Math.max(1, query.page - 1)" ng-disabled="query.page <= 1">
|
||||
<i class="fas fa-chevron-left"></i>
|
||||
</button>
|
||||
<form class="w-100 dashboard-filter-row" aria-label="Repositories" accept-charset="UTF-8">
|
||||
<div class="search-wrap">
|
||||
<input
|
||||
type="number"
|
||||
class="form-control form-control-sm"
|
||||
ng-model="query.page"
|
||||
min="1"
|
||||
max="{{totalPage}}"
|
||||
type="search"
|
||||
class="form-control"
|
||||
aria-label="Search repositories"
|
||||
placeholder="Search repositories…"
|
||||
autocomplete="off"
|
||||
ng-model="query.search"
|
||||
/>
|
||||
<span>/{{totalPage}}</span>
|
||||
<button class="btn btn-sm" ng-click="query.page = Math.min(totalPage, query.page + 1)" ng-disabled="query.page >= totalPage">
|
||||
<i class="fas fa-chevron-right"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="dropdown">
|
||||
<button
|
||||
class="btn btn-sm dropdown-toggle"
|
||||
type="button"
|
||||
id="dropdownSort"
|
||||
data-toggle="dropdown"
|
||||
aria-haspopup="true"
|
||||
aria-expanded="false"
|
||||
>
|
||||
<i class="fas fa-sort"></i> Sort
|
||||
</button>
|
||||
<div class="dropdown-menu" aria-labelledby="dropdownSort">
|
||||
<h6 class="dropdown-header">Sort by</h6>
|
||||
<a class="dropdown-item" href="#" ng-click="query.sort = 'source.repositoryName'">
|
||||
<i class="fas fa-check" ng-show="query.sort == 'source.repositoryName'"></i> Repository
|
||||
</a>
|
||||
<a class="dropdown-item" href="#" ng-click="query.sort = 'anonymizeDate'">
|
||||
<i class="fas fa-check" ng-show="query.sort == 'anonymizeDate'"></i> Anonymize Date
|
||||
</a>
|
||||
<a class="dropdown-item" href="#" ng-click="query.sort = 'status'">
|
||||
<i class="fas fa-check" ng-show="query.sort == 'status'"></i> Status
|
||||
</a>
|
||||
<a class="dropdown-item" href="#" ng-click="query.sort = 'lastView'">
|
||||
<i class="fas fa-check" ng-show="query.sort == 'lastView'"></i> Last View
|
||||
</a>
|
||||
<a class="dropdown-item" href="#" ng-click="query.sort = 'pageView'">
|
||||
<i class="fas fa-check" ng-show="query.sort == 'pageView'"></i> Page Views
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="dropdown">
|
||||
<button
|
||||
class="btn btn-sm dropdown-toggle"
|
||||
type="button"
|
||||
id="dropdownStatus"
|
||||
data-toggle="dropdown"
|
||||
aria-haspopup="true"
|
||||
aria-expanded="false"
|
||||
>
|
||||
<i class="fas fa-filter"></i> Status
|
||||
</button>
|
||||
<div class="dropdown-menu" aria-labelledby="dropdownStatus">
|
||||
<h6 class="dropdown-header">Filter by status</h6>
|
||||
<label class="dropdown-item mb-0">
|
||||
<input type="checkbox" ng-model="query.ready" /> Ready
|
||||
</label>
|
||||
<label class="dropdown-item mb-0">
|
||||
<input type="checkbox" ng-model="query.preparing" /> Preparing
|
||||
</label>
|
||||
<label class="dropdown-item mb-0">
|
||||
<input type="checkbox" ng-model="query.expired" /> Expired
|
||||
</label>
|
||||
<label class="dropdown-item mb-0">
|
||||
<input type="checkbox" ng-model="query.removed" /> Removed
|
||||
</label>
|
||||
<label class="dropdown-item mb-0">
|
||||
<input type="checkbox" ng-model="query.error" /> Error
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Repository List -->
|
||||
<div
|
||||
class="admin-list-item"
|
||||
ng-repeat="repo in repositories | filter:repoFiler | orderBy:orderBy as filteredRepositories"
|
||||
>
|
||||
<div class="item-main">
|
||||
<div class="item-title">
|
||||
<a target="_blank" ng-href="/r/{{repo.repoId}}" ng-bind="repo.repoId"></a>
|
||||
<span
|
||||
class="status-badge"
|
||||
ng-class="'status-' + repo.status"
|
||||
>{{repo.status | title}}</span>
|
||||
<span
|
||||
ng-if="repo.status == 'error'"
|
||||
style="font-size: 0.8rem; color: #dc3545;"
|
||||
ng-bind="repo.statusMessage"
|
||||
></span>
|
||||
</div>
|
||||
<div class="item-meta">
|
||||
<span>
|
||||
<i class="fab fa-github"></i>
|
||||
<a
|
||||
href="https://github.com/{{repo.source.repositoryName}}/"
|
||||
ng-bind="repo.source.repositoryName"
|
||||
></a>
|
||||
</span>
|
||||
<span ng-if="repo.options.update">
|
||||
<i class="fas fa-code-branch"></i>
|
||||
<a
|
||||
href="https://github.com/{{repo.source.fullName}}/tree/{{repo.source.branch}}"
|
||||
ng-bind="repo.source.branch"
|
||||
></a>
|
||||
</span>
|
||||
<span ng-if="!repo.options.update">
|
||||
@<a
|
||||
href="https://github.com/{{repo.source.fullName}}/tree/{{repo.source.commit}}"
|
||||
ng-bind="repo.source.commit.substring(0, 8)"
|
||||
></a>
|
||||
</span>
|
||||
<span>anonymized {{repo.anonymizeDate | humanTime}}</span>
|
||||
</div>
|
||||
<div class="item-meta" style="margin-top: 4px">
|
||||
<span ng-if="::repo.conference">
|
||||
<i class="fas fa-chalkboard-teacher"></i> {{repo.conference}}
|
||||
</span>
|
||||
<span title="Terms: {{::repo.options.terms.join(', ')}}">
|
||||
<i class="fas fa-shield-alt"></i> {{::repo.options.terms.length | number}} terms
|
||||
</span>
|
||||
<span title="Size: {{::repo.size.storage | humanFileSize}}">
|
||||
<i class="fas fa-database"></i> {{::repo.size.storage | humanFileSize}}
|
||||
</span>
|
||||
<span>
|
||||
<i class="far fa-eye"></i> {{::repo.pageView || 0 | number}} views
|
||||
</span>
|
||||
<span>
|
||||
<i class="far fa-calendar-alt"></i> Last: {{::repo.lastView | humanTime}}
|
||||
</span>
|
||||
<span ng-if="repo.options.expirationMode != 'never' && repo.status == 'ready'">
|
||||
<i class="far fa-clock"></i> Expire: {{repo.options.expirationDate | humanTime}}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="item-actions">
|
||||
<div class="dropdown">
|
||||
<button
|
||||
class="btn dropdown-toggle btn-sm"
|
||||
type="button"
|
||||
data-toggle="dropdown"
|
||||
aria-haspopup="true"
|
||||
aria-expanded="false"
|
||||
>
|
||||
Actions
|
||||
<div class="d-flex flex-wrap" style="gap: 8px; align-items: center;">
|
||||
<div class="pagination-compact">
|
||||
<button class="btn btn-sm" type="button" ng-click="query.page = Math.max(1, query.page - 1)" ng-disabled="query.page <= 1">
|
||||
<i class="fas fa-chevron-left"></i>
|
||||
</button>
|
||||
<div class="dropdown-menu dropdown-menu-right">
|
||||
<a class="dropdown-item" href="/anonymize/{{repo.repoId}}">
|
||||
<i class="far fa-edit"></i> Edit
|
||||
<input
|
||||
type="number"
|
||||
class="form-control form-control-sm"
|
||||
ng-model="query.page"
|
||||
min="1"
|
||||
max="{{totalPage}}"
|
||||
/>
|
||||
<span>/{{totalPage}}</span>
|
||||
<button class="btn btn-sm" type="button" ng-click="query.page = Math.min(totalPage, query.page + 1)" ng-disabled="query.page >= totalPage">
|
||||
<i class="fas fa-chevron-right"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="dropdown">
|
||||
<button class="btn 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">Sort by</h6>
|
||||
<a class="dropdown-item" href="#" ng-click="query.sort = 'source.repositoryName'">
|
||||
<i class="fas fa-check" ng-show="query.sort == 'source.repositoryName'"></i> Repository
|
||||
</a>
|
||||
<a class="dropdown-item" href="/r/{{repo.repoId}}/">
|
||||
<i class="fa fa-eye"></i> View Repo
|
||||
<a class="dropdown-item" href="#" ng-click="query.sort = 'anonymizeDate'">
|
||||
<i class="fas fa-check" ng-show="query.sort == 'anonymizeDate'"></i> Anonymize date
|
||||
</a>
|
||||
<a
|
||||
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
|
||||
<a class="dropdown-item" href="#" ng-click="query.sort = 'status'">
|
||||
<i class="fas fa-check" ng-show="query.sort == 'status'"></i> Status
|
||||
</a>
|
||||
<div class="dropdown-divider"></div>
|
||||
<a
|
||||
class="dropdown-item"
|
||||
href="#"
|
||||
ng-show="repo.status == 'ready' || repo.status == 'error'"
|
||||
ng-click="updateRepository(repo)"
|
||||
>
|
||||
<i class="fas fa-sync"></i> Force Update
|
||||
<a class="dropdown-item" href="#" ng-click="query.sort = 'lastView'">
|
||||
<i class="fas fa-check" ng-show="query.sort == 'lastView'"></i> Last view
|
||||
</a>
|
||||
<a
|
||||
class="dropdown-item"
|
||||
href="#"
|
||||
ng-show="repo.status == 'removed'"
|
||||
ng-click="updateRepository(repo)"
|
||||
>
|
||||
<i class="fas fa-check-circle"></i> Enable
|
||||
</a>
|
||||
<div class="dropdown-divider"></div>
|
||||
<a class="dropdown-item" href="#" ng-click="removeCache(repo)">
|
||||
<i class="fas fa-broom"></i> Remove Cache
|
||||
</a>
|
||||
<a
|
||||
class="dropdown-item text-danger"
|
||||
href="#"
|
||||
ng-show="repo.status == 'ready'"
|
||||
ng-click="removeRepository(repo)"
|
||||
>
|
||||
<i class="fas fa-trash-alt"></i> Remove
|
||||
<a class="dropdown-item" href="#" ng-click="query.sort = 'pageView'">
|
||||
<i class="fas fa-check" ng-show="query.sort == 'pageView'"></i> Page views
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="dropdown">
|
||||
<button class="btn 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">Filter by status</h6>
|
||||
<div class="form-check dropdown-item">
|
||||
<input class="form-check-input" type="checkbox" id="adminStatusReady" ng-model="query.ready" />
|
||||
<label class="form-check-label" for="adminStatusReady">Ready</label>
|
||||
</div>
|
||||
<div class="form-check dropdown-item">
|
||||
<input class="form-check-input" type="checkbox" id="adminStatusPreparing" ng-model="query.preparing" />
|
||||
<label class="form-check-label" for="adminStatusPreparing">Preparing</label>
|
||||
</div>
|
||||
<div class="form-check dropdown-item">
|
||||
<input class="form-check-input" type="checkbox" id="adminStatusExpired" ng-model="query.expired" />
|
||||
<label class="form-check-label" for="adminStatusExpired">Expired</label>
|
||||
</div>
|
||||
<div class="form-check dropdown-item">
|
||||
<input class="form-check-input" type="checkbox" id="adminStatusRemoved" ng-model="query.removed" />
|
||||
<label class="form-check-label" for="adminStatusRemoved">Removed</label>
|
||||
</div>
|
||||
<div class="form-check dropdown-item">
|
||||
<input class="form-check-input" type="checkbox" id="adminStatusError" ng-model="query.error" />
|
||||
<label class="form-check-label" for="adminStatusError">Error</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div class="paper-table paper-table-repos w-100" role="table" aria-label="Repositories">
|
||||
<div class="paper-table-head" role="row">
|
||||
<div role="columnheader">Repository</div>
|
||||
<div role="columnheader">Status</div>
|
||||
<div role="columnheader" class="num">Views</div>
|
||||
<div role="columnheader">Anonymized</div>
|
||||
<div role="columnheader" aria-label="Actions"></div>
|
||||
</div>
|
||||
<div
|
||||
class="paper-table-row"
|
||||
role="row"
|
||||
ng-class="{'repo-inactive': repo.status == 'expired' || repo.status == 'removed'}"
|
||||
ng-repeat="repo in repositories | filter:repoFiler | orderBy:orderBy as filteredRepositories"
|
||||
>
|
||||
<div class="cell-anon" role="cell">
|
||||
<span class="type-badge type-repo">Repo</span>
|
||||
<div class="anon-text">
|
||||
<a class="repo-name" target="_blank" ng-href="/r/{{repo.repoId}}" ng-bind="repo.repoId"></a>
|
||||
<div class="anon-sub">
|
||||
<a href="https://github.com/{{repo.source.repositoryName}}/" ng-bind="repo.source.repositoryName"></a><span ng-if="repo.options.update"> · <a href="https://github.com/{{repo.source.fullName}}/tree/{{repo.source.branch}}" ng-bind="repo.source.branch"></a></span><span ng-if="!repo.options.update"> · @<a href="https://github.com/{{repo.source.fullName}}/tree/{{repo.source.commit}}" ng-bind="repo.source.commit.substring(0, 8)"></a></span><span ng-if="::repo.conference"> · <i class="fas fa-chalkboard-teacher"></i> {{repo.conference}}</span><span> · {{::repo.size.storage | humanFileSize}}</span><span> · {{::repo.options.terms.length | number}} terms</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="cell-status" role="cell">
|
||||
<span class="status-dot" ng-class="{'status-removed': repo.status == 'removed' || repo.status == 'expired', 'status-ready': repo.status == 'ready', 'status-error': repo.status == 'error', 'status-preparing': repo.status == 'preparing'}"></span>
|
||||
<span ng-bind="repo.status | title"></span>
|
||||
</div>
|
||||
<div class="cell-views num" role="cell" ng-bind="::repo.pageView || 0 | number"></div>
|
||||
<div class="cell-expires" role="cell" ng-bind="repo.anonymizeDate | humanTime"></div>
|
||||
<div class="cell-actions" role="cell">
|
||||
<div class="dropdown">
|
||||
<button class="btn btn-icon-dots" type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" aria-label="Actions">
|
||||
<i class="fas fa-ellipsis-h" aria-hidden="true"></i>
|
||||
</button>
|
||||
<div class="dropdown-menu dropdown-menu-right">
|
||||
<a class="dropdown-item" href="/anonymize/{{repo.repoId}}"><i class="far fa-edit"></i> Edit</a>
|
||||
<a class="dropdown-item" href="/r/{{repo.repoId}}/"><i class="fa fa-eye"></i> View repo</a>
|
||||
<a 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</a>
|
||||
<div class="dropdown-divider"></div>
|
||||
<a class="dropdown-item" href="#" ng-show="repo.status == 'ready' || repo.status == 'error'" ng-click="updateRepository(repo)"><i class="fas fa-sync"></i> Force update</a>
|
||||
<a class="dropdown-item" href="#" ng-show="repo.status == 'removed'" ng-click="updateRepository(repo)"><i class="fas fa-check-circle"></i> Enable</a>
|
||||
<div class="dropdown-divider"></div>
|
||||
<a class="dropdown-item" href="#" ng-click="removeCache(repo)"><i class="fas fa-broom"></i> Remove cache</a>
|
||||
<a class="dropdown-item text-danger" href="#" ng-show="repo.status == 'ready'" ng-click="removeRepository(repo)"><i class="fas fa-trash-alt"></i> Remove</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="paper-table-empty" ng-if="filteredRepositories.length == 0">
|
||||
<i class="fas fa-inbox"></i>
|
||||
<span>No repositories match the current filters.</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="empty-state" ng-if="filteredRepositories.length == 0">
|
||||
<i class="fas fa-code-branch"></i>
|
||||
No repositories match the current filters.
|
||||
</div>
|
||||
|
||||
<!-- Bottom pagination -->
|
||||
<div class="admin-toolbar" ng-if="totalPage > 1" style="justify-content: center; border-bottom: none;">
|
||||
<div class="pagination-compact">
|
||||
<button class="btn btn-sm" ng-click="query.page = Math.max(1, query.page - 1)" ng-disabled="query.page <= 1">
|
||||
|
||||
+126
-218
@@ -1,40 +1,26 @@
|
||||
<div class="container page">
|
||||
<!-- Admin Navigation -->
|
||||
<div class="container paper-page">
|
||||
<div class="paper-crumbs"><a href="/admin/users">Users</a> / <span class="here">{{userInfo.username || 'Profile'}}</span></div>
|
||||
<h1 class="paper-page-title">User <em>profile</em></h1>
|
||||
<p class="paper-page-lede">Inspect activity, quota, and repositories for a single account.</p>
|
||||
|
||||
<nav class="admin-nav">
|
||||
<a href="/admin/">
|
||||
<i class="fas fa-code-branch"></i> Repositories
|
||||
</a>
|
||||
<a href="/admin/users" class="active">
|
||||
<i class="fas fa-users"></i> Users
|
||||
</a>
|
||||
<a href="/admin/conferences">
|
||||
<i class="fas fa-chalkboard-teacher"></i> Conferences
|
||||
</a>
|
||||
<a href="/admin/queues">
|
||||
<i class="fas fa-tasks"></i> Queues
|
||||
</a>
|
||||
<a href="/admin/"><i class="fas fa-code-branch"></i> Repositories</a>
|
||||
<a href="/admin/users" class="active"><i class="fas fa-users"></i> Users</a>
|
||||
<a href="/admin/conferences"><i class="fas fa-chalkboard-teacher"></i> Conferences</a>
|
||||
<a href="/admin/queues"><i class="fas fa-tasks"></i> Queues</a>
|
||||
</nav>
|
||||
|
||||
<!-- User Detail Card -->
|
||||
<div class="user-detail-card" ng-if="userInfo">
|
||||
<div class="user-header">
|
||||
<img
|
||||
ng-src="{{userInfo.photo}}"
|
||||
ng-if="userInfo.photo"
|
||||
width="48"
|
||||
height="48"
|
||||
/>
|
||||
<img ng-src="{{userInfo.photo}}" ng-if="userInfo.photo" width="56" height="56" />
|
||||
<div>
|
||||
<h1>
|
||||
{{userInfo.username}}
|
||||
<span
|
||||
class="status-badge"
|
||||
ng-class="'status-' + userInfo.status"
|
||||
>{{userInfo.status | title}}</span>
|
||||
<span
|
||||
class="status-badge status-active"
|
||||
ng-if="userInfo.isAdmin"
|
||||
>Admin</span>
|
||||
<span class="status-dot-wrap">
|
||||
<span class="status-dot" ng-class="{'status-ready': userInfo.status == 'active', 'status-removed': userInfo.status != 'active'}"></span>
|
||||
<span ng-bind="userInfo.status | title"></span>
|
||||
</span>
|
||||
<span class="type-badge type-repo" ng-if="userInfo.isAdmin">Admin</span>
|
||||
</h1>
|
||||
</div>
|
||||
</div>
|
||||
@@ -46,8 +32,8 @@
|
||||
<div class="detail-label">Email</div>
|
||||
<div class="detail-value">{{userInfo.emails[0].email}}</div>
|
||||
|
||||
<div class="detail-label">Access Token</div>
|
||||
<div class="detail-value" style="font-family: monospace; font-size: 0.85rem;">{{userInfo.accessTokens.github}}</div>
|
||||
<div class="detail-label">Access token</div>
|
||||
<div class="detail-value" style="font-family: var(--font-mono); font-size: 0.85rem;">{{userInfo.accessTokens.github}}</div>
|
||||
|
||||
<div class="detail-label">GitHub</div>
|
||||
<div class="detail-value">
|
||||
@@ -56,219 +42,141 @@
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="detail-label">GitHub Repos</div>
|
||||
<div class="detail-label">GitHub repos</div>
|
||||
<div class="detail-value">
|
||||
{{userInfo.repositories.length}} repositories
|
||||
<button
|
||||
class="btn btn-sm ml-2"
|
||||
ng-click="showRepos = !showRepos"
|
||||
>
|
||||
<button class="btn btn-sm ml-2" ng-click="showRepos = !showRepos">
|
||||
{{showRepos ? 'Hide' : 'Show'}}
|
||||
</button>
|
||||
<button
|
||||
class="btn btn-primary btn-sm ml-1"
|
||||
ng-click="getGitHubRepositories()"
|
||||
>
|
||||
<button class="btn btn-sm ml-1" ng-click="getGitHubRepositories()">
|
||||
<i class="fas fa-sync"></i> Refresh
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- GitHub repos expandable list -->
|
||||
<div ng-if="showRepos" style="margin-top: 16px">
|
||||
<div
|
||||
class="admin-list-item"
|
||||
ng-repeat="repo in userInfo.repositories"
|
||||
>
|
||||
<div class="item-main">
|
||||
<div class="item-title" style="font-size: 0.9rem">{{repo.name}}</div>
|
||||
<div class="item-meta">
|
||||
<span title="Size">
|
||||
<i class="fas fa-database"></i> {{::repo.size | humanFileSize}}
|
||||
</span>
|
||||
<div ng-if="showRepos" style="margin-top: 20px">
|
||||
<div class="paper-section-eyebrow">GitHub repositories</div>
|
||||
<div class="paper-table w-100" style="margin-top: 10px;">
|
||||
<div class="paper-table-row" ng-repeat="repo in userInfo.repositories" style="grid-template-columns: 1fr 160px;">
|
||||
<div class="cell-anon" role="cell">
|
||||
<span class="type-badge type-repo">Repo</span>
|
||||
<div class="anon-text">
|
||||
<span class="repo-name" ng-bind="repo.name"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="cell-expires" role="cell">
|
||||
<i class="fas fa-database"></i> {{::repo.size | humanFileSize}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Repositories Section -->
|
||||
<div class="admin-section-header">
|
||||
<h2><i class="fas fa-code-branch mr-1"></i> Anonymized Repositories</h2>
|
||||
<h2><i class="fas fa-code-branch"></i> Anonymized repositories</h2>
|
||||
<span class="section-count">{{repositories.length}}</span>
|
||||
</div>
|
||||
|
||||
<!-- Toolbar -->
|
||||
<div class="admin-toolbar">
|
||||
<input
|
||||
type="search"
|
||||
class="form-control"
|
||||
aria-label="Search repositories..."
|
||||
placeholder="Search repositories..."
|
||||
autocomplete="off"
|
||||
ng-model="search"
|
||||
/>
|
||||
|
||||
<div class="dropdown">
|
||||
<button
|
||||
class="btn btn-sm dropdown-toggle"
|
||||
type="button"
|
||||
id="dropdownSort"
|
||||
data-toggle="dropdown"
|
||||
>
|
||||
<i class="fas fa-sort"></i> Sort
|
||||
</button>
|
||||
<div class="dropdown-menu" aria-labelledby="dropdownSort">
|
||||
<h6 class="dropdown-header">Sort by</h6>
|
||||
<a class="dropdown-item" href="#" ng-click="orderBy = '-anonymizeDate'">
|
||||
<i class="fas fa-check" ng-show="orderBy == '-anonymizeDate'"></i> Anonymize Date
|
||||
</a>
|
||||
<a class="dropdown-item" href="#" ng-click="orderBy = 'repoId'">
|
||||
<i class="fas fa-check" ng-show="orderBy == 'repoId'"></i> ID
|
||||
</a>
|
||||
<a class="dropdown-item" href="#" ng-click="orderBy = '-status'">
|
||||
<i class="fas fa-check" ng-show="orderBy == '-status'"></i> Status
|
||||
</a>
|
||||
</div>
|
||||
<form class="w-100 dashboard-filter-row" aria-label="Repositories" accept-charset="UTF-8">
|
||||
<div class="search-wrap">
|
||||
<input
|
||||
type="search"
|
||||
class="form-control"
|
||||
aria-label="Search repositories"
|
||||
placeholder="Search repositories…"
|
||||
autocomplete="off"
|
||||
ng-model="search"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="dropdown">
|
||||
<button
|
||||
class="btn btn-sm dropdown-toggle"
|
||||
type="button"
|
||||
id="dropdownStatus"
|
||||
data-toggle="dropdown"
|
||||
>
|
||||
<i class="fas fa-filter"></i> Status
|
||||
</button>
|
||||
<div class="dropdown-menu" aria-labelledby="dropdownStatus">
|
||||
<label class="dropdown-item mb-0">
|
||||
<input type="checkbox" ng-model="filters.status.ready" /> Ready
|
||||
</label>
|
||||
<label class="dropdown-item mb-0">
|
||||
<input type="checkbox" ng-model="filters.status.expired" /> Expired
|
||||
</label>
|
||||
<label class="dropdown-item mb-0">
|
||||
<input type="checkbox" ng-model="filters.status.removed" /> Removed
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Repository List -->
|
||||
<div
|
||||
class="admin-list-item"
|
||||
ng-repeat="repo in repositories | filter:repoFiler | orderBy:orderBy as filteredRepositories"
|
||||
>
|
||||
<div class="item-main">
|
||||
<div class="item-title">
|
||||
<a ng-href="/r/{{repo.repoId}}" ng-bind="repo.repoId"></a>
|
||||
<span
|
||||
class="status-badge"
|
||||
ng-class="'status-' + repo.status"
|
||||
>{{repo.status | title}}</span>
|
||||
</div>
|
||||
<div class="item-meta">
|
||||
<span>
|
||||
<i class="fab fa-github"></i>
|
||||
<a
|
||||
href="https://github.com/{{repo.source.fullName}}/"
|
||||
ng-bind="repo.source.fullName"
|
||||
></a>
|
||||
</span>
|
||||
<span ng-if="repo.options.update">
|
||||
<i class="fas fa-code-branch"></i>
|
||||
<a
|
||||
href="https://github.com/{{repo.source.fullName}}/tree/{{repo.source.branch}}"
|
||||
ng-bind="repo.source.branch"
|
||||
></a>
|
||||
</span>
|
||||
<span ng-if="!repo.options.update">
|
||||
@<a
|
||||
href="https://github.com/{{repo.source.fullName}}/tree/{{repo.source.commit}}"
|
||||
ng-bind="repo.source.commit.substring(0, 8)"
|
||||
></a>
|
||||
</span>
|
||||
<span>anonymized {{repo.anonymizeDate | humanTime}}</span>
|
||||
</div>
|
||||
<div class="item-meta" style="margin-top: 4px">
|
||||
<span title="Terms: {{::repo.options.terms.join(', ')}}">
|
||||
<i class="fas fa-shield-alt"></i> {{::repo.options.terms.length | number}}
|
||||
</span>
|
||||
<span title="Size">
|
||||
<i class="fas fa-database"></i> {{::repo.size.storage | humanFileSize}}
|
||||
</span>
|
||||
<span>
|
||||
<i class="far fa-eye"></i> {{::repo.pageView | number}}
|
||||
</span>
|
||||
<span>
|
||||
<i class="far fa-calendar-alt"></i> Last: {{::repo.lastView | humanTime}}
|
||||
</span>
|
||||
<span ng-if="repo.options.expirationMode != 'never' && repo.status == 'ready'">
|
||||
<i class="far fa-clock"></i> Expire: {{repo.options.expirationDate | humanTime}}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="item-actions">
|
||||
<div class="d-flex flex-wrap" style="gap: 8px;">
|
||||
<div class="dropdown">
|
||||
<button
|
||||
class="btn dropdown-toggle btn-sm"
|
||||
type="button"
|
||||
data-toggle="dropdown"
|
||||
>
|
||||
Actions
|
||||
</button>
|
||||
<div class="dropdown-menu dropdown-menu-right">
|
||||
<a class="dropdown-item" href="/anonymize/{{repo.repoId}}">
|
||||
<i class="far fa-edit"></i> Edit
|
||||
<button class="btn dropdown-toggle" type="button" id="dropdownSort" data-toggle="dropdown">Sort</button>
|
||||
<div class="dropdown-menu" aria-labelledby="dropdownSort">
|
||||
<h6 class="dropdown-header">Sort by</h6>
|
||||
<a class="dropdown-item" href="#" ng-click="orderBy = '-anonymizeDate'">
|
||||
<i class="fas fa-check" ng-show="orderBy == '-anonymizeDate'"></i> Anonymize date
|
||||
</a>
|
||||
<a class="dropdown-item" href="/r/{{repo.repoId}}/">
|
||||
<i class="fa fa-eye"></i> View Repo
|
||||
<a class="dropdown-item" href="#" ng-click="orderBy = 'repoId'">
|
||||
<i class="fas fa-check" ng-show="orderBy == 'repoId'"></i> ID
|
||||
</a>
|
||||
<a
|
||||
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
|
||||
</a>
|
||||
<div class="dropdown-divider"></div>
|
||||
<a
|
||||
class="dropdown-item"
|
||||
href="#"
|
||||
ng-show="repo.status == 'ready' || repo.status == 'error'"
|
||||
ng-click="updateRepository(repo)"
|
||||
>
|
||||
<i class="fas fa-sync"></i> Force Update
|
||||
</a>
|
||||
<a
|
||||
class="dropdown-item"
|
||||
href="#"
|
||||
ng-show="repo.status == 'removed'"
|
||||
ng-click="updateRepository(repo)"
|
||||
>
|
||||
<i class="fas fa-check-circle"></i> Enable
|
||||
</a>
|
||||
<div class="dropdown-divider"></div>
|
||||
<a class="dropdown-item" href="#" ng-click="removeCache(repo)">
|
||||
<i class="fas fa-broom"></i> Remove Cache
|
||||
</a>
|
||||
<a
|
||||
class="dropdown-item text-danger"
|
||||
href="#"
|
||||
ng-show="repo.status == 'ready'"
|
||||
ng-click="removeRepository(repo)"
|
||||
>
|
||||
<i class="fas fa-trash-alt"></i> Remove
|
||||
<a class="dropdown-item" href="#" ng-click="orderBy = '-status'">
|
||||
<i class="fas fa-check" ng-show="orderBy == '-status'"></i> Status
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="dropdown">
|
||||
<button class="btn dropdown-toggle" type="button" id="dropdownStatus" data-toggle="dropdown">Status</button>
|
||||
<div class="dropdown-menu" aria-labelledby="dropdownStatus">
|
||||
<h6 class="dropdown-header">Filter by status</h6>
|
||||
<div class="form-check dropdown-item">
|
||||
<input class="form-check-input" type="checkbox" id="adminUserStatusReady" ng-model="filters.status.ready" />
|
||||
<label class="form-check-label" for="adminUserStatusReady">Ready</label>
|
||||
</div>
|
||||
<div class="form-check dropdown-item">
|
||||
<input class="form-check-input" type="checkbox" id="adminUserStatusExpired" ng-model="filters.status.expired" />
|
||||
<label class="form-check-label" for="adminUserStatusExpired">Expired</label>
|
||||
</div>
|
||||
<div class="form-check dropdown-item">
|
||||
<input class="form-check-input" type="checkbox" id="adminUserStatusRemoved" ng-model="filters.status.removed" />
|
||||
<label class="form-check-label" for="adminUserStatusRemoved">Removed</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div class="paper-table paper-table-repos w-100" role="table" aria-label="Repositories">
|
||||
<div class="paper-table-head" role="row">
|
||||
<div role="columnheader">Repository</div>
|
||||
<div role="columnheader">Status</div>
|
||||
<div role="columnheader" class="num">Views</div>
|
||||
<div role="columnheader">Anonymized</div>
|
||||
<div role="columnheader" aria-label="Actions"></div>
|
||||
</div>
|
||||
<div
|
||||
class="paper-table-row"
|
||||
role="row"
|
||||
ng-class="{'repo-inactive': repo.status == 'expired' || repo.status == 'removed'}"
|
||||
ng-repeat="repo in repositories | filter:repoFiler | orderBy:orderBy as filteredRepositories"
|
||||
>
|
||||
<div class="cell-anon" role="cell">
|
||||
<span class="type-badge type-repo">Repo</span>
|
||||
<div class="anon-text">
|
||||
<a class="repo-name" ng-href="/r/{{repo.repoId}}" ng-bind="repo.repoId"></a>
|
||||
<div class="anon-sub">
|
||||
<a href="https://github.com/{{repo.source.fullName}}/" ng-bind="repo.source.fullName"></a><span ng-if="repo.options.update"> · <a href="https://github.com/{{repo.source.fullName}}/tree/{{repo.source.branch}}" ng-bind="repo.source.branch"></a></span><span ng-if="!repo.options.update"> · @<a href="https://github.com/{{repo.source.fullName}}/tree/{{repo.source.commit}}" ng-bind="repo.source.commit.substring(0, 8)"></a></span><span> · {{::repo.size.storage | humanFileSize}}</span><span> · {{::repo.options.terms.length | number}} terms</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="cell-status" role="cell">
|
||||
<span class="status-dot" ng-class="{'status-removed': repo.status == 'removed' || repo.status == 'expired', 'status-ready': repo.status == 'ready', 'status-error': repo.status == 'error'}"></span>
|
||||
<span ng-bind="repo.status | title"></span>
|
||||
</div>
|
||||
<div class="cell-views num" role="cell" ng-bind="::repo.pageView | number"></div>
|
||||
<div class="cell-expires" role="cell" ng-bind="repo.anonymizeDate | humanTime"></div>
|
||||
<div class="cell-actions" role="cell">
|
||||
<div class="dropdown">
|
||||
<button class="btn btn-icon-dots" type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" aria-label="Actions">
|
||||
<i class="fas fa-ellipsis-h" aria-hidden="true"></i>
|
||||
</button>
|
||||
<div class="dropdown-menu dropdown-menu-right">
|
||||
<a class="dropdown-item" href="/anonymize/{{repo.repoId}}"><i class="far fa-edit"></i> Edit</a>
|
||||
<a class="dropdown-item" href="/r/{{repo.repoId}}/"><i class="fa fa-eye"></i> View repo</a>
|
||||
<a 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</a>
|
||||
<div class="dropdown-divider"></div>
|
||||
<a class="dropdown-item" href="#" ng-show="repo.status == 'ready' || repo.status == 'error'" ng-click="updateRepository(repo)"><i class="fas fa-sync"></i> Force update</a>
|
||||
<a class="dropdown-item" href="#" ng-show="repo.status == 'removed'" ng-click="updateRepository(repo)"><i class="fas fa-check-circle"></i> Enable</a>
|
||||
<div class="dropdown-divider"></div>
|
||||
<a class="dropdown-item" href="#" ng-click="removeCache(repo)"><i class="fas fa-broom"></i> Remove cache</a>
|
||||
<a class="dropdown-item text-danger" href="#" ng-show="repo.status == 'ready'" ng-click="removeRepository(repo)"><i class="fas fa-trash-alt"></i> Remove</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="paper-table-empty" ng-if="filteredRepositories.length == 0">
|
||||
<i class="fas fa-inbox"></i>
|
||||
<span>No repositories to display.</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="empty-state" ng-if="filteredRepositories.length == 0">
|
||||
<i class="fas fa-code-branch"></i>
|
||||
No repositories to display.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
+85
-135
@@ -1,160 +1,110 @@
|
||||
<div class="container page">
|
||||
<!-- Admin Navigation -->
|
||||
<div class="container paper-page">
|
||||
<div class="paper-crumbs">Admin / <span class="here">Users</span></div>
|
||||
<h1 class="paper-page-title">Registered <em>users</em></h1>
|
||||
<p class="paper-page-lede">Browse, search, and manage every account.</p>
|
||||
|
||||
<nav class="admin-nav">
|
||||
<a href="/admin/">
|
||||
<i class="fas fa-code-branch"></i> Repositories
|
||||
</a>
|
||||
<a href="/admin/users" class="active">
|
||||
<i class="fas fa-users"></i> Users
|
||||
</a>
|
||||
<a href="/admin/conferences">
|
||||
<i class="fas fa-chalkboard-teacher"></i> Conferences
|
||||
</a>
|
||||
<a href="/admin/queues">
|
||||
<i class="fas fa-tasks"></i> Queues
|
||||
</a>
|
||||
<a href="/admin/"><i class="fas fa-code-branch"></i> Repositories</a>
|
||||
<a href="/admin/users" class="active"><i class="fas fa-users"></i> Users</a>
|
||||
<a href="/admin/conferences"><i class="fas fa-chalkboard-teacher"></i> Conferences</a>
|
||||
<a href="/admin/queues"><i class="fas fa-tasks"></i> Queues</a>
|
||||
</nav>
|
||||
|
||||
<!-- Summary Stats -->
|
||||
<div class="admin-stats">
|
||||
<div class="admin-stat-card">
|
||||
<div class="stat-value" ng-bind="total >= 0 ? (total | number) : '...'"></div>
|
||||
<div class="stat-label">Total Users</div>
|
||||
<div class="stat-label">Total users</div>
|
||||
</div>
|
||||
<div class="admin-stat-card">
|
||||
<div class="stat-value">{{query.page}}/{{totalPage || '...'}}</div>
|
||||
<div class="stat-label">Current Page</div>
|
||||
<div class="stat-label">Current page</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Toolbar -->
|
||||
<div class="admin-toolbar">
|
||||
<input
|
||||
type="search"
|
||||
class="form-control"
|
||||
aria-label="Search users..."
|
||||
placeholder="Search users..."
|
||||
autocomplete="off"
|
||||
ng-model="query.search"
|
||||
/>
|
||||
|
||||
<div class="pagination-compact">
|
||||
<button class="btn btn-sm" ng-click="query.page = Math.max(1, query.page - 1)" ng-disabled="query.page <= 1">
|
||||
<i class="fas fa-chevron-left"></i>
|
||||
</button>
|
||||
<form class="w-100 dashboard-filter-row" aria-label="Users" accept-charset="UTF-8">
|
||||
<div class="search-wrap">
|
||||
<input
|
||||
type="number"
|
||||
class="form-control form-control-sm"
|
||||
ng-model="query.page"
|
||||
min="1"
|
||||
max="{{totalPage}}"
|
||||
type="search"
|
||||
class="form-control"
|
||||
aria-label="Search users"
|
||||
placeholder="Search users…"
|
||||
autocomplete="off"
|
||||
ng-model="query.search"
|
||||
/>
|
||||
<span>/{{totalPage}}</span>
|
||||
<button class="btn btn-sm" ng-click="query.page = Math.min(totalPage, query.page + 1)" ng-disabled="query.page >= totalPage">
|
||||
<i class="fas fa-chevron-right"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="dropdown">
|
||||
<button
|
||||
class="btn btn-sm dropdown-toggle"
|
||||
type="button"
|
||||
id="dropdownSort"
|
||||
data-toggle="dropdown"
|
||||
aria-haspopup="true"
|
||||
aria-expanded="false"
|
||||
>
|
||||
<i class="fas fa-sort"></i> Sort
|
||||
</button>
|
||||
<div class="dropdown-menu" aria-labelledby="dropdownSort">
|
||||
<h6 class="dropdown-header">Sort by</h6>
|
||||
<a class="dropdown-item" href="#" ng-click="query.sort = 'username'">
|
||||
<i class="fas fa-check" ng-show="query.sort == 'username'"></i> Username
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- User List -->
|
||||
<div
|
||||
class="admin-list-item"
|
||||
ng-repeat="u in users | filter:userFiler | orderBy:orderBy as filteredUsers"
|
||||
>
|
||||
<div class="item-main">
|
||||
<div class="item-title">
|
||||
<a ng-href="/admin/users/{{u.username}}">
|
||||
<img
|
||||
ng-src="{{u.photo}}"
|
||||
ng-if="u.photo"
|
||||
width="20"
|
||||
height="20"
|
||||
class="rounded-circle mr-1"
|
||||
style="vertical-align: text-bottom"
|
||||
/>
|
||||
{{u.username}}
|
||||
</a>
|
||||
<span
|
||||
class="status-badge"
|
||||
ng-class="'status-' + u.status"
|
||||
>{{u.status | title}}</span>
|
||||
<span
|
||||
class="status-badge status-active"
|
||||
ng-if="u.isAdmin"
|
||||
>Admin</span>
|
||||
</div>
|
||||
<div class="item-meta">
|
||||
<span ng-if="u.emails[0].email">
|
||||
<i class="fas fa-envelope"></i> {{u.emails[0].email}}
|
||||
</span>
|
||||
<span>
|
||||
<i class="fab fa-github"></i>
|
||||
<a ng-href="https://github.com/{{u.username}}" target="_blank">{{u.username}}</a>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="item-actions">
|
||||
<div class="dropdown">
|
||||
<button
|
||||
class="btn dropdown-toggle btn-sm"
|
||||
type="button"
|
||||
data-toggle="dropdown"
|
||||
aria-haspopup="true"
|
||||
aria-expanded="false"
|
||||
>
|
||||
Actions
|
||||
<div class="d-flex flex-wrap" style="gap: 8px; align-items: center;">
|
||||
<div class="pagination-compact">
|
||||
<button class="btn btn-sm" type="button" ng-click="query.page = Math.max(1, query.page - 1)" ng-disabled="query.page <= 1">
|
||||
<i class="fas fa-chevron-left"></i>
|
||||
</button>
|
||||
<div class="dropdown-menu dropdown-menu-right">
|
||||
<a class="dropdown-item" href="/admin/users/{{u.username}}">
|
||||
<i class="far fa-eye"></i> View Details
|
||||
</a>
|
||||
<div class="dropdown-divider"></div>
|
||||
<a
|
||||
class="dropdown-item"
|
||||
href="#"
|
||||
ng-show="u.status == 'active'"
|
||||
ng-click="banUser(u)"
|
||||
>
|
||||
<i class="fas fa-ban"></i> Ban
|
||||
</a>
|
||||
<a
|
||||
class="dropdown-item"
|
||||
href="#"
|
||||
ng-show="u.status == 'removed' || u.status == 'banned'"
|
||||
ng-click="activateUser(u)"
|
||||
>
|
||||
<i class="fas fa-check-circle"></i> Activate
|
||||
<input type="number" class="form-control form-control-sm" ng-model="query.page" min="1" max="{{totalPage}}" />
|
||||
<span>/{{totalPage}}</span>
|
||||
<button class="btn btn-sm" type="button" ng-click="query.page = Math.min(totalPage, query.page + 1)" ng-disabled="query.page >= totalPage">
|
||||
<i class="fas fa-chevron-right"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="dropdown">
|
||||
<button class="btn 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">Sort by</h6>
|
||||
<a class="dropdown-item" href="#" ng-click="query.sort = 'username'">
|
||||
<i class="fas fa-check" ng-show="query.sort == 'username'"></i> Username
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div class="paper-table w-100" role="table" aria-label="Users" style="--cols: minmax(280px, 2.4fr) 140px 140px 52px;">
|
||||
<div class="paper-table-head admin-users-row" role="row">
|
||||
<div role="columnheader">User</div>
|
||||
<div role="columnheader">Status</div>
|
||||
<div role="columnheader">Role</div>
|
||||
<div role="columnheader" aria-label="Actions"></div>
|
||||
</div>
|
||||
<div
|
||||
class="paper-table-row admin-users-row"
|
||||
role="row"
|
||||
ng-repeat="u in users | filter:userFiler | orderBy:orderBy as filteredUsers"
|
||||
>
|
||||
<div class="cell-anon" role="cell">
|
||||
<img ng-src="{{u.photo}}" ng-if="u.photo" width="28" height="28" class="rounded-circle" style="flex-shrink:0;" />
|
||||
<div class="anon-text">
|
||||
<a class="repo-name" ng-href="/admin/users/{{u.username}}" ng-bind="u.username"></a>
|
||||
<div class="anon-sub">
|
||||
<span ng-if="u.emails[0].email">{{u.emails[0].email}}</span><span ng-if="u.emails[0].email"> · </span><a href="https://github.com/{{u.username}}" target="_blank"><i class="fab fa-github"></i> {{u.username}}</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="cell-status" role="cell">
|
||||
<span class="status-dot" ng-class="{'status-ready': u.status == 'active', 'status-removed': u.status == 'removed' || u.status == 'banned'}"></span>
|
||||
<span ng-bind="u.status | title"></span>
|
||||
</div>
|
||||
<div class="cell-status" role="cell">
|
||||
<span ng-if="u.isAdmin" class="type-badge type-repo">Admin</span>
|
||||
<span ng-if="!u.isAdmin" class="empty-dash">—</span>
|
||||
</div>
|
||||
<div class="cell-actions" role="cell">
|
||||
<div class="dropdown">
|
||||
<button class="btn btn-icon-dots" type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" aria-label="Actions">
|
||||
<i class="fas fa-ellipsis-h" aria-hidden="true"></i>
|
||||
</button>
|
||||
<div class="dropdown-menu dropdown-menu-right">
|
||||
<a class="dropdown-item" href="/admin/users/{{u.username}}"><i class="far fa-eye"></i> View details</a>
|
||||
<div class="dropdown-divider"></div>
|
||||
<a class="dropdown-item text-danger" href="#" ng-show="u.status == 'active'" ng-click="banUser(u)"><i class="fas fa-ban"></i> Ban</a>
|
||||
<a class="dropdown-item" href="#" ng-show="u.status == 'removed' || u.status == 'banned'" ng-click="activateUser(u)"><i class="fas fa-check-circle"></i> Activate</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="paper-table-empty" ng-if="filteredUsers.length == 0">
|
||||
<i class="fas fa-inbox"></i>
|
||||
<span>No users match the current filters.</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="empty-state" ng-if="filteredUsers.length == 0">
|
||||
<i class="fas fa-users"></i>
|
||||
No users match the current filters.
|
||||
</div>
|
||||
|
||||
<!-- Bottom pagination -->
|
||||
<div class="admin-toolbar" ng-if="totalPage > 1" style="justify-content: center; border-bottom: none;">
|
||||
<div class="pagination-compact">
|
||||
<button class="btn btn-sm" ng-click="query.page = Math.max(1, query.page - 1)" ng-disabled="query.page <= 1">
|
||||
|
||||
+217
-197
@@ -2,216 +2,170 @@
|
||||
<!-- ===== STATE 1: No URL — centered input ===== -->
|
||||
<div class="anonymize-landing" ng-hide="sourceUrl">
|
||||
<div class="anonymize-landing-inner">
|
||||
<h2 class="mb-3">Anonymize</h2>
|
||||
<div class="form-group mb-2">
|
||||
<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 or pull-request 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"
|
||||
ng-model="sourceUrl"
|
||||
placeholder="Paste a GitHub repo or pull request URL"
|
||||
placeholder="https://github.com/owner/repo or https://github.com/owner/repo/pull/42"
|
||||
ng-model-options="{ debounce: {default: 1000, blur: 0, click: 0}, updateOn: 'default blur click' }"
|
||||
ng-change="urlSelected()"
|
||||
/>
|
||||
</div>
|
||||
<small class="form-text text-muted">
|
||||
Paste a repository URL to anonymize a repo, or a pull request URL to anonymize a PR.
|
||||
<small class="form-text" style="color: var(--ink-muted);">
|
||||
Paste a repository URL to anonymize a repo, or a pull-request URL to anonymize a PR.
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ===== STATE 2: URL provided — preview (left) + form (right) ===== -->
|
||||
<div class="container-fluid h-100" ng-show="sourceUrl">
|
||||
<div class="row h-100 flex-column flex-md-row">
|
||||
|
||||
<!-- Preview column (left on desktop) -->
|
||||
<div
|
||||
class="col-md p-2 overflow-auto markdown-body body anonymize-preview-col"
|
||||
ng-bind-html="html_readme"
|
||||
ng-if="detectedType === 'repo' && html_readme"
|
||||
></div>
|
||||
|
||||
<div class="col-md 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>
|
||||
<!-- ===== STATE 2: URL provided — form (left) + preview (right) ===== -->
|
||||
<div class="anonymize-workspace" ng-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>
|
||||
<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 class="anonymize-topbar-head">
|
||||
<h1 class="paper-page-title anonymize-topbar-title">
|
||||
<span ng-if="!isUpdate">New <em>anonymization</em></span>
|
||||
<span ng-if="isUpdate">Edit <em>anonymization</em></span>
|
||||
</h1>
|
||||
<span class="type-badge" ng-show="detectedType" ng-class="{'type-repo': detectedType === 'repo', 'type-pr': detectedType === 'pr'}">
|
||||
{{detectedType === 'repo' ? 'Repo' : 'PR'}}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- Form column (right on desktop) -->
|
||||
<div class="anonymize-split">
|
||||
<!-- Form column (left) -->
|
||||
<div class="anonymize-form-col overflow-auto">
|
||||
<form class="form needs-validation p-3" name="anonymize" novalidate>
|
||||
<form class="form needs-validation paper-settings-main" name="anonymize" novalidate>
|
||||
|
||||
<!-- Source URL -->
|
||||
<div class="form-group mb-2">
|
||||
<input
|
||||
type="text"
|
||||
class="form-control"
|
||||
name="sourceUrl"
|
||||
id="sourceUrl"
|
||||
ng-class="{'is-invalid': anonymize.sourceUrl.$invalid}"
|
||||
ng-model="sourceUrl"
|
||||
placeholder="Paste a GitHub repo or pull request URL"
|
||||
ng-model-options="{ debounce: {default: 1000, blur: 0, click: 0}, updateOn: 'default blur click' }"
|
||||
ng-change="urlSelected()"
|
||||
/>
|
||||
<div class="invalid-feedback" ng-show="anonymize.sourceUrl.$error.github">
|
||||
Please provide a valid GitHub URL.
|
||||
</div>
|
||||
<div class="invalid-feedback" ng-show="anonymize.sourceUrl.$error.access">
|
||||
Not accessible. The organization may restrict access.
|
||||
</div>
|
||||
<div class="invalid-feedback" ng-show="anonymize.sourceUrl.$error.missing">
|
||||
Does not exist or is not accessible.
|
||||
</div>
|
||||
<div class="invalid-feedback" ng-show="anonymize.sourceUrl.$error.used">
|
||||
Already anonymized.
|
||||
</div>
|
||||
</div>
|
||||
<section class="paper-settings-section">
|
||||
<div class="paper-section-eyebrow">Source</div>
|
||||
|
||||
<div class="mb-2" ng-show="detectedType">
|
||||
<span class="type-badge" ng-class="{'type-repo': detectedType === 'repo', 'type-pr': detectedType === 'pr'}">
|
||||
<i ng-class="{'fas fa-code-branch': detectedType === 'repo', 'fas fa-code-merge': detectedType === 'pr'}"></i>
|
||||
{{detectedType === 'repo' ? 'Repository' : 'Pull Request'}}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<!-- ── Source (repo only) ── -->
|
||||
<h6 class="anonymize-section-title" ng-show="detectedType === 'repo'">
|
||||
<i class="fas fa-code-branch"></i> Source
|
||||
</h6>
|
||||
|
||||
<div ng-show="detectedType === 'repo'">
|
||||
<div class="form-group">
|
||||
<label for="branch">Branch</label>
|
||||
<div class="input-group mb-1">
|
||||
<select class="form-control" id="branch" name="branch" ng-model="source.branch">
|
||||
<option ng-repeat="b in branches" ng-bind="b.name" value="{{b.name}}"></option>
|
||||
</select>
|
||||
<div class="input-group-append">
|
||||
<button class="btn btn-outline-secondary" ng-click="getBranches(true)" title="Refresh" data-toggle="tooltip" data-placement="bottom">
|
||||
<i class="fa fa-undo"></i>
|
||||
</button>
|
||||
<label class="paper-field-label" for="sourceUrl">GitHub URL</label>
|
||||
<input
|
||||
type="text"
|
||||
class="form-control"
|
||||
name="sourceUrl"
|
||||
id="sourceUrl"
|
||||
ng-class="{'is-invalid': anonymize.sourceUrl.$invalid}"
|
||||
ng-model="sourceUrl"
|
||||
placeholder="Paste a GitHub repo or pull request URL"
|
||||
ng-model-options="{ debounce: {default: 1000, blur: 0, click: 0}, updateOn: 'default blur click' }"
|
||||
ng-change="urlSelected()"
|
||||
/>
|
||||
<div class="invalid-feedback" ng-show="anonymize.sourceUrl.$error.github">
|
||||
Please provide a valid GitHub URL.
|
||||
</div>
|
||||
<div class="invalid-feedback" ng-show="anonymize.sourceUrl.$error.access">
|
||||
Not accessible. The organization may restrict access.
|
||||
</div>
|
||||
<div class="invalid-feedback" ng-show="anonymize.sourceUrl.$error.missing">
|
||||
Does not exist or is not accessible.
|
||||
</div>
|
||||
<div class="invalid-feedback" ng-show="anonymize.sourceUrl.$error.used">
|
||||
Already anonymized.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ng-show="detectedType === 'repo'" class="form-grid-2">
|
||||
<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" ng-model="source.branch">
|
||||
<option ng-repeat="b in branches" ng-bind="b.name" value="{{b.name}}"></option>
|
||||
</select>
|
||||
<div class="input-group-append">
|
||||
<button class="btn" type="button" ng-click="getBranches(true)" title="Refresh" data-toggle="tooltip" data-placement="bottom">
|
||||
<i class="fa fa-undo"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="paper-field-label" for="commit">Commit</label>
|
||||
<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}" />
|
||||
<div class="invalid-feedback" ng-show="anonymize.commit.$error.pattern || anonymize.commit.$error.required">
|
||||
The commit SHA is not valid.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="commit">Commit</label>
|
||||
<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}" />
|
||||
<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.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group" ng-show="detectedType">
|
||||
<div class="form-check">
|
||||
<div class="form-check" ng-show="detectedType">
|
||||
<input class="form-check-input" type="checkbox" id="update" name="update" ng-model="options.update" />
|
||||
<label class="form-check-label" for="update">Auto update</label>
|
||||
<small class="form-text text-muted">Automatically update with the latest changes.</small>
|
||||
<small class="form-text text-muted">Automatically update with the latest changes (hourly max).</small>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ── Identity ── -->
|
||||
<h6 class="anonymize-section-title" ng-show="detectedType">
|
||||
<i class="fas fa-fingerprint"></i> Identity
|
||||
</h6>
|
||||
<section class="paper-settings-section" ng-show="detectedType">
|
||||
<div class="paper-section-eyebrow">Identity</div>
|
||||
|
||||
<div class="form-group" ng-show="detectedType === 'repo'">
|
||||
<label for="repoId">Anonymized repository ID</label>
|
||||
<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' }" />
|
||||
<small class="form-text text-muted">https://anonymous.4open.science/r/{{repoId}}</small>
|
||||
<div class="invalid-feedback" ng-show="anonymize.repoId.$error.format">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 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">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>
|
||||
|
||||
<div class="form-group" ng-show="detectedType">
|
||||
<label for="conference">Conference <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">
|
||||
<a ng-href="{{conference_data.url}}" target="_blank">{{conference_data.name}}</a> expires {{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">Links to a conference for automatic settings.</small>
|
||||
</div>
|
||||
|
||||
<!-- ── Anonymization ── -->
|
||||
<h6 class="anonymize-section-title" ng-show="detectedType">
|
||||
<i class="fas fa-shield-alt"></i> Anonymization
|
||||
</h6>
|
||||
|
||||
<div class="form-group" ng-show="detectedType">
|
||||
<label for="terms">Terms to anonymize</label>
|
||||
<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>
|
||||
<small class="form-text text-muted">One term per line (regex). Replaced by {{site_options.ANONYMIZATION_MASK}}-[N].</small>
|
||||
<div class="warning-feedback" ng-show="anonymize.terms.$error.regex">Regex characters detected. Escape them if unintentional.</div>
|
||||
<div class="invalid-feedback" ng-show="anonymize.terms.$error.format">Terms are in an invalid format.</div>
|
||||
</div>
|
||||
|
||||
<!-- ── Display ── -->
|
||||
<h6 class="anonymize-section-title" ng-show="detectedType">
|
||||
<i class="fas fa-eye"></i> Display
|
||||
</h6>
|
||||
|
||||
<div ng-show="detectedType">
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" id="link" name="link" ng-model="options.link" />
|
||||
<label class="form-check-label" for="link">Keep links</label>
|
||||
<div class="form-group" ng-show="detectedType === 'repo'">
|
||||
<label class="paper-field-label" for="repoId">Anonymized repository ID</label>
|
||||
<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' }" />
|
||||
<small class="form-text text-muted">Your share link will be <code>anonymous.4open.science/r/{{repoId}}</code>.</small>
|
||||
<div class="invalid-feedback" ng-show="anonymize.repoId.$error.format">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 class="form-check">
|
||||
<input class="form-check-input" type="checkbox" id="image" name="image" ng-model="options.image" />
|
||||
<label class="form-check-label" for="image">Display images</label>
|
||||
|
||||
<div class="form-group" ng-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" 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">Your share link will be <code>anonymous.4open.science/pr/{{pullRequestId}}</code>.</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>
|
||||
|
||||
<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" 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="_blank">{{conference_data.name}}</a> expires {{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">Link to a conference to apply its shared defaults.</small>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="paper-settings-section" ng-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" ng-model="terms" ng-model-options="{ debounce: 250 }" ng-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>.</small>
|
||||
<div class="warning-feedback" ng-show="anonymize.terms.$error.regex">Regex characters detected. Escape them if unintentional.</div>
|
||||
<div class="invalid-feedback" ng-show="anonymize.terms.$error.format">Terms are in an invalid format.</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="paper-settings-section" ng-show="detectedType">
|
||||
<div class="paper-section-eyebrow">Display</div>
|
||||
|
||||
<div ng-show="detectedType === 'repo'">
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" id="link" name="link" ng-model="options.link" />
|
||||
<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" ng-model="options.image" />
|
||||
<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" ng-model="options.pdf" />
|
||||
<label class="form-check-label" for="pdf">Display PDFs</label>
|
||||
@@ -256,45 +210,111 @@
|
||||
<label class="form-check-label" for="origin">Project name</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ── Expiration ── -->
|
||||
<h6 class="anonymize-section-title" ng-show="detectedType">
|
||||
<i class="far fa-clock"></i> Expiration
|
||||
</h6>
|
||||
<section class="paper-settings-section" ng-show="detectedType">
|
||||
<div class="paper-section-eyebrow">Expiration</div>
|
||||
|
||||
<div class="form-group" ng-show="detectedType">
|
||||
<select class="form-control" id="expiration" name="expiration" ng-model="options.expirationMode">
|
||||
<option value="never" selected>Never expire</option>
|
||||
<option value="redirect">Redirect to GitHub when expired</option>
|
||||
<option value="remove">Remove when expired</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group" ng-show="detectedType && options.expirationMode!='never'">
|
||||
<label for="expirationDate">Expiration date</label>
|
||||
<input class="form-control" type="date" name="expirationDate" id="expirationDate" ng-model="options.expirationDate" />
|
||||
<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" ng-model="options.expirationMode">
|
||||
<option value="never" selected>Never expire</option>
|
||||
<option value="redirect">Redirect to GitHub when expired</option>
|
||||
<option value="remove">Remove when expired</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group" ng-show="options.expirationMode!='never'">
|
||||
<label class="paper-field-label" for="expirationDate">Expiration date</label>
|
||||
<input class="form-control" type="date" name="expirationDate" id="expirationDate" ng-model="options.expirationDate" />
|
||||
</div>
|
||||
</div>
|
||||
<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>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div class="alert alert-danger" role="alert" ng-if="error" ng-bind="error"></div>
|
||||
|
||||
<div class="anonymize-submit-bar" ng-show="detectedType">
|
||||
<button type="submit" class="btn btn-primary btn-block" ng-click="anonymizeRepo($event)" ng-if="detectedType === 'repo' && !isUpdate">
|
||||
<button type="submit" class="btn btn-ink" ng-click="anonymizeRepo($event)" ng-if="detectedType === 'repo' && !isUpdate">
|
||||
<i class="fas fa-user-secret mr-1"></i> Anonymize Repository
|
||||
</button>
|
||||
<button type="submit" class="btn btn-primary btn-block" ng-click="anonymizeRepo($event)" ng-if="detectedType === 'repo' && isUpdate">
|
||||
<button type="submit" class="btn btn-ink" ng-click="anonymizeRepo($event)" ng-if="detectedType === 'repo' && isUpdate">
|
||||
<i class="fas fa-save mr-1"></i> Update Repository
|
||||
</button>
|
||||
<button type="submit" class="btn btn-primary btn-block" ng-click="anonymizePullRequest($event)" ng-if="detectedType === 'pr' && !isUpdate">
|
||||
<button type="submit" class="btn btn-ink" ng-click="anonymizePullRequest($event)" ng-if="detectedType === 'pr' && !isUpdate">
|
||||
<i class="fas fa-user-secret mr-1"></i> Anonymize Pull Request
|
||||
</button>
|
||||
<button type="submit" class="btn btn-primary btn-block" ng-click="anonymizePullRequest($event)" ng-if="detectedType === 'pr' && isUpdate">
|
||||
<button type="submit" class="btn btn-ink" ng-click="anonymizePullRequest($event)" ng-if="detectedType === 'pr' && isUpdate">
|
||||
<i class="fas fa-save mr-1"></i> Update Pull Request
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<!-- Preview column (right) -->
|
||||
<div
|
||||
class="anonymize-preview-col"
|
||||
ng-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" ng-bind-html="html_readme"></div>
|
||||
</div>
|
||||
|
||||
<div class="anonymize-preview-col" ng-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 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(--paper-bg-alt)" 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(--paper-bg-alt)">
|
||||
<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>
|
||||
|
||||
@@ -13,14 +13,15 @@
|
||||
name="anonymizeForm"
|
||||
novalidate
|
||||
>
|
||||
<h5 class="card-title mb-2">Anonymize a pull request</h5>
|
||||
<p class="text-muted mb-3" style="font-size: 0.9rem">
|
||||
Fill the information to anonymize! It will only take 5min.
|
||||
<div class="paper-crumbs">Anonymize / <span class="here">Pull request</span></div>
|
||||
<h1 class="paper-page-title">Anonymize a <em>pull request</em></h1>
|
||||
<p class="paper-page-lede">
|
||||
Fill in the details — it only takes a minute.
|
||||
</p>
|
||||
|
||||
<h5 class="anonymize-section-title">
|
||||
<div class="paper-section-eyebrow anonymize-section-title">
|
||||
<i class="fab fa-github"></i> Source
|
||||
</h5>
|
||||
</div>
|
||||
<!-- pullRequestUrl -->
|
||||
<div class="form-group">
|
||||
<label for="pullRequestUrl"
|
||||
@@ -76,9 +77,9 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h5 class="anonymize-section-title">
|
||||
<div class="paper-section-eyebrow anonymize-section-title">
|
||||
<i class="fas fa-chalkboard-teacher"></i> Conference ID
|
||||
</h5>
|
||||
</div>
|
||||
<!-- Conference -->
|
||||
<div class="form-group">
|
||||
<label for="conference"
|
||||
@@ -111,9 +112,9 @@
|
||||
</small>
|
||||
</div>
|
||||
|
||||
<h5 class="anonymize-section-title">
|
||||
<div class="paper-section-eyebrow anonymize-section-title">
|
||||
<i class="fas fa-shield-alt"></i> Anonymization Options
|
||||
</h5>
|
||||
</div>
|
||||
<!-- Pull Request ID -->
|
||||
<div class="form-group">
|
||||
<label for="pullRequestId">Anonymized pull request id</label>
|
||||
@@ -375,7 +376,7 @@
|
||||
<button
|
||||
id="submit"
|
||||
type="submit"
|
||||
class="btn btn-primary btn-block"
|
||||
class="btn btn-ink btn-block"
|
||||
ng-click="anonymizePullRequest($event)"
|
||||
ng-if="!isUpdate"
|
||||
>
|
||||
@@ -384,7 +385,7 @@
|
||||
<button
|
||||
id="submit"
|
||||
type="submit"
|
||||
class="btn btn-primary btn-block"
|
||||
class="btn btn-ink btn-block"
|
||||
ng-click="updatePullRequest($event)"
|
||||
ng-if="isUpdate"
|
||||
>
|
||||
|
||||
+48
-56
@@ -1,59 +1,51 @@
|
||||
<div class="container-fluid h-100">
|
||||
<div class="row h-100">
|
||||
<div class="col shadow overflow-auto h-100 d-flex ">
|
||||
<div class="card w-50 m-auto">
|
||||
<form
|
||||
class="form needs-validation card-body"
|
||||
name="claimForm"
|
||||
novalidate
|
||||
>
|
||||
<h5 class="card-title">Claim an anonymized repository</h5>
|
||||
<h6 class="card-subtitle mb-2 text-muted">
|
||||
Claim the ownership of an existing anonymized repository.
|
||||
</h6>
|
||||
<div class="form-row">
|
||||
<div class="col form-group">
|
||||
<label for="repoUrl">Type the url of your repository</label>
|
||||
<input
|
||||
type="text"
|
||||
class="form-control"
|
||||
name="repoUrl"
|
||||
id="repoUrl"
|
||||
required
|
||||
ng-class="{'is-invalid': claimForm.repoUrl.$invalid}"
|
||||
ng-model="repoUrl"
|
||||
/>
|
||||
<div class="invalid-feedback"
|
||||
ng-show="claimForm.repoUrl.$error.not_found">The repository is not found.</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="repoId">Anonymize repository id</label>
|
||||
<input
|
||||
type="text"
|
||||
class="form-control"
|
||||
name="repoId"
|
||||
id="repoId"
|
||||
required
|
||||
ng-model="repoId"
|
||||
ng-class="{'is-invalid': claimForm.repoId.$invalid}"
|
||||
/>
|
||||
<small id="idHelp" class="form-text text-muted"
|
||||
>The id is in the repository
|
||||
https://anonymous.4open.science/r/{id}</small
|
||||
>
|
||||
</div>
|
||||
|
||||
<button
|
||||
id="submit"
|
||||
type="submit"
|
||||
class="btn black_border"
|
||||
ng-click="claim()"
|
||||
>
|
||||
Claim
|
||||
</button>
|
||||
</form>
|
||||
<div class="container paper-page">
|
||||
<div class="paper-crumbs">Reference / <span class="here">Claim</span></div>
|
||||
<h1 class="paper-page-title">Claim an <em>anonymization</em></h1>
|
||||
<p class="paper-page-lede">
|
||||
Take ownership of an existing anonymized repository so it appears on your
|
||||
dashboard.
|
||||
</p>
|
||||
<div class="paper-meta-rule"></div>
|
||||
<form class="form needs-validation" name="claimForm" novalidate>
|
||||
<div class="form-group">
|
||||
<label class="paper-field-label" for="repoUrl">Repository URL</label>
|
||||
<input
|
||||
type="text"
|
||||
class="form-control"
|
||||
name="repoUrl"
|
||||
id="repoUrl"
|
||||
required
|
||||
ng-class="{'is-invalid': claimForm.repoUrl.$invalid}"
|
||||
ng-model="repoUrl"
|
||||
/>
|
||||
<div class="invalid-feedback" ng-show="claimForm.repoUrl.$error.not_found">
|
||||
The repository is not found.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="paper-field-label" for="repoId">Anonymized repository ID</label>
|
||||
<input
|
||||
type="text"
|
||||
class="form-control"
|
||||
name="repoId"
|
||||
id="repoId"
|
||||
required
|
||||
ng-model="repoId"
|
||||
ng-class="{'is-invalid': claimForm.repoId.$invalid}"
|
||||
/>
|
||||
<small id="idHelp" class="form-text text-muted"
|
||||
>The id is in the repository
|
||||
https://anonymous.4open.science/r/{id}</small
|
||||
>
|
||||
</div>
|
||||
|
||||
<button
|
||||
id="submit"
|
||||
type="submit"
|
||||
class="btn btn-ink"
|
||||
ng-click="claim()"
|
||||
>
|
||||
Claim
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
+151
-258
@@ -1,269 +1,162 @@
|
||||
<div class="container page">
|
||||
<div class="container page paper-page">
|
||||
<div>
|
||||
<h1>
|
||||
<a ng-href="{{conference.url}}">{{conference.name}}</a>
|
||||
<span
|
||||
class="badge"
|
||||
ng-class="{'badge-warning': conference.status == 'removed' || conference.status == 'expired', 'badge-success': conference.status == 'ready'}"
|
||||
ng-bind="conference.status | title"
|
||||
></span>
|
||||
</h1>
|
||||
<div class="row mb-3 m-0 py-2 border">
|
||||
<div class="col-2 font-weight-bold">ID</div>
|
||||
<div class="col-10">{{conference.conferenceID}}</div>
|
||||
|
||||
<div class="col-2 font-weight-bold">Name</div>
|
||||
<div class="col-10">{{conference.name}}</div>
|
||||
|
||||
<div class="col-2 font-weight-bold">URL</div>
|
||||
<div class="col-10">
|
||||
<a ng-href="{{conference.url}}" ng-bind="conference.url"></a>
|
||||
<div class="paper-crumbs">
|
||||
<a href="/conferences">Conferences</a> / <span class="here">{{conference.conferenceID}}</span>
|
||||
</div>
|
||||
<div class="d-flex align-items-end flex-wrap" style="gap: 12px; justify-content: space-between;">
|
||||
<div>
|
||||
<h1 class="paper-page-title">
|
||||
<a ng-href="{{conference.url}}" style="color: var(--color);">{{conference.name}}</a>
|
||||
</h1>
|
||||
<p class="paper-page-lede" ng-if="conference.url">
|
||||
<a ng-href="{{conference.url}}" ng-bind="conference.url"></a>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="col-2 font-weight-bold">From</div>
|
||||
<div class="col-10">{{conference.startDate | date}}</div>
|
||||
|
||||
<div class="col-2 font-weight-bold">End</div>
|
||||
<div class="col-10">{{conference.endDate | date}}</div>
|
||||
|
||||
<div class="col-2 font-weight-bold"># Repositories</div>
|
||||
<div class="col-10">{{::conference.repositories.length | number}}</div>
|
||||
|
||||
<div class="col-2 font-weight-bold">Price</div>
|
||||
<div class="col-10">{{conference.price || 0 | number}} €</div>
|
||||
<span class="status-dot-wrap">
|
||||
<span class="status-dot" ng-class="{'status-removed': conference.status == 'removed' || conference.status == 'expired', 'status-ready': conference.status == 'ready'}"></span>
|
||||
<span ng-bind="conference.status | title"></span>
|
||||
</span>
|
||||
</div>
|
||||
<div class="paper-meta-rule">
|
||||
<span>ID <b ng-bind="conference.conferenceID"></b></span>
|
||||
<span>From <b>{{conference.startDate | date}}</b></span>
|
||||
<span>Ends <b>{{conference.endDate | date}}</b></span>
|
||||
<span>Repos <b>{{::conference.repositories.length | number}}</b></span>
|
||||
<span>Price <b>{{conference.price || 0 | number}} €</b></span>
|
||||
</div>
|
||||
|
||||
<h3>Repositories</h3>
|
||||
<div class="border-bottom color-border-secondary py-3 w-100">
|
||||
<div class="d-flex flex-items-start w-100">
|
||||
<form class="w-100" aria-label="Repositories" accept-charset="UTF-8">
|
||||
<div class="d-flex flex-column flex-lg-row flex-auto">
|
||||
<div class="mb-1 mb-md-0 mr-md-3">
|
||||
<input
|
||||
type="search"
|
||||
id="search"
|
||||
class="form-control"
|
||||
aria-label="Find repositories"
|
||||
placeholder="Find repositories"
|
||||
autocomplete="off"
|
||||
ng-model="search"
|
||||
/>
|
||||
</div>
|
||||
<div class="paper-section-eyebrow">Repositories</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="anonymizeDate"
|
||||
value="-anonymizeDate"
|
||||
ng-model="orderBy"
|
||||
/>
|
||||
<label class="form-check-label" for="anonymizeDate">
|
||||
Anonymize Date
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-check dropdown-item">
|
||||
<input
|
||||
class="form-check-input"
|
||||
type="radio"
|
||||
name="sort"
|
||||
id="sortID"
|
||||
value="repoId"
|
||||
ng-model="orderBy"
|
||||
/>
|
||||
<label class="form-check-label" for="sortID"> ID </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>
|
||||
</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-none d-md-flex flex-md-items-center flex-md-justify-end"
|
||||
></div>
|
||||
<form class="w-100 dashboard-filter-row" aria-label="Repositories" accept-charset="UTF-8">
|
||||
<div class="search-wrap">
|
||||
<input
|
||||
type="search"
|
||||
id="search"
|
||||
class="form-control"
|
||||
aria-label="Find repositories"
|
||||
placeholder="Find repositories…"
|
||||
autocomplete="off"
|
||||
ng-model="search"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ul class="p-0 m-0 w-100">
|
||||
<li
|
||||
class="col-12 d-flex px-0 py-3 border-bottom color-border-secondary"
|
||||
ng-class="{'expired': repo.status == 'expired','removed': repo.status == 'removed' }"
|
||||
ng-repeat="repo in conference.repositories| filter:repoFiler| orderBy:orderBy as filteredRepositories"
|
||||
>
|
||||
<div class="w-100">
|
||||
<div class="">
|
||||
<h3>
|
||||
<a ng-href="/r/{{repo.repoId}}" ng-bind="repo.repoId"></a>
|
||||
<span
|
||||
class="badge"
|
||||
ng-class="{'badge-warning': repo.status == 'removed' || repo.status == 'expired', 'badge-success': repo.status == 'ready', 'badge-danger': ''}"
|
||||
ng-bind="repo.status | title"
|
||||
></span>
|
||||
</h3>
|
||||
<span class="color-text-secondary mb-1">
|
||||
<span class="repository">
|
||||
<i class="fab fa-github" aria-hidden="true"></i>
|
||||
<a
|
||||
href="https://github.com/{{repo.source.fullName}}/"
|
||||
class="fullName"
|
||||
ng-bind="repo.source.fullName"
|
||||
></a>
|
||||
</span>
|
||||
<span class="branch" ng-if="repo.options.update">
|
||||
<i class="fas fa-code-branch" aria-hidden="true"></i>
|
||||
<a
|
||||
href="https://github.com/{{repo.source.fullName}}/tree/{{repo.source.branch}}"
|
||||
class="branch"
|
||||
ng-bind="repo.source.branch"
|
||||
></a>
|
||||
</span>
|
||||
<span class="commit" ng-if="!repo.options.update">
|
||||
@<a
|
||||
href="https://github.com/{{repo.source.fullName}}/tree/{{repo.source.commit}}"
|
||||
class="commit"
|
||||
ng-bind="repo.source.commit.substring(0, 8)"
|
||||
></a>
|
||||
</span>
|
||||
anonymized {{repo.anonymizeDate | humanTime}}
|
||||
</span>
|
||||
</div>
|
||||
<div class="color-text-secondary mt-2">
|
||||
<span
|
||||
class="ml-0 mr-3"
|
||||
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
|
||||
class="ml-0 mr-3"
|
||||
title="Size: {{::repo.size | humanFileSize}}"
|
||||
data-toggle="tooltip"
|
||||
data-placement="bottom"
|
||||
>
|
||||
<i class="fas fa-database"></i> {{::repo.size.storage |
|
||||
humanFileSize}}</span
|
||||
>
|
||||
<span
|
||||
class="ml-0 mr-3"
|
||||
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
|
||||
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 class="d-flex flex-wrap" style="gap: 8px">
|
||||
<div class="dropdown">
|
||||
<button
|
||||
class="btn 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="anonymizeDate" value="-anonymizeDate" ng-model="orderBy" />
|
||||
<label class="form-check-label" for="anonymizeDate">Anonymize Date</label>
|
||||
</div>
|
||||
<div class="form-check dropdown-item">
|
||||
<input class="form-check-input" type="radio" name="sort" id="sortID" value="repoId" ng-model="orderBy" />
|
||||
<label class="form-check-label" for="sortID">ID</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>
|
||||
</div>
|
||||
</li>
|
||||
<li
|
||||
class="col-12 d-flex px-0 py-3 border-bottom color-border-secondary"
|
||||
ng-if="filteredRepositories.length == 0"
|
||||
|
||||
<div class="dropdown">
|
||||
<button
|
||||
class="btn 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>
|
||||
</form>
|
||||
|
||||
<div class="paper-table paper-table-repos w-100" role="table" aria-label="Repositories">
|
||||
<div class="paper-table-head" role="row">
|
||||
<div role="columnheader">Repository</div>
|
||||
<div role="columnheader">Status</div>
|
||||
<div role="columnheader" class="num">Views</div>
|
||||
<div role="columnheader">Expires</div>
|
||||
<div role="columnheader" aria-label="Actions"></div>
|
||||
</div>
|
||||
<div
|
||||
class="paper-table-row"
|
||||
role="row"
|
||||
ng-class="{'repo-inactive': repo.status == 'expired' || repo.status == 'removed'}"
|
||||
ng-repeat="repo in conference.repositories| filter:repoFiler| orderBy:orderBy as filteredRepositories"
|
||||
>
|
||||
There is no repository to display.
|
||||
</li>
|
||||
</ul>
|
||||
<div class="cell-anon" role="cell">
|
||||
<span class="type-badge type-repo">Repo</span>
|
||||
<div class="anon-text">
|
||||
<a class="repo-name" ng-href="/r/{{repo.repoId}}" ng-bind="repo.repoId"></a>
|
||||
<div class="anon-sub">
|
||||
<a href="https://github.com/{{repo.source.fullName}}/" ng-bind="repo.source.fullName"></a><span ng-if="repo.options.update"> · <a href="https://github.com/{{repo.source.fullName}}/tree/{{repo.source.branch}}" ng-bind="repo.source.branch"></a></span><span ng-if="!repo.options.update"> · @<a href="https://github.com/{{repo.source.fullName}}/tree/{{repo.source.commit}}" ng-bind="repo.source.commit.substring(0, 8)"></a></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="cell-status" role="cell">
|
||||
<span class="status-dot" ng-class="{'status-removed': repo.status == 'removed' || repo.status == 'expired', 'status-ready': repo.status == 'ready', 'status-error': repo.status == 'error'}"></span>
|
||||
<span ng-bind="repo.status | title"></span>
|
||||
</div>
|
||||
<div class="cell-views num" role="cell" ng-bind="::repo.pageView | number"></div>
|
||||
<div class="cell-expires" role="cell">
|
||||
<span ng-if="repo.options.expirationMode !== 'never' && repo.status == 'ready'" ng-bind="repo.options.expirationDate | humanTime"></span>
|
||||
<span class="empty-dash" ng-if="!(repo.options.expirationMode !== 'never' && repo.status == 'ready')">—</span>
|
||||
</div>
|
||||
<div class="cell-actions" role="cell">
|
||||
<div class="dropdown">
|
||||
<button
|
||||
class="btn btn-icon-dots"
|
||||
type="button"
|
||||
data-toggle="dropdown"
|
||||
aria-haspopup="true"
|
||||
aria-expanded="false"
|
||||
aria-label="Actions"
|
||||
>
|
||||
<i class="fas fa-ellipsis-h" aria-hidden="true"></i>
|
||||
</button>
|
||||
<div class="dropdown-menu dropdown-menu-right">
|
||||
<a class="dropdown-item" href="/anonymize/{{repo.repoId}}">
|
||||
<i class="far fa-edit" aria-hidden="true"></i> Edit
|
||||
</a>
|
||||
<a class="dropdown-item" href="/r/{{repo.repoId}}/">
|
||||
<i class="fa fa-eye" aria-hidden="true"></i> View
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="paper-table-empty" ng-if="filteredRepositories.length == 0">
|
||||
<i class="fas fa-inbox"></i>
|
||||
<span>There is no repository to display.</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
+128
-200
@@ -1,224 +1,152 @@
|
||||
<div class="container page">
|
||||
<div class="container page paper-page">
|
||||
<div class="row">
|
||||
<h1>Conferences</h1>
|
||||
<div class="border-bottom color-border-secondary py-3 w-100">
|
||||
<div class="d-flex flex-items-start w-100">
|
||||
<form class="w-100" aria-label="Repositories" accept-charset="UTF-8">
|
||||
<div class="d-flex flex-column flex-lg-row flex-auto">
|
||||
<div class="mb-1 mb-md-0 mr-md-3">
|
||||
<input
|
||||
type="search"
|
||||
id="search"
|
||||
class="form-control"
|
||||
aria-label="Find a conference…"
|
||||
placeholder="Find a conference…"
|
||||
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="sortName"
|
||||
value="name"
|
||||
ng-model="orderBy"
|
||||
/>
|
||||
<label class="form-check-label" for="sortName">
|
||||
Name
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-check dropdown-item">
|
||||
<input
|
||||
class="form-check-input"
|
||||
type="radio"
|
||||
name="sort"
|
||||
id="sortID"
|
||||
value="conferenceID"
|
||||
ng-model="orderBy"
|
||||
/>
|
||||
<label class="form-check-label" for="sortID"> ID </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>
|
||||
</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-none d-md-flex flex-md-items-center flex-md-justify-end">
|
||||
<a href="/conference/new" class="text-center btn btn-primary ml-3">
|
||||
<i class="fa fa-plus-circle" aria-hidden="true"></i> Add
|
||||
</a>
|
||||
<div class="w-100">
|
||||
<div class="paper-crumbs">Reference / <span class="here">Conferences</span></div>
|
||||
<div class="d-flex align-items-end justify-content-between flex-wrap" style="gap: 12px;">
|
||||
<div>
|
||||
<h1 class="paper-page-title">Your <em>conferences</em></h1>
|
||||
<p class="paper-page-lede">Group anonymizations by venue; set shared expiry and defaults.</p>
|
||||
</div>
|
||||
<a href="/conference/new" class="btn btn-ink">
|
||||
<i class="fa fa-plus-circle mr-1"></i> New conference
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<ul class="p-0 m-0 w-100">
|
||||
<li
|
||||
class="col-12 d-flex px-0 py-3 border-bottom color-border-secondary"
|
||||
ng-class="{'expired': conference.status == 'expired','removed': conference.status == 'removed' }"
|
||||
ng-repeat="conference in conferences| filter:conferenceFilter| orderBy:orderBy as filteredConferences"
|
||||
>
|
||||
<div class="w-100">
|
||||
<div class="">
|
||||
<h3>
|
||||
<a
|
||||
ng-href="/conference/{{conference.conferenceID}}"
|
||||
ng-bind="conference.name"
|
||||
></a>
|
||||
<span
|
||||
class="badge"
|
||||
ng-class="{'badge-warning': conference.status == 'removed' || conference.status == 'expired', 'badge-success': conference.status == 'ready', 'badge-danger': ''}"
|
||||
ng-bind="conference.status | title"
|
||||
></span>
|
||||
</h3>
|
||||
<span class="color-text-secondary mb-1 ng-binding">
|
||||
Conference ID: '{{conference.conferenceID}}'
|
||||
</span>
|
||||
</div>
|
||||
<div class="color-text-secondary mt-2">
|
||||
<span
|
||||
class="ml-0 mr-3"
|
||||
title="# repository: {{conference.nbRepository || 0 | number}}"
|
||||
data-toggle="tooltip"
|
||||
data-placement="bottom"
|
||||
>
|
||||
<i class="fas fa-table"></i>
|
||||
{{::conference.nbRepositories || 0 | number}}</span
|
||||
>
|
||||
<span class="ml-0 mr-3">
|
||||
<i class="fas fa-euro-sign"></i>
|
||||
Total: {{conference.price || 0 | number}} €
|
||||
</span>
|
||||
<span class="ml-0 mr-3">
|
||||
<i class="fas fa-calendar-alt"></i>
|
||||
From {{conference.startDate | date}} to {{conference.endDate |
|
||||
date}}</span
|
||||
>
|
||||
</div>
|
||||
|
||||
<form class="w-100 dashboard-filter-row" aria-label="Conferences" accept-charset="UTF-8">
|
||||
<div class="search-wrap">
|
||||
<input
|
||||
type="search"
|
||||
id="search"
|
||||
class="form-control"
|
||||
aria-label="Find a conference…"
|
||||
placeholder="Find a conference…"
|
||||
autocomplete="off"
|
||||
ng-model="search"
|
||||
/>
|
||||
</div>
|
||||
<div class="d-flex">
|
||||
<div class="d-flex flex-wrap" style="gap: 8px">
|
||||
<div class="dropdown">
|
||||
<button
|
||||
class="btn black_border dropdown-toggle btn-sm"
|
||||
class="btn 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="sortName" value="name" ng-model="orderBy" />
|
||||
<label class="form-check-label" for="sortName">Name</label>
|
||||
</div>
|
||||
<div class="form-check dropdown-item">
|
||||
<input class="form-check-input" type="radio" name="sort" id="sortID" value="conferenceID" ng-model="orderBy" />
|
||||
<label class="form-check-label" for="sortID">ID</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>
|
||||
</div>
|
||||
|
||||
<div class="dropdown">
|
||||
<button
|
||||
class="btn 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>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="paper-table paper-table-conferences w-100" role="table" aria-label="Conferences">
|
||||
<div class="paper-table-head" role="row">
|
||||
<div role="columnheader">Conference</div>
|
||||
<div role="columnheader">Status</div>
|
||||
<div role="columnheader" class="num">Repos</div>
|
||||
<div role="columnheader">Window</div>
|
||||
<div role="columnheader" aria-label="Actions"></div>
|
||||
</div>
|
||||
<div
|
||||
class="paper-table-row"
|
||||
role="row"
|
||||
ng-class="{'repo-inactive': conference.status == 'expired' || conference.status == 'removed'}"
|
||||
ng-repeat="conference in conferences| filter:conferenceFilter| orderBy:orderBy as filteredConferences"
|
||||
>
|
||||
<div class="cell-anon" role="cell">
|
||||
<span class="type-badge type-repo">Conf</span>
|
||||
<div class="anon-text">
|
||||
<a class="repo-name" ng-href="/conference/{{conference.conferenceID}}" ng-bind="conference.name"></a>
|
||||
<div class="anon-sub">
|
||||
ID <a ng-href="/conference/{{conference.conferenceID}}" ng-bind="conference.conferenceID"></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="cell-status" role="cell">
|
||||
<span class="status-dot" ng-class="{'status-removed': conference.status == 'removed' || conference.status == 'expired', 'status-ready': conference.status == 'ready'}"></span>
|
||||
<span ng-bind="conference.status | title"></span>
|
||||
</div>
|
||||
<div class="cell-views num" role="cell" ng-bind="::conference.nbRepositories || 0 | number"></div>
|
||||
<div class="cell-expires" role="cell">
|
||||
<span ng-if="conference.startDate">{{conference.startDate | date}} – {{conference.endDate | date}}</span>
|
||||
<span class="empty-dash" ng-if="!conference.startDate">—</span>
|
||||
</div>
|
||||
<div class="cell-actions" role="cell">
|
||||
<div class="dropdown">
|
||||
<button
|
||||
class="btn btn-icon-dots"
|
||||
type="button"
|
||||
id="dropdownMenuButton"
|
||||
data-toggle="dropdown"
|
||||
aria-haspopup="true"
|
||||
aria-expanded="false"
|
||||
aria-label="Actions"
|
||||
>
|
||||
Actions
|
||||
<i class="fas fa-ellipsis-h" aria-hidden="true"></i>
|
||||
</button>
|
||||
<div class="dropdown-menu" aria-labelledby="dropdownMenuButton">
|
||||
<a
|
||||
class="dropdown-item"
|
||||
href="/conference/{{conference.conferenceID}}/edit"
|
||||
>
|
||||
<div class="dropdown-menu dropdown-menu-right" aria-labelledby="dropdownMenuButton">
|
||||
<a class="dropdown-item" href="/conference/{{conference.conferenceID}}/edit">
|
||||
<i class="far fa-edit" aria-hidden="true"></i> Edit
|
||||
</a>
|
||||
<a
|
||||
class="dropdown-item"
|
||||
href="#"
|
||||
ng-show="conference.status != 'removed'"
|
||||
ng-click="removeConference(conference)"
|
||||
>
|
||||
<a class="dropdown-item" href="#" ng-show="conference.status != 'removed'" ng-click="removeConference(conference)">
|
||||
<i class="fas fa-trash-alt"></i> Remove
|
||||
</a>
|
||||
<a
|
||||
class="dropdown-item"
|
||||
href="/conference/{{conference.conferenceID}}/"
|
||||
>
|
||||
<a class="dropdown-item" href="/conference/{{conference.conferenceID}}/">
|
||||
<i class="fa fa-eye" aria-hidden="true"></i> View conference
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
<li class="col-12 d-flex px-0 py-3 border-bottom color-border-secondary" ng-if="filteredConferences.length == 0">
|
||||
There are no conference entries. You can create a <a href="/conference/new">new one</a>.
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="paper-table-empty" ng-if="filteredConferences.length == 0">
|
||||
<i class="fas fa-inbox"></i>
|
||||
<span>There are no conference entries. You can create a <a href="/conference/new">new one</a>.</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
<div class="container page dashboard-page">
|
||||
<div class="container page dashboard-page paper-page">
|
||||
<div class="row">
|
||||
<div class="w-100 py-3">
|
||||
<!-- Header row: title + action button -->
|
||||
<div class="d-flex align-items-center justify-content-between mb-3">
|
||||
<h2 class="dashboard-title mb-0">Dashboard</h2>
|
||||
<a href="/anonymize" class="btn btn-primary btn-sm">
|
||||
<i class="fa fa-plus-circle" aria-hidden="true"></i> Anonymize
|
||||
<div class="w-100">
|
||||
<div class="paper-crumbs">My work / <span class="here">Dashboard</span></div>
|
||||
<div class="d-flex align-items-end justify-content-between flex-wrap" style="gap: 12px;">
|
||||
<div>
|
||||
<h1 class="paper-page-title">Your <em>anonymizations</em></h1>
|
||||
<p class="paper-page-lede">Every repository and pull request you’ve mirrored, with live status and stats.</p>
|
||||
</div>
|
||||
<a href="/anonymize" class="btn btn-ink">
|
||||
<i class="fa fa-plus-circle mr-1" aria-hidden="true"></i> New anonymization
|
||||
</a>
|
||||
</div>
|
||||
|
||||
@@ -65,20 +68,19 @@
|
||||
</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">
|
||||
<form class="w-100 dashboard-filter-row" aria-label="Dashboard" accept-charset="UTF-8">
|
||||
<div class="search-wrap">
|
||||
<input
|
||||
type="search"
|
||||
id="search"
|
||||
class="form-control"
|
||||
aria-label="Search..."
|
||||
placeholder="Search anonymizations…"
|
||||
autocomplete="off"
|
||||
ng-model="search"
|
||||
/>
|
||||
</div>
|
||||
<div class="d-flex flex-wrap" style="gap: 8px">
|
||||
<!-- 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>
|
||||
@@ -150,7 +152,6 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<!-- Active filter chips -->
|
||||
@@ -164,90 +165,59 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Unified item list -->
|
||||
<ul class="repo-list w-100">
|
||||
<li
|
||||
class="repo-list-item"
|
||||
<!-- Table -->
|
||||
<div class="paper-table w-100" role="table" aria-label="Anonymizations">
|
||||
<div class="paper-table-head" role="row">
|
||||
<div role="columnheader">Anonymization</div>
|
||||
<div role="columnheader">Conference</div>
|
||||
<div role="columnheader">Status</div>
|
||||
<div role="columnheader" class="num">Views</div>
|
||||
<div role="columnheader">Expires</div>
|
||||
<div role="columnheader" aria-label="Actions"></div>
|
||||
</div>
|
||||
<div
|
||||
class="paper-table-row"
|
||||
role="row"
|
||||
ng-class="{'repo-inactive': item.status == 'expired' || item.status == 'removed' || item.status == 'error'}"
|
||||
ng-repeat="item in items | filter:itemFilter | orderBy:orderBy as filteredItems"
|
||||
>
|
||||
<div class="repo-list-item-content">
|
||||
<div class="repo-list-item-main">
|
||||
<div class="repo-list-item-header">
|
||||
<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
|
||||
class="status-badge"
|
||||
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="item.status | title"></span><span
|
||||
ng-if="item.status == 'error' && item.statusMessage"
|
||||
ng-bind="': ' + item.statusMessage"
|
||||
></span></span>
|
||||
<div class="cell-anon" role="cell">
|
||||
<span class="type-badge" ng-class="{'type-repo': item._type === 'repo', 'type-pr': item._type === 'pr'}">{{item._type === 'repo' ? 'Repo' : 'PR'}}</span>
|
||||
<div class="anon-text">
|
||||
<a ng-href="{{item._viewUrl}}" class="repo-name" ng-bind="item._name"></a>
|
||||
<div class="anon-sub">
|
||||
<a ng-if="item._type === 'repo'" href="https://github.com/{{item.source.fullName}}/" ng-bind="item.source.fullName"></a><span ng-if="item._type === 'repo' && item.options.update"> · <a href="https://github.com/{{item.source.fullName}}/tree/{{item.source.branch}}" ng-bind="item.source.branch"></a></span><span ng-if="item._type === 'repo' && !item.options.update"> · @<a href="https://github.com/{{item.source.fullName}}/tree/{{item.source.commit}}" ng-bind="item.source.commit.substring(0, 8)"></a></span>
|
||||
<a ng-if="item._type === 'pr'" href="https://github.com/{{item.source.repositoryFullName}}/pull/{{item.source.pullRequestId}}" ng-bind="item._source"></a>
|
||||
</div>
|
||||
<div class="repo-source">
|
||||
<span>
|
||||
<i class="fab fa-github" aria-hidden="true"></i>
|
||||
<a
|
||||
ng-if="item._type === 'repo'"
|
||||
href="https://github.com/{{item.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>
|
||||
</span>
|
||||
<span ng-if="item._type === 'repo' && item.options.update">
|
||||
<i class="fas fa-code-branch" aria-hidden="true"></i>
|
||||
<a
|
||||
href="https://github.com/{{item.source.fullName}}/tree/{{item.source.branch}}"
|
||||
ng-bind="item.source.branch"
|
||||
></a>
|
||||
</span>
|
||||
<span ng-if="item._type === 'repo' && !item.options.update">
|
||||
@<a
|
||||
href="https://github.com/{{item.source.fullName}}/tree/{{item.source.commit}}"
|
||||
ng-bind="item.source.commit.substring(0, 8)"
|
||||
></a>
|
||||
</span>
|
||||
<span class="repo-date">anonymized {{item.anonymizeDate | humanTime}}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="repo-meta">
|
||||
<span ng-if="item.conference" title="Conference">
|
||||
<i class="fas fa-chalkboard-teacher"></i> {{item.conference}}
|
||||
</span>
|
||||
<span title="Terms" data-toggle="tooltip" data-placement="bottom">
|
||||
<i class="fas fa-shield-alt"></i> {{item.options.terms.length | number}}
|
||||
</span>
|
||||
<span ng-if="item._type === 'repo' && item.size" title="Size" data-toggle="tooltip" data-placement="bottom">
|
||||
<i class="fas fa-database"></i> {{item.size.storage | humanFileSize}}
|
||||
</span>
|
||||
<span title="Views" data-toggle="tooltip" data-placement="bottom">
|
||||
<i class="far fa-eye"></i> {{item.pageView | number}}
|
||||
</span>
|
||||
<span title="Last view" data-toggle="tooltip" data-placement="bottom">
|
||||
<i class="far fa-calendar-alt"></i> {{item.lastView | humanTime}}
|
||||
</span>
|
||||
<span ng-if="item.options.expirationMode !== 'never' && item.status == 'ready'">
|
||||
<i class="far fa-clock"></i> Expire: {{item.options.expirationDate | humanTime}}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="repo-list-item-actions">
|
||||
<div class="cell-conf" role="cell">
|
||||
<span ng-if="item.conference" ng-bind="item.conference"></span>
|
||||
<span class="empty-dash" ng-if="!item.conference">—</span>
|
||||
</div>
|
||||
<div class="cell-status" role="cell">
|
||||
<span
|
||||
class="status-dot"
|
||||
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>
|
||||
<span ng-bind="item.status | title"></span>
|
||||
</div>
|
||||
<div class="cell-views num" role="cell" ng-bind="item.pageView | number"></div>
|
||||
<div class="cell-expires" role="cell">
|
||||
<span ng-if="item.options.expirationMode !== 'never' && item.status == 'ready'" ng-bind="item.options.expirationDate | humanTime"></span>
|
||||
<span class="empty-dash" ng-if="!(item.options.expirationMode !== 'never' && item.status == 'ready')">—</span>
|
||||
</div>
|
||||
<div class="cell-actions" role="cell">
|
||||
<div class="dropdown">
|
||||
<button
|
||||
class="btn btn-sm dropdown-toggle"
|
||||
class="btn btn-icon-dots"
|
||||
type="button"
|
||||
data-toggle="dropdown"
|
||||
aria-haspopup="true"
|
||||
aria-expanded="false"
|
||||
aria-label="Actions"
|
||||
>
|
||||
Actions
|
||||
<i class="fas fa-ellipsis-h" aria-hidden="true"></i>
|
||||
</button>
|
||||
<div class="dropdown-menu dropdown-menu-right">
|
||||
<a class="dropdown-item" ng-href="{{item._editUrl}}">
|
||||
@@ -271,11 +241,11 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
<li class="repo-list-empty" ng-if="filteredItems.length == 0">
|
||||
</div>
|
||||
<div class="paper-table-empty" ng-if="filteredItems.length == 0">
|
||||
<i class="fas fa-inbox"></i>
|
||||
<span>Nothing to display.</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -36,35 +36,35 @@
|
||||
<a
|
||||
ng-if="options.isAdmin || options.isOwner"
|
||||
ng-href="/anonymize/{{repoId}}"
|
||||
class="btn btn-outline-primary btn-sm"
|
||||
class="btn btn-sm"
|
||||
>Edit</a
|
||||
>
|
||||
<a
|
||||
ng-show="content != null"
|
||||
ng-href="{{url}}"
|
||||
target="__self"
|
||||
class="btn btn-outline-primary btn-sm"
|
||||
class="btn btn-sm"
|
||||
>Raw</a
|
||||
>
|
||||
<a
|
||||
ng-show="content != null"
|
||||
ng-href="{{url}}&download=true"
|
||||
target="__self"
|
||||
class="btn btn-outline-primary btn-sm"
|
||||
class="btn btn-sm"
|
||||
><i class="fas fa-download"></i><span class="d-none d-md-inline"> Download</span></a
|
||||
>
|
||||
<a
|
||||
ng-if="options.download"
|
||||
ng-href="/api/repo/{{repoId}}/zip"
|
||||
target="__self"
|
||||
class="btn btn-outline-primary btn-sm"
|
||||
class="btn btn-sm"
|
||||
><i class="fas fa-file-archive"></i><span class="d-none d-md-inline"> ZIP</span></a
|
||||
>
|
||||
<a
|
||||
ng-if="options.hasWebsite"
|
||||
ng-href="/w/{{repoId}}/"
|
||||
target="__self"
|
||||
class="btn btn-outline-primary btn-sm"
|
||||
class="btn btn-sm"
|
||||
>Website</a
|
||||
>
|
||||
</div>
|
||||
|
||||
+27
-17
@@ -1,18 +1,28 @@
|
||||
<div class="container-fluid h-100">
|
||||
<div class="row h-100">
|
||||
<div class="container px-md-3 px-sm-0">
|
||||
<section
|
||||
class="faq-section clearfix mt-3"
|
||||
aria-label="FAQs"
|
||||
>
|
||||
<div class="container">
|
||||
<h2 class="text-center pt-4 pb-2">Frequently Asked Questions</h2>
|
||||
<p class="text-center text-muted mb-4" style="font-size: 1.1rem;">
|
||||
Everything you need to know about using Anonymous GitHub for double-anonymous peer review.
|
||||
</p>
|
||||
<div class="paper-faq">
|
||||
<div class="paper-faq-hero">
|
||||
<div class="paper-faq-eyebrow">Help</div>
|
||||
<h1 class="paper-faq-title">Answers to the <em>questions</em> that come up most.</h1>
|
||||
<p class="paper-faq-lede">
|
||||
If something's missing, write to us —
|
||||
<a href="https://github.com/tdurieux/anonymous_github/issues">open an issue on GitHub</a>.
|
||||
</p>
|
||||
</div>
|
||||
<div class="paper-faq-body">
|
||||
<aside class="paper-faq-toc">
|
||||
<div class="paper-faq-toc-head">Contents</div>
|
||||
<nav>
|
||||
<a href="#faq-general">General</a>
|
||||
<a href="#faq-features">Features</a>
|
||||
<a href="#faq-limitations">Limitations</a>
|
||||
<a href="#faq-privacy">Privacy & Security</a>
|
||||
<a href="#faq-hosting">Self-Hosting</a>
|
||||
</nav>
|
||||
</aside>
|
||||
<section class="faq-section" aria-label="FAQs">
|
||||
<div>
|
||||
|
||||
<!-- General -->
|
||||
<h4 class="mt-4 mb-3" style="color: var(--link-color); font-weight: 600; border-bottom: 2px solid var(--border-color); padding-bottom: 0.5rem;">
|
||||
<h4 class="faq-cat">
|
||||
<i class="fas fa-info-circle mr-2"></i>General
|
||||
</h4>
|
||||
<div
|
||||
@@ -159,7 +169,7 @@
|
||||
</div>
|
||||
|
||||
<!-- Features -->
|
||||
<h4 class="mt-5 mb-3" style="color: var(--link-color); font-weight: 600; border-bottom: 2px solid var(--border-color); padding-bottom: 0.5rem;">
|
||||
<h4 class="faq-cat">
|
||||
<i class="fas fa-cogs mr-2"></i>Features
|
||||
</h4>
|
||||
<div
|
||||
@@ -376,7 +386,7 @@ anonymous_github</code></pre>
|
||||
</div>
|
||||
|
||||
<!-- Limitations -->
|
||||
<h4 class="mt-5 mb-3" style="color: var(--link-color); font-weight: 600; border-bottom: 2px solid var(--border-color); padding-bottom: 0.5rem;">
|
||||
<h4 class="faq-cat">
|
||||
<i class="fas fa-exclamation-triangle mr-2"></i>Limitations
|
||||
</h4>
|
||||
<div
|
||||
@@ -431,7 +441,7 @@ anonymous_github</code></pre>
|
||||
</div>
|
||||
|
||||
<!-- Privacy & Security -->
|
||||
<h4 class="mt-5 mb-3" style="color: var(--link-color); font-weight: 600; border-bottom: 2px solid var(--border-color); padding-bottom: 0.5rem;">
|
||||
<h4 class="faq-cat">
|
||||
<i class="fas fa-shield-alt mr-2"></i>Privacy & Security
|
||||
</h4>
|
||||
<div
|
||||
@@ -507,7 +517,7 @@ anonymous_github</code></pre>
|
||||
</div>
|
||||
|
||||
<!-- Self-Hosting -->
|
||||
<h4 class="mt-5 mb-3" style="color: var(--link-color); font-weight: 600; border-bottom: 2px solid var(--border-color); padding-bottom: 0.5rem;">
|
||||
<h4 class="faq-cat">
|
||||
<i class="fas fa-server mr-2"></i>Self-Hosting
|
||||
</h4>
|
||||
<div
|
||||
|
||||
+22
-23
@@ -1,8 +1,8 @@
|
||||
<nav
|
||||
class="navbar navbar-expand-lg navbar-dark bg-dark"
|
||||
ng-class="{'shadow': !generalMessage}"
|
||||
class="navbar navbar-expand-lg"
|
||||
ng-class="{'navbar-dark': isDarkMode}"
|
||||
>
|
||||
<a class="navbar-brand" href="/"> Anonymous GitHub </a>
|
||||
<a class="navbar-brand" href="/">Anonymous <em>GitHub</em></a>
|
||||
<button
|
||||
class="navbar-toggler"
|
||||
type="button"
|
||||
@@ -29,7 +29,7 @@
|
||||
href="/dashboard"
|
||||
>
|
||||
<i class="fas fa-th-large d-lg-none mr-1"></i>
|
||||
Dashboard
|
||||
My work
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item" ng-if="user">
|
||||
@@ -52,6 +52,11 @@
|
||||
Conferences
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" ng-class="{'active':path == '/faq'}" href="/faq"
|
||||
>FAQ</a
|
||||
>
|
||||
</li>
|
||||
<li class="nav-item" ng-if="user && user.isAdmin">
|
||||
<a
|
||||
class="nav-link"
|
||||
@@ -69,15 +74,11 @@
|
||||
class="nav-link nav-icon"
|
||||
target="_blank"
|
||||
href="https://github.com/tdurieux/anonymous_github/"
|
||||
title="GitHub repository"
|
||||
data-offset="30"
|
||||
><i class="fab fa-github" aria-hidden="true"></i
|
||||
></a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" ng-class="{'active':path == '/faq'}" href="/faq"
|
||||
>FAQ</a
|
||||
>
|
||||
</li>
|
||||
<li class="nav-item d-none d-lg-block">
|
||||
<a
|
||||
class="nav-link"
|
||||
@@ -89,28 +90,26 @@
|
||||
</li>
|
||||
|
||||
<li class="nav-item" ng-if="!isDarkMode">
|
||||
<a class="nav-link" href="#" ng-click="darkMode(true);">
|
||||
<i class="fas fa-moon d-lg-none mr-1"></i>
|
||||
Dark Mode
|
||||
<a class="nav-link nav-icon" href="#" ng-click="darkMode(true);" title="Dark mode">
|
||||
<i class="fas fa-moon"></i>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item" ng-if="isDarkMode">
|
||||
<a class="nav-link" href="#" ng-click="darkMode(false);">
|
||||
<i class="fas fa-sun d-lg-none mr-1"></i>
|
||||
Light Mode
|
||||
<a class="nav-link nav-icon" href="#" ng-click="darkMode(false);" title="Light mode">
|
||||
<i class="fas fa-sun"></i>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item" ng-if="!user">
|
||||
<a class="nav-link" target="_self" href="/github/login" data-offset="30"
|
||||
><i class="fab fa-github d-lg-none mr-1"></i>
|
||||
Login
|
||||
<a class="nav-link btn-signin" target="_self" href="/github/login" data-offset="30"
|
||||
><i class="fab fa-github mr-1"></i>
|
||||
Sign in
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item dropdown" ng-if="user">
|
||||
<li class="nav-item dropdown user-chip-wrap" ng-if="user">
|
||||
<a
|
||||
class="nav-link dropdown-toggle"
|
||||
class="nav-link user-chip dropdown-toggle"
|
||||
href="#"
|
||||
id="navbarDropdownMenuLink"
|
||||
role="button"
|
||||
@@ -121,11 +120,11 @@
|
||||
<img
|
||||
ng-src="{{user.photo}}"
|
||||
ng-if="user.photo"
|
||||
width="30"
|
||||
height="30"
|
||||
width="22"
|
||||
height="22"
|
||||
class="rounded-circle"
|
||||
/>
|
||||
{{user.username}}
|
||||
<span class="user-chip-name">{{user.username}}</span>
|
||||
</a>
|
||||
<div class="dropdown-menu dropdown-menu-right" aria-labelledby="navbarDropdownMenuLink">
|
||||
<a class="dropdown-item" href="/profile">Default settings</a>
|
||||
|
||||
+246
-130
@@ -1,142 +1,258 @@
|
||||
<div class="container-fluid">
|
||||
<div
|
||||
id="home"
|
||||
class="row view rgba-gradient d-flex align-self-stretch justify-content-center align-items-center"
|
||||
>
|
||||
<div class="container px-md-3 px-sm-0">
|
||||
<div class="row fadeIn main-options">
|
||||
<div class="col-md-12 mb-4 white-text text-center fadeIn">
|
||||
<h1 class="hero-title pt-5">
|
||||
Anonymous GitHub
|
||||
</h1>
|
||||
<p class="hero-subtitle mt-3 mb-4">
|
||||
Anonymize your repository in 5 minutes — join {{stat.nbUsers | number}} users.
|
||||
</p>
|
||||
<span ng-if="!user">
|
||||
<a
|
||||
href="/github/login"
|
||||
target="_self"
|
||||
class="btn btn-hero"
|
||||
><i class="fab fa-github mr-2"></i>Login with GitHub</a
|
||||
>
|
||||
</span>
|
||||
<span ng-if="user">
|
||||
<a
|
||||
href="/anonymize"
|
||||
class="btn btn-hero"
|
||||
><i class="fa fa-plus-circle mr-2"></i>Anonymize a Repository</a
|
||||
>
|
||||
</span>
|
||||
<div class="paper-landing">
|
||||
<!-- Hero -->
|
||||
<section class="paper-hero">
|
||||
<div class="paper-eyebrow">For double-blind peer review</div>
|
||||
<h1 class="hero-title">
|
||||
Share the code,<br />
|
||||
<span class="accent">not the author.</span>
|
||||
</h1>
|
||||
<p class="hero-subtitle">
|
||||
Anonymous GitHub makes a read-only mirror of your repository or pull
|
||||
request with every trace of identity removed — so you can attach a
|
||||
link to your submission without breaking anonymity.
|
||||
</p>
|
||||
|
||||
<div class="paper-cta-row">
|
||||
<span ng-if="!user">
|
||||
<a href="/github/login" target="_self" class="btn-hero">
|
||||
<i class="fab fa-github mr-2"></i>Sign in with GitHub
|
||||
</a>
|
||||
</span>
|
||||
<span ng-if="user">
|
||||
<a href="/anonymize" class="btn-hero">
|
||||
Anonymize a repository →
|
||||
</a>
|
||||
</span>
|
||||
<a href="/anonymize" ng-if="user" class="btn-hero-ghost">
|
||||
Anonymize a pull request
|
||||
</a>
|
||||
<span class="meta">Free · Open source · Self-hostable</span>
|
||||
</div>
|
||||
|
||||
<!-- Before / after -->
|
||||
<div class="paper-preview" aria-label="Before and after example">
|
||||
<div class="pane">
|
||||
<div class="pane-header">
|
||||
<span class="pane-label">Before · your repo</span>
|
||||
<span class="pane-url">github.com/jane-smith/DeepLearnUtils</span>
|
||||
</div>
|
||||
<div class="pane-body">
|
||||
<div><span class="tok">#</span> <strong>DeepLearnUtils</strong></div>
|
||||
<div>Developed by Jane Smith at MIT CSAIL.</div>
|
||||
<div>See Smith et al., “Attention That Works,” 2026.</div>
|
||||
<div>Contact: jane.smith@mit.edu</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="pane">
|
||||
<div class="pane-header">
|
||||
<span class="pane-label">After · anonymous mirror</span>
|
||||
<span class="pane-url">anonymous.4open.science/r/paper-7f2a</span>
|
||||
</div>
|
||||
<div class="pane-body">
|
||||
<div><span class="tok">#</span> <strong>DeepLearnUtils</strong></div>
|
||||
<div>Developed by <span class="redact">Jane Smith</span> at <span class="redact">MIT CSAIL</span>.</div>
|
||||
<div>See <span class="redact">Smith et al.</span>, “Attention That Works,” 2026.</div>
|
||||
<div>Contact: <span class="redact">jane.smith@mit.edu</span></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<main>
|
||||
<div class="container home-content">
|
||||
<div class="row pt-5">
|
||||
<div class="col-md-12">
|
||||
<h2 id="usage">How it works</h2>
|
||||
<ol class="usage-steps">
|
||||
<li>
|
||||
<a href="/github/login" target="_self">Login</a> with GitHub to access your dashboard and anonymize your repositories.
|
||||
</li>
|
||||
<li>Configure which terms, links, and images to anonymize.</li>
|
||||
<li>Share the anonymous link in your double-blind paper submission.</li>
|
||||
</ol>
|
||||
<p class="usage-example">
|
||||
</section>
|
||||
|
||||
<!-- Stats strip -->
|
||||
<section class="paper-stats" id="metrics">
|
||||
<div class="paper-stats-inner">
|
||||
<div>
|
||||
<div class="paper-stat-value">{{stat.nbRepositories | number}}</div>
|
||||
<div class="paper-stat-label">repositories anonymized</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="paper-stat-value">{{stat.nbUsers | number}}</div>
|
||||
<div class="paper-stat-label">researchers</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="paper-stat-value">{{stat.nbPageViews | number}}</div>
|
||||
<div class="paper-stat-label">page views</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- How it works -->
|
||||
<section class="paper-how" id="usage">
|
||||
<div>
|
||||
<div class="paper-eyebrow">How it works</div>
|
||||
<h2 class="paper-how-heading">
|
||||
Three steps,<br />about a minute.
|
||||
</h2>
|
||||
</div>
|
||||
<div>
|
||||
<div class="paper-step">
|
||||
<div class="step-num">01</div>
|
||||
<div>
|
||||
<div class="step-title">Authenticate</div>
|
||||
<div class="step-desc">
|
||||
<a href="/github/login" target="_self">Sign in with GitHub</a> to
|
||||
access your dashboard. We ask for read-only access to the
|
||||
repositories you pick — nothing else.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="paper-step">
|
||||
<div class="step-num">02</div>
|
||||
<div>
|
||||
<div class="step-title">Configure</div>
|
||||
<div class="step-desc">
|
||||
Point us at a repo, branch, commit, or pull request. List the
|
||||
terms to redact (names, affiliation, emails). Set an expiry.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="paper-step">
|
||||
<div class="step-num">03</div>
|
||||
<div>
|
||||
<div class="step-title">Share</div>
|
||||
<div class="step-desc">
|
||||
You get a stable link with a randomised slug. Reviewers browse,
|
||||
diff, and download — never seeing who you are.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="paper-step" style="border-top: none;">
|
||||
<div class="step-num"> </div>
|
||||
<div>
|
||||
<div class="step-desc">
|
||||
Example:
|
||||
<a
|
||||
target="_self"
|
||||
href="https://anonymous.4open.science/r/840c8c57-3c32-451e-bf12-0e20be300389/"
|
||||
>https://anonymous.4open.science/r/840c8c57-3c32-451e-bf12-0e20be300389/</a
|
||||
>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr class="featurette-divider" />
|
||||
|
||||
<div class="row featurette align-items-center">
|
||||
<div class="col-md-7 order-md-2">
|
||||
<h2 class="featurette-heading">
|
||||
Double-anonymous
|
||||
</h2>
|
||||
<p class="featurette-lead">
|
||||
Anonymize your GitHub repository with options to remove links,
|
||||
images, or specific terms. Keep full control — set an
|
||||
expiration date to make your repository unavailable after review.
|
||||
</p>
|
||||
</div>
|
||||
<div class="col-md-5 order-md-1">
|
||||
<img
|
||||
width="500"
|
||||
src="/imgs/anonymize.png"
|
||||
class="featurette-image img-fluid mx-auto"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr class="featurette-divider" />
|
||||
|
||||
<div class="row featurette align-items-center">
|
||||
<div class="col-md-7">
|
||||
<h2 class="featurette-heading">
|
||||
Explorer
|
||||
</h2>
|
||||
<p class="featurette-lead">
|
||||
Reviewers can browse your repository with highlighted source code,
|
||||
rendered PDFs, images, and notebooks.
|
||||
<a href="https://pages.github.com">GitHub Pages</a>
|
||||
is also supported.
|
||||
</p>
|
||||
</div>
|
||||
<div class="col-md-5">
|
||||
<img
|
||||
width="500"
|
||||
src="/imgs/explorer.png"
|
||||
class="featurette-image img-fluid mx-auto"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr class="featurette-divider" />
|
||||
|
||||
<div class="row featurette align-items-center">
|
||||
<div class="col-md-7 order-md-2">
|
||||
<h2 class="featurette-heading">
|
||||
Manage
|
||||
</h2>
|
||||
<p class="featurette-lead">
|
||||
Monitor views, edit configuration, remove or update your repository —
|
||||
all from a clean dashboard.
|
||||
</p>
|
||||
</div>
|
||||
<div class="col-md-5 order-md-1">
|
||||
<img
|
||||
width="500"
|
||||
src="/imgs/dashboard.png"
|
||||
class="featurette-image img-fluid mx-auto"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr class="featurette-divider" />
|
||||
|
||||
<h2 id="about" class="mb-4">Metrics</h2>
|
||||
<div class="metrics-grid">
|
||||
<div class="metric-card">
|
||||
<div class="metric-value">{{stat.nbUsers | number}}</div>
|
||||
<div class="metric-label">Users</div>
|
||||
</div>
|
||||
<div class="metric-card">
|
||||
<div class="metric-value">{{stat.nbRepositories | number}}</div>
|
||||
<div class="metric-label">Repositories</div>
|
||||
</div>
|
||||
<div class="metric-card">
|
||||
<div class="metric-value">{{stat.nbPageViews | number}}</div>
|
||||
<div class="metric-label">Page Views</div>
|
||||
>anonymous.4open.science/r/840c8c57-3c32-451e-bf12-0e20be300389</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Trust row -->
|
||||
<section class="paper-trust">
|
||||
<div class="paper-trust-inner">
|
||||
<div>
|
||||
<div class="paper-eyebrow">Trusted by the community</div>
|
||||
<div class="paper-trust-lede">
|
||||
Used by authors submitting to major software-engineering and systems
|
||||
venues.
|
||||
</div>
|
||||
</div>
|
||||
<div class="paper-trust-item">
|
||||
<div class="short">ICSE</div>
|
||||
<div class="long">International Conference on Software Engineering</div>
|
||||
</div>
|
||||
<div class="paper-trust-item">
|
||||
<div class="short">FSE</div>
|
||||
<div class="long">Foundations of Software Engineering</div>
|
||||
</div>
|
||||
<div class="paper-trust-item">
|
||||
<div class="short">OOPSLA</div>
|
||||
<div class="long">
|
||||
Object-Oriented Programming, Systems, Languages, and Applications
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Featurettes — existing content, kept but restyled -->
|
||||
<main class="container home-content" id="about">
|
||||
<div class="row featurette align-items-center">
|
||||
<div class="col-md-7 order-md-2">
|
||||
<h2 class="featurette-heading">Double-anonymous</h2>
|
||||
<p class="featurette-lead">
|
||||
Anonymize your GitHub repository with options to remove links,
|
||||
images, or specific terms. Keep full control — set an
|
||||
expiration date to make your repository unavailable after review.
|
||||
</p>
|
||||
</div>
|
||||
<div class="col-md-5 order-md-1">
|
||||
<img
|
||||
width="500"
|
||||
src="/imgs/anonymize.png"
|
||||
class="featurette-image img-fluid mx-auto"
|
||||
alt="Anonymize form"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr class="featurette-divider" />
|
||||
|
||||
<div class="row featurette align-items-center">
|
||||
<div class="col-md-7">
|
||||
<h2 class="featurette-heading">Explorer</h2>
|
||||
<p class="featurette-lead">
|
||||
Reviewers can browse your repository with highlighted source code,
|
||||
rendered PDFs, images, and notebooks.
|
||||
<a href="https://pages.github.com">GitHub Pages</a> is also supported.
|
||||
</p>
|
||||
</div>
|
||||
<div class="col-md-5">
|
||||
<img
|
||||
width="500"
|
||||
src="/imgs/explorer.png"
|
||||
class="featurette-image img-fluid mx-auto"
|
||||
alt="Repository explorer"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr class="featurette-divider" />
|
||||
|
||||
<div class="row featurette align-items-center">
|
||||
<div class="col-md-7 order-md-2">
|
||||
<h2 class="featurette-heading">Manage</h2>
|
||||
<p class="featurette-lead">
|
||||
Monitor views, edit configuration, remove or update your repository
|
||||
— all from a clean dashboard.
|
||||
</p>
|
||||
</div>
|
||||
<div class="col-md-5 order-md-1">
|
||||
<img
|
||||
width="500"
|
||||
src="/imgs/dashboard.png"
|
||||
class="featurette-image img-fluid mx-auto"
|
||||
alt="Dashboard"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<footer class="paper-footer">
|
||||
<div class="paper-footer-inner">
|
||||
<div class="paper-footer-brand">
|
||||
<div class="paper-footer-mark">Anonymous <em>GitHub</em></div>
|
||||
<p class="paper-footer-tag">
|
||||
A read-only, identity-stripped mirror of your repository —
|
||||
built for double-blind peer review.
|
||||
</p>
|
||||
</div>
|
||||
<div class="paper-footer-col">
|
||||
<div class="paper-footer-head">Product</div>
|
||||
<a href="/anonymize">Anonymize</a>
|
||||
<a href="/dashboard" ng-if="user">My work</a>
|
||||
<a href="/conferences" ng-if="user">Conferences</a>
|
||||
<a href="/faq">FAQ</a>
|
||||
</div>
|
||||
<div class="paper-footer-col">
|
||||
<div class="paper-footer-head">Project</div>
|
||||
<a href="https://github.com/tdurieux/anonymous_github/" target="_blank">Source code</a>
|
||||
<a href="https://github.com/tdurieux/anonymous_github/issues/new" target="_blank">Report an issue</a>
|
||||
<a href="https://github.com/sponsors/tdurieux/" target="_blank">Sponsor</a>
|
||||
</div>
|
||||
<div class="paper-footer-col">
|
||||
<div class="paper-footer-head">Contact</div>
|
||||
<a href="mailto:tdurieux@anonymous.4open.science">Email maintainer</a>
|
||||
<a href="https://github.com/tdurieux" target="_blank">@tdurieux</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="paper-footer-rule"></div>
|
||||
<div class="paper-footer-foot">
|
||||
<span>© {{year || 2026}} Anonymous GitHub · MIT licensed</span>
|
||||
<span class="paper-footer-meta">Built for reviewers, by researchers.</span>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
@@ -1,4 +1,10 @@
|
||||
<div class="container d-flex h-100">
|
||||
<h1 class="display-1 m-auto" ng-if="!error">Loading...</h1>
|
||||
<h1 class="display-1 m-auto" ng-if="error" translate="{{error}}"></h1>
|
||||
<div class="paper-empty">
|
||||
<div class="paper-empty-inner">
|
||||
<div class="paper-eyebrow" ng-if="!error">Working</div>
|
||||
<div class="paper-eyebrow" ng-if="error">Error</div>
|
||||
<h1 class="paper-empty-title" ng-if="!error">
|
||||
Loading<span class="dot-anim">…</span>
|
||||
</h1>
|
||||
<h1 class="paper-empty-title" ng-if="error" translate="{{error}}"></h1>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
+248
-423
@@ -1,443 +1,268 @@
|
||||
<div class="container py-4">
|
||||
<h2>Create a conference</h2>
|
||||
<p>
|
||||
A conference allow the the chairs to access the complete list of anonymized
|
||||
repositories. It also allows to increase the quota for authors.
|
||||
<div class="container paper-page paper-settings">
|
||||
<div class="paper-crumbs"><a href="/conferences">Conferences</a> / <span class="here">{{editionMode ? 'Edit' : 'New'}}</span></div>
|
||||
<h1 class="paper-page-title">{{editionMode ? 'Edit' : 'Create a'}} <em>conference</em></h1>
|
||||
<p class="paper-page-lede">
|
||||
Give chairs access to every anonymization submitted to a venue, and lift
|
||||
author quotas during the review window.
|
||||
</p>
|
||||
<form class="form needs-validation" name="conference" novalidate>
|
||||
<!-- name -->
|
||||
<div class="form-group">
|
||||
<label for="name">The name of the conference</label>
|
||||
<input
|
||||
type="text"
|
||||
class="form-control"
|
||||
name="name"
|
||||
id="name"
|
||||
required
|
||||
ng-class="{'is-invalid': conference.name.$invalid}"
|
||||
ng-model="options.name"
|
||||
placeholder="The name of the conference"
|
||||
/>
|
||||
<div class="invalid-feedback" ng-show="conference.name.$error.invalid">
|
||||
The name of the conference is invalid.
|
||||
</div>
|
||||
<div class="invalid-feedback" ng-show="conference.name.$error.required">
|
||||
The name of the conference is required.
|
||||
</div>
|
||||
</div>
|
||||
<!-- url -->
|
||||
<div class="form-group">
|
||||
<label for="url">The url of the conference</label>
|
||||
<input
|
||||
type="url"
|
||||
class="form-control"
|
||||
name="url"
|
||||
id="url"
|
||||
ng-class="{'is-invalid': conference.url.$invalid}"
|
||||
ng-model="options.url"
|
||||
placeholder="The url of the conference"
|
||||
/>
|
||||
<div class="invalid-feedback" ng-show="conference.url.$error.invalid">
|
||||
The url of the conference is invalid.
|
||||
</div>
|
||||
</div>
|
||||
<!-- conferenceID -->
|
||||
<div class="form-group">
|
||||
<label for="conferenceID">The conference ID</label>
|
||||
<input
|
||||
type="text"
|
||||
class="form-control"
|
||||
name="conferenceID"
|
||||
id="conferenceID"
|
||||
required
|
||||
pattern="[a-zA-Z0-9\-_]{3,10}"
|
||||
ng-class="{'is-invalid': conference.conferenceID.$invalid}"
|
||||
ng-model="options.conferenceID"
|
||||
placeholder="The conference ID that the authors will reference"
|
||||
/>
|
||||
<div
|
||||
class="invalid-feedback"
|
||||
ng-show="conference.conferenceID.$error.used"
|
||||
>
|
||||
The conference ID '{{options.conferenceID}}' is already used.
|
||||
</div>
|
||||
<div
|
||||
class="invalid-feedback"
|
||||
ng-show="conference.conferenceID.$error.required"
|
||||
>
|
||||
The conference ID is required.
|
||||
</div>
|
||||
<div
|
||||
class="invalid-feedback"
|
||||
ng-show="conference.conferenceID.$error.pattern"
|
||||
>
|
||||
The format of the conference ID is incorrect ([a-zA-Z0-9-_]{3,10}).
|
||||
</div>
|
||||
</div>
|
||||
<!-- startDate -->
|
||||
<div class="form-group">
|
||||
<label for="startDate">Start date of the conference</label>
|
||||
<input
|
||||
type="date"
|
||||
class="form-control"
|
||||
name="startDate"
|
||||
id="startDate"
|
||||
required
|
||||
ng-class="{'is-invalid': conference.startDate.$invalid}"
|
||||
ng-model="options.startDate"
|
||||
/>
|
||||
<small class="form-text text-muted"
|
||||
>The beginning date of the reviewing process.</small
|
||||
>
|
||||
<div
|
||||
class="invalid-feedback"
|
||||
ng-show="conference.startDate.$error.required"
|
||||
>
|
||||
The start date of the conference is required.
|
||||
</div>
|
||||
<div
|
||||
class="invalid-feedback"
|
||||
ng-show="conference.startDate.$error.invalid"
|
||||
>
|
||||
The start date of the conference is invalid. The start date should
|
||||
always be smaller than the end date.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- endDate -->
|
||||
<div class="form-group">
|
||||
<label for="endDate">End date of the conference</label>
|
||||
<input
|
||||
type="date"
|
||||
class="form-control"
|
||||
name="endDate"
|
||||
id="endDate"
|
||||
required
|
||||
ng-class="{'is-invalid': conference.endDate.$invalid}"
|
||||
ng-model="options.endDate"
|
||||
/>
|
||||
<small class="form-text text-muted"
|
||||
>The end date of the reviewing process. All the repositories will expire
|
||||
and they will not be accessible after this date.</small
|
||||
>
|
||||
<div
|
||||
class="invalid-feedback"
|
||||
ng-show="conference.endDate.$error.required"
|
||||
>
|
||||
The end date of the conference is required.
|
||||
</div>
|
||||
<div class="invalid-feedback" ng-show="conference.endDate.$error.invalid">
|
||||
The end date of the conference is invalid. The end date should always be
|
||||
smaller than today.
|
||||
</div>
|
||||
</div>
|
||||
<div class="paper-settings-body">
|
||||
<aside class="paper-settings-toc">
|
||||
<div class="paper-settings-toc-head">Contents</div>
|
||||
<nav>
|
||||
<a href="#conf-basics">Basics</a>
|
||||
<a href="#conf-window">Review window</a>
|
||||
<a href="#conf-rendering">Rendering defaults</a>
|
||||
<a href="#conf-features">Features</a>
|
||||
<a href="#conf-plan">Plan</a>
|
||||
<a href="#conf-billing" ng-show="plan.pricePerRepo > 0">Billing</a>
|
||||
</nav>
|
||||
</aside>
|
||||
|
||||
<h4>Anonymization options</h4>
|
||||
<form class="form needs-validation paper-settings-main" name="conference" novalidate>
|
||||
<section id="conf-basics" class="paper-settings-section">
|
||||
<div class="paper-section-eyebrow">Basics</div>
|
||||
|
||||
<div class="accordion mb-3" id="options">
|
||||
<div class="card">
|
||||
<div class="card-header" id="headingOne">
|
||||
<h2 class="mb-0">
|
||||
<button
|
||||
class="btn btn-block text-left"
|
||||
type="button"
|
||||
data-toggle="collapse"
|
||||
data-target="#collapseOne"
|
||||
aria-expanded="true"
|
||||
aria-controls="collapseOne"
|
||||
>
|
||||
Rendering options
|
||||
</button>
|
||||
</h2>
|
||||
<div class="form-group">
|
||||
<label class="paper-field-label" for="name">Conference name</label>
|
||||
<input
|
||||
type="text"
|
||||
class="form-control"
|
||||
name="name"
|
||||
id="name"
|
||||
required
|
||||
ng-class="{'is-invalid': conference.name.$invalid}"
|
||||
ng-model="options.name"
|
||||
placeholder="e.g. International Conference on Software Engineering"
|
||||
/>
|
||||
<div class="invalid-feedback" ng-show="conference.name.$error.invalid">
|
||||
The name of the conference is invalid.
|
||||
</div>
|
||||
<div class="invalid-feedback" ng-show="conference.name.$error.required">
|
||||
The name of the conference is required.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
id="collapseOne"
|
||||
class="collapse show"
|
||||
aria-labelledby="headingOne"
|
||||
data-parent="#options"
|
||||
>
|
||||
<div class="card-body">
|
||||
<div class="form-group">
|
||||
<div class="form-check">
|
||||
<input
|
||||
class="form-check-input"
|
||||
type="checkbox"
|
||||
id="link"
|
||||
name="link"
|
||||
ng-model="options.options.link"
|
||||
/>
|
||||
<label class="form-check-label" for="link">Keep links</label>
|
||||
<small id="linkHelp" class="form-text text-muted"
|
||||
>Keep or remove all links from text files.</small
|
||||
>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input
|
||||
class="form-check-input"
|
||||
type="checkbox"
|
||||
id="image"
|
||||
name="image"
|
||||
ng-model="options.options.image"
|
||||
/>
|
||||
<label class="form-check-label" for="image"
|
||||
>Display images</label
|
||||
>
|
||||
<small id="imageHelp" class="form-text text-muted"
|
||||
>Display or hide images from the repositories and text
|
||||
files.</small
|
||||
>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input
|
||||
class="form-check-input"
|
||||
type="checkbox"
|
||||
id="pdf"
|
||||
name="pdf"
|
||||
ng-model="options.options.pdf"
|
||||
/>
|
||||
<label class="form-check-label" for="pdf">Display PDFs</label>
|
||||
<small id="pdfHelp" class="form-text text-muted"
|
||||
>Display or hide PDF from the repositories.</small
|
||||
>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input
|
||||
class="form-check-input"
|
||||
type="checkbox"
|
||||
id="notebook"
|
||||
name="notebook"
|
||||
ng-model="options.options.notebook"
|
||||
/>
|
||||
<label class="form-check-label" for="notebook"
|
||||
>Display Notebooks</label
|
||||
>
|
||||
<small id="notebookHelp" class="form-text text-muted"
|
||||
>Display or hide Notebooks from the repositories.</small
|
||||
>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="paper-field-label" for="url">Website</label>
|
||||
<input
|
||||
type="url"
|
||||
class="form-control"
|
||||
name="url"
|
||||
id="url"
|
||||
ng-class="{'is-invalid': conference.url.$invalid}"
|
||||
ng-model="options.url"
|
||||
placeholder="https://example.org"
|
||||
/>
|
||||
<div class="invalid-feedback" ng-show="conference.url.$error.invalid">
|
||||
The url of the conference is invalid.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="paper-field-label" for="conferenceID">Conference ID</label>
|
||||
<input
|
||||
type="text"
|
||||
class="form-control"
|
||||
name="conferenceID"
|
||||
id="conferenceID"
|
||||
required
|
||||
pattern="[a-zA-Z0-9\-_]{3,10}"
|
||||
ng-class="{'is-invalid': conference.conferenceID.$invalid}"
|
||||
ng-model="options.conferenceID"
|
||||
placeholder="ICSE26"
|
||||
/>
|
||||
<small class="form-text text-muted">3–10 characters, letters, numbers, <code>-</code> and <code>_</code>. Authors reference this when they anonymize.</small>
|
||||
<div class="invalid-feedback" ng-show="conference.conferenceID.$error.used">
|
||||
The conference ID '{{options.conferenceID}}' is already used.
|
||||
</div>
|
||||
<div class="invalid-feedback" ng-show="conference.conferenceID.$error.required">
|
||||
The conference ID is required.
|
||||
</div>
|
||||
<div class="invalid-feedback" ng-show="conference.conferenceID.$error.pattern">
|
||||
The format of the conference ID is incorrect ([a-zA-Z0-9-_]{3,10}).
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="conf-window" class="paper-settings-section">
|
||||
<div class="paper-section-eyebrow">Review window</div>
|
||||
|
||||
<div class="form-grid-2">
|
||||
<div class="form-group">
|
||||
<label class="paper-field-label" for="startDate">Start date</label>
|
||||
<input
|
||||
type="date"
|
||||
class="form-control"
|
||||
name="startDate"
|
||||
id="startDate"
|
||||
required
|
||||
ng-class="{'is-invalid': conference.startDate.$invalid}"
|
||||
ng-model="options.startDate"
|
||||
/>
|
||||
<small class="form-text text-muted">Beginning of the review process.</small>
|
||||
<div class="invalid-feedback" ng-show="conference.startDate.$error.required">
|
||||
Start date is required.
|
||||
</div>
|
||||
<div class="invalid-feedback" ng-show="conference.startDate.$error.invalid">
|
||||
Start date must be before end date.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="paper-field-label" for="endDate">End date</label>
|
||||
<input
|
||||
type="date"
|
||||
class="form-control"
|
||||
name="endDate"
|
||||
id="endDate"
|
||||
required
|
||||
ng-class="{'is-invalid': conference.endDate.$invalid}"
|
||||
ng-model="options.endDate"
|
||||
/>
|
||||
<small class="form-text text-muted">All repositories expire on this date.</small>
|
||||
<div class="invalid-feedback" ng-show="conference.endDate.$error.required">
|
||||
End date is required.
|
||||
</div>
|
||||
<div class="invalid-feedback" ng-show="conference.endDate.$error.invalid">
|
||||
End date is invalid.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card">
|
||||
<div class="card-header" id="headingTwo">
|
||||
<h2 class="mb-0">
|
||||
<button
|
||||
class="btn btn-block text-left collapsed"
|
||||
type="button"
|
||||
data-toggle="collapse"
|
||||
data-target="#collapseTwo"
|
||||
aria-expanded="false"
|
||||
aria-controls="collapseTwo"
|
||||
>
|
||||
Features
|
||||
</button>
|
||||
</h2>
|
||||
</div>
|
||||
<div
|
||||
id="collapseTwo"
|
||||
class="collapse"
|
||||
aria-labelledby="headingTwo"
|
||||
data-parent="#options"
|
||||
>
|
||||
<div class="card-body">
|
||||
<div class="form-group">
|
||||
<div class="form-check">
|
||||
<input
|
||||
class="form-check-input"
|
||||
type="checkbox"
|
||||
id="update"
|
||||
name="update"
|
||||
ng-model="options.options.update"
|
||||
/>
|
||||
<label class="form-check-label" for="update">Auto update</label>
|
||||
<small id="termsHelp" class="form-text text-muted"
|
||||
>Automatically update the anonymized repository with the
|
||||
latest commit of the repository. The repository is updated
|
||||
once per hour maximum.</small
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="form-check">
|
||||
<input
|
||||
class="form-check-input"
|
||||
type="checkbox"
|
||||
id="page"
|
||||
name="page"
|
||||
ng-model="options.options.page"
|
||||
/>
|
||||
<label class="form-check-label" for="page">Github page</label>
|
||||
<small id="termsHelp" class="form-text text-muted"
|
||||
>Enable anonymized Github pages.</small
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<h4>Plan</h4>
|
||||
<small class="text-muted">The repositories are bill per hour.</small>
|
||||
<div class="card-deck mb-3 text-center">
|
||||
<div class="card mb-4 shadow-sm" ng-repeat="plan in plans">
|
||||
<div class="card-header">
|
||||
<h4 class="my-0 font-weight-normal" ng-bind="plan.name"></h4>
|
||||
<section id="conf-rendering" class="paper-settings-section">
|
||||
<div class="paper-section-eyebrow">Rendering defaults</div>
|
||||
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" id="link" name="link" ng-model="options.options.link" />
|
||||
<label class="form-check-label" for="link">Keep links</label>
|
||||
<small class="form-text text-muted">Keep or remove all links from text files.</small>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<h1 class="card-title pricing-card-title">
|
||||
{{plan.pricePerRepo | number}}€
|
||||
<small class="text-muted"> / repository / month</small>
|
||||
</h1>
|
||||
<ul
|
||||
class="list-unstyled mt-3 mb-4"
|
||||
ng-bind-html="plan.description"
|
||||
></ul>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-lg btn-block"
|
||||
ng-class="{'btn-primary': options.plan.planID == plan.id}"
|
||||
ng-click="options.plan.planID = plan.id"
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" id="image" name="image" ng-model="options.options.image" />
|
||||
<label class="form-check-label" for="image">Display images</label>
|
||||
<small class="form-text text-muted">Images are not anonymized.</small>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" id="pdf" name="pdf" ng-model="options.options.pdf" />
|
||||
<label class="form-check-label" for="pdf">Display PDFs</label>
|
||||
<small class="form-text text-muted">PDFs are not anonymized.</small>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" id="notebook" name="notebook" ng-model="options.options.notebook" />
|
||||
<label class="form-check-label" for="notebook">Display Notebooks</label>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="conf-features" class="paper-settings-section">
|
||||
<div class="paper-section-eyebrow">Features</div>
|
||||
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" id="update" name="update" ng-model="options.options.update" />
|
||||
<label class="form-check-label" for="update">Auto update</label>
|
||||
<small class="form-text text-muted">Pull the latest commit automatically (hourly maximum).</small>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" id="page" name="page" ng-model="options.options.page" />
|
||||
<label class="form-check-label" for="page">GitHub pages</label>
|
||||
<small class="form-text text-muted">Enable anonymized GitHub pages.</small>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="conf-plan" class="paper-settings-section">
|
||||
<div class="paper-section-eyebrow">Plan</div>
|
||||
<p class="paper-settings-copy">Repositories are billed per hour.</p>
|
||||
|
||||
<div class="paper-plan-grid">
|
||||
<label
|
||||
class="paper-plan-card"
|
||||
ng-class="{'selected': options.plan.planID == plan.id}"
|
||||
ng-repeat="plan in plans"
|
||||
>
|
||||
Select
|
||||
</button>
|
||||
<input
|
||||
type="radio"
|
||||
name="planPicker"
|
||||
ng-value="plan.id"
|
||||
ng-model="options.plan.planID"
|
||||
/>
|
||||
<div class="paper-plan-head" ng-bind="plan.name"></div>
|
||||
<div class="paper-plan-price">
|
||||
{{plan.pricePerRepo | number}}€
|
||||
<span class="paper-plan-per">/ repo / month</span>
|
||||
</div>
|
||||
<div class="paper-plan-desc" ng-bind-html="plan.description"></div>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div ng-if="plan.pricePerRepo > 0">
|
||||
<h3>Billing</h3>
|
||||
</section>
|
||||
|
||||
<!-- name -->
|
||||
<div class="form-group">
|
||||
<label for="billing_name">The name & last name</label>
|
||||
<input
|
||||
type="text"
|
||||
class="form-control"
|
||||
name="billing_name"
|
||||
id="billing_name"
|
||||
required
|
||||
ng-class="{'is-invalid': conference.billing_name.$invalid}"
|
||||
ng-model="options.billing.name"
|
||||
placeholder="Name & last name"
|
||||
/>
|
||||
</div>
|
||||
<section id="conf-billing" class="paper-settings-section" ng-if="plan.pricePerRepo > 0">
|
||||
<div class="paper-section-eyebrow">Billing</div>
|
||||
|
||||
<!-- email -->
|
||||
<div class="form-group">
|
||||
<label for="email">Email</label>
|
||||
<input
|
||||
type="email"
|
||||
class="form-control"
|
||||
name="email"
|
||||
id="email"
|
||||
required
|
||||
ng-class="{'is-invalid': conference.email.$invalid}"
|
||||
ng-model="options.billing.email"
|
||||
placeholder="Email"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- address -->
|
||||
<div class="form-group">
|
||||
<label for="inputAddress">Address</label>
|
||||
<input
|
||||
type="text"
|
||||
class="form-control"
|
||||
name="inputAddress"
|
||||
id="inputAddress"
|
||||
required
|
||||
placeholder="1234 Main St"
|
||||
ng-model="options.billing.address"
|
||||
ng-class="{'is-invalid': conference.inputAddress.$invalid}"
|
||||
/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<input
|
||||
type="text"
|
||||
class="form-control"
|
||||
name="inputAddress2"
|
||||
id="inputAddress2"
|
||||
ng-model="options.billing.address2"
|
||||
ng-class="{'is-invalid': conference.inputAddress2.$invalid}"
|
||||
placeholder="Apartment, studio, or floor"
|
||||
/>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<div class="form-group col-md-6">
|
||||
<input
|
||||
type="text"
|
||||
class="form-control"
|
||||
name="city"
|
||||
id="city"
|
||||
ng-model="options.billing.city"
|
||||
required
|
||||
ng-class="{'is-invalid': conference.city.$invalid}"
|
||||
placeholder="City"
|
||||
/>
|
||||
<div class="form-group">
|
||||
<label class="paper-field-label" for="billing_name">Name</label>
|
||||
<input type="text" class="form-control" name="billing_name" id="billing_name" required
|
||||
ng-class="{'is-invalid': conference.billing_name.$invalid}"
|
||||
ng-model="options.billing.name" placeholder="First & last name" />
|
||||
</div>
|
||||
<div class="form-group col-md-4">
|
||||
<input
|
||||
type="text"
|
||||
class="form-control"
|
||||
name="country"
|
||||
id="country"
|
||||
ng-model="options.billing.country"
|
||||
required
|
||||
ng-class="{'is-invalid': conference.country.$invalid}"
|
||||
placeholder="Country"
|
||||
/>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="paper-field-label" for="email">Email</label>
|
||||
<input type="email" class="form-control" name="email" id="email" required
|
||||
ng-class="{'is-invalid': conference.email.$invalid}"
|
||||
ng-model="options.billing.email" placeholder="you@example.org" />
|
||||
</div>
|
||||
<div class="form-group col-md-2">
|
||||
<input
|
||||
type="text"
|
||||
class="form-control"
|
||||
name="zip"
|
||||
id="zip"
|
||||
ng-model="options.billing.zip"
|
||||
required
|
||||
ng-class="{'is-invalid': conference.zip.$invalid}"
|
||||
placeholder="Zip"
|
||||
/>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="paper-field-label" for="inputAddress">Address</label>
|
||||
<input type="text" class="form-control" name="inputAddress" id="inputAddress" required
|
||||
placeholder="1234 Main St"
|
||||
ng-model="options.billing.address"
|
||||
ng-class="{'is-invalid': conference.inputAddress.$invalid}" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<input type="text" class="form-control" name="inputAddress2" id="inputAddress2"
|
||||
ng-model="options.billing.address2"
|
||||
ng-class="{'is-invalid': conference.inputAddress2.$invalid}"
|
||||
placeholder="Apartment, studio, or floor" />
|
||||
</div>
|
||||
|
||||
<div class="form-grid-3">
|
||||
<div class="form-group">
|
||||
<label class="paper-field-label" for="city">City</label>
|
||||
<input type="text" class="form-control" name="city" id="city"
|
||||
ng-model="options.billing.city" required
|
||||
ng-class="{'is-invalid': conference.city.$invalid}" placeholder="City" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="paper-field-label" for="country">Country</label>
|
||||
<input type="text" class="form-control" name="country" id="country"
|
||||
ng-model="options.billing.country" required
|
||||
ng-class="{'is-invalid': conference.country.$invalid}" placeholder="Country" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="paper-field-label" for="zip">Zip</label>
|
||||
<input type="text" class="form-control" name="zip" id="zip"
|
||||
ng-model="options.billing.zip" required
|
||||
ng-class="{'is-invalid': conference.zip.$invalid}" placeholder="Zip" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="paper-field-label" for="vat">VAT number</label>
|
||||
<input type="text" class="form-control" name="vat" id="vat"
|
||||
ng-class="{'is-invalid': conference.vat.$invalid}"
|
||||
ng-model="options.billing.vat" placeholder="VAT Number" />
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div class="paper-settings-footer">
|
||||
<div class="alert alert-danger" role="alert" ng-if="error" ng-bind="error"></div>
|
||||
<div class="alert alert-success" role="alert" ng-if="message" ng-bind="message"></div>
|
||||
<button id="send" type="submit" class="btn btn-ink" ng-click="submit($event)">
|
||||
{{editionMode ? "Update" : "Create"}} conference
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- VAT -->
|
||||
<div class="form-group">
|
||||
<label for="vat">VAT Number</label>
|
||||
<input
|
||||
type="text"
|
||||
class="form-control"
|
||||
name="vat"
|
||||
id="vat"
|
||||
ng-class="{'is-invalid': conference.vat.$invalid}"
|
||||
ng-model="options.billing.vat"
|
||||
placeholder="VAT Number"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="alert alert-danger"
|
||||
role="alert"
|
||||
ng-if="error"
|
||||
ng-bind="error"
|
||||
></div>
|
||||
<div
|
||||
class="alert alert-success"
|
||||
role="alert"
|
||||
ng-if="message"
|
||||
ng-bind="message"
|
||||
></div>
|
||||
|
||||
<button
|
||||
id="send"
|
||||
type="submit"
|
||||
class="btn btn-primary"
|
||||
ng-click="submit($event)"
|
||||
>
|
||||
{{editionMode ? "Update" : "Create"}}
|
||||
</button>
|
||||
</form>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -8,8 +8,8 @@
|
||||
</div>
|
||||
<div ng-if="type == 'audio'"><audio controls="controls"><source src="{{url}}" /></audio></div>
|
||||
<div ng-if="type == 'IPython'"><notebook file="url"></notebook></div>
|
||||
<div ng-if="type == 'error'" class="file-error container d-flex h-100"><h1 class="display-1 m-auto" translate="ERRORS.{{content}}">Error</h1></div></div>
|
||||
<div ng-if="type == 'loading' && !error" class="file-error container d-flex h-100"><h1 class="display-1 m-auto">Loading...</h1></div></div>
|
||||
<div ng-if="type == 'empty'" class="file-error container d-flex h-100"><h1 class="display-1 m-auto">Empty repository!</h1></div>
|
||||
<div ng-if="content == null && type != 'empty'" class="file-error container d-flex h-100"><h1 class="display-1 m-auto">Empty file!</h1></div>
|
||||
<div ng-if="type == 'binary'" class="file-error container d-flex h-100"><h1 class="display-1 m-auto">Unsupported binary file. You can download the file: <a target="_blank" ng-href="{{url}}&download=true">here</a>.</h1></div>
|
||||
<div ng-if="type == 'error'" class="file-error container d-flex h-100"><h1 class="paper-empty-title m-auto" translate="ERRORS.{{content}}">Error</h1></div>
|
||||
<div ng-if="type == 'loading' && !error" class="file-error container d-flex h-100"><h1 class="paper-empty-title m-auto">Loading…</h1></div>
|
||||
<div ng-if="type == 'empty'" class="file-error container d-flex h-100"><h1 class="paper-empty-title m-auto">Empty <em>repository</em>.</h1></div>
|
||||
<div ng-if="content == null && type != 'empty'" class="file-error container d-flex h-100"><h1 class="paper-empty-title m-auto">Empty <em>file</em>.</h1></div>
|
||||
<div ng-if="type == 'binary'" class="file-error container d-flex h-100"><h1 class="paper-empty-title m-auto">Unsupported <em>binary file</em>. You can <a target="_blank" ng-href="{{url}}&download=true">download it</a>.</h1></div>
|
||||
|
||||
@@ -1,22 +1,25 @@
|
||||
<div class="container page dashboard-page">
|
||||
<div class="container page dashboard-page paper-page">
|
||||
<div class="row">
|
||||
<div class="w-100 py-3">
|
||||
<!-- Header row: title + action buttons -->
|
||||
<div class="d-flex align-items-center justify-content-between mb-3">
|
||||
<h2 class="dashboard-title mb-0">Pull Requests</h2>
|
||||
<div class="w-100">
|
||||
<div class="paper-crumbs">My work / <span class="here">Pull requests</span></div>
|
||||
<div class="d-flex align-items-end justify-content-between flex-wrap" style="gap: 12px;">
|
||||
<div>
|
||||
<h1 class="paper-page-title">Anonymized <em>pull requests</em></h1>
|
||||
<p class="paper-page-lede">Track PR mirrors you’ve created and their reviewer traffic.</p>
|
||||
</div>
|
||||
<div class="d-flex flex-wrap" style="gap: 6px">
|
||||
<a href="/anonymize" class="btn btn-primary btn-sm">
|
||||
<i class="fa fa-plus-circle" aria-hidden="true"></i> Anonymize Repo
|
||||
<a href="/anonymize" class="btn btn-ink">
|
||||
<i class="fa fa-plus-circle mr-1" aria-hidden="true"></i> Anonymize Repo
|
||||
</a>
|
||||
<a href="/pull-request-anonymize" class="btn btn-primary btn-sm">
|
||||
<i class="fa fa-plus-circle" aria-hidden="true"></i> Anonymize PR
|
||||
<a href="/pull-request-anonymize" class="btn btn-ink">
|
||||
<i class="fa fa-plus-circle mr-1" aria-hidden="true"></i> Anonymize PR
|
||||
</a>
|
||||
<a
|
||||
title="Claim the ownership of an existing anonymized repository."
|
||||
data-toggle="tooltip"
|
||||
data-placement="bottom"
|
||||
href="/claim"
|
||||
class="btn btn-sm"
|
||||
class="btn"
|
||||
>
|
||||
Claim
|
||||
</a>
|
||||
|
||||
+161
-301
@@ -1,313 +1,173 @@
|
||||
<div class="container py-4">
|
||||
<h2>Quota</h2>
|
||||
<h3>Quota</h3>
|
||||
<h5>Repository</h5>
|
||||
<div class="progress">
|
||||
<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}}"
|
||||
>
|
||||
<span ng-show="quota"
|
||||
>{{quota.repository.used | number}}/{{quota.repository.total}}</span
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<h5>Storage</h5>
|
||||
<div class="progress">
|
||||
<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}}"
|
||||
>
|
||||
<span ng-show="quota"
|
||||
>{{quota.storage.used | humanFileSize}}/{{quota.storage.total|
|
||||
humanFileSize}}</span
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<h5>File</h5>
|
||||
<div class="progress">
|
||||
<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}}"
|
||||
>
|
||||
<span ng-show="quota"
|
||||
>{{quota.file.used | number}}/{{quota.file.total || "∞"}}</span
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<div class="container paper-page paper-settings">
|
||||
<div class="paper-crumbs">Reference / <span class="here">Settings</span></div>
|
||||
<h1 class="paper-page-title">Your <em>settings</em></h1>
|
||||
<p class="paper-page-lede">Review quotas and set defaults applied to every new anonymization.</p>
|
||||
|
||||
<h2>Default anonymization options</h2>
|
||||
<form class="form needs-validation" name="default" novalidate>
|
||||
<!-- Terms -->
|
||||
<div class="form-group">
|
||||
<label for="terms">Terms to anonymize</label>
|
||||
<textarea
|
||||
class="form-control"
|
||||
id="terms"
|
||||
name="terms"
|
||||
rows="3"
|
||||
ng-model="terms"
|
||||
ng-model-options="{ debounce: 250 }"
|
||||
></textarea>
|
||||
<small id="termsHelp" class="form-text text-muted"
|
||||
>One term per line. Each term will be replaced by XXX</small
|
||||
>
|
||||
<div class="invalid-feedback" ng-show="anonymize.terms.$error.format">
|
||||
Terms are in an invalid format
|
||||
</div>
|
||||
</div>
|
||||
<div class="accordion mb-3" id="options">
|
||||
<div class="card">
|
||||
<div class="card-header" id="headingOne">
|
||||
<h2 class="mb-0">
|
||||
<button
|
||||
class="btn btn-block text-left"
|
||||
type="button"
|
||||
data-toggle="collapse"
|
||||
data-target="#collapseOne"
|
||||
aria-expanded="true"
|
||||
aria-controls="collapseOne"
|
||||
>
|
||||
Rendering options
|
||||
</button>
|
||||
</h2>
|
||||
</div>
|
||||
<div class="paper-settings-body">
|
||||
<aside class="paper-settings-toc">
|
||||
<div class="paper-settings-toc-head">Contents</div>
|
||||
<nav>
|
||||
<a href="#settings-quota">Quota</a>
|
||||
<a href="#settings-terms">Terms</a>
|
||||
<a href="#settings-rendering">Rendering</a>
|
||||
<a href="#settings-features">Features</a>
|
||||
<a href="#settings-expiration">Expiration</a>
|
||||
</nav>
|
||||
</aside>
|
||||
|
||||
<div
|
||||
id="collapseOne"
|
||||
class="collapse show"
|
||||
aria-labelledby="headingOne"
|
||||
data-parent="#options"
|
||||
>
|
||||
<div class="card-body">
|
||||
<div class="form-group">
|
||||
<div class="form-check">
|
||||
<input
|
||||
class="form-check-input"
|
||||
type="checkbox"
|
||||
id="link"
|
||||
name="link"
|
||||
ng-model="options.link"
|
||||
/>
|
||||
<label class="form-check-label" for="link">Keep links</label>
|
||||
<small id="termsHelp" class="form-text text-muted"
|
||||
>Keep or remove all the links.</small
|
||||
>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input
|
||||
class="form-check-input"
|
||||
type="checkbox"
|
||||
id="image"
|
||||
name="image"
|
||||
ng-model="options.image"
|
||||
/>
|
||||
<label class="form-check-label" for="image"
|
||||
>Display images</label
|
||||
>
|
||||
<small id="termsHelp" class="form-text text-muted"
|
||||
>Images are not anonymized</small
|
||||
>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input
|
||||
class="form-check-input"
|
||||
type="checkbox"
|
||||
id="pdf"
|
||||
name="pdf"
|
||||
ng-model="options.pdf"
|
||||
/>
|
||||
<label class="form-check-label" for="pdf">Display PDFs</label>
|
||||
<small id="termsHelp" class="form-text text-muted"
|
||||
>PDF are not anonymized</small
|
||||
>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input
|
||||
class="form-check-input"
|
||||
type="checkbox"
|
||||
id="notebook"
|
||||
name="notebook"
|
||||
ng-model="options.notebook"
|
||||
/>
|
||||
<label class="form-check-label" for="notebook"
|
||||
>Display Notebooks</label
|
||||
>
|
||||
</div>
|
||||
<div class="paper-settings-main">
|
||||
<section id="settings-quota" class="paper-settings-section">
|
||||
<div class="paper-section-eyebrow">Quota</div>
|
||||
<div class="quota-row">
|
||||
<div class="quota-item">
|
||||
<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="{'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>
|
||||
</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="{'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>
|
||||
</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 || "∞"}}</span>
|
||||
</div>
|
||||
<div class="progress quota-progress">
|
||||
<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>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card">
|
||||
<div class="card-header" id="headingTwo">
|
||||
<h2 class="mb-0">
|
||||
<button
|
||||
class="btn btn-block text-left collapsed"
|
||||
type="button"
|
||||
data-toggle="collapse"
|
||||
data-target="#collapseTwo"
|
||||
aria-expanded="false"
|
||||
aria-controls="collapseTwo"
|
||||
>
|
||||
Features
|
||||
</button>
|
||||
</h2>
|
||||
</div>
|
||||
<div
|
||||
id="collapseTwo"
|
||||
class="collapse"
|
||||
aria-labelledby="headingTwo"
|
||||
data-parent="#options"
|
||||
>
|
||||
<div class="card-body">
|
||||
<div class="form-group">
|
||||
<div class="form-check">
|
||||
<input
|
||||
class="form-check-input"
|
||||
type="checkbox"
|
||||
id="page"
|
||||
name="page"
|
||||
ng-model="options.page"
|
||||
/>
|
||||
<label class="form-check-label" for="page">Github page</label>
|
||||
<small id="termsHelp" class="form-text text-muted"
|
||||
>Enable anonymized Github pages. It currently only supported
|
||||
for Github pages that are defined in the same branch.</small
|
||||
>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input
|
||||
class="form-check-input"
|
||||
type="checkbox"
|
||||
id="loc"
|
||||
name="loc"
|
||||
ng-model="options.loc"
|
||||
/>
|
||||
<label class="form-check-label" for="page">Line of code</label>
|
||||
<small id="termsHelp" class="form-text text-muted"
|
||||
>Display the number of line of code in the repository</small
|
||||
>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="form-check">
|
||||
<input
|
||||
class="form-check-input"
|
||||
type="checkbox"
|
||||
id="update"
|
||||
name="update"
|
||||
ng-model="options.update"
|
||||
/>
|
||||
<label class="form-check-label" for="update"
|
||||
>Auto update</label
|
||||
>
|
||||
<small id="termsHelp" class="form-text text-muted"
|
||||
>Automatically update the anonymized repository with the
|
||||
latest commit of the repository. The repository is updated
|
||||
once per hour maximum.</small
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="mode">Proxy mode</label>
|
||||
<select
|
||||
class="form-control"
|
||||
id="mode"
|
||||
name="mode"
|
||||
ng-model="options.mode"
|
||||
>
|
||||
<option value="GitHubStream" selected>Stream</option>
|
||||
<option value="GitHubDownload">Download</option>
|
||||
</select>
|
||||
<small class="form-text text-muted"
|
||||
>How the repository will be anonymized. Stream mode
|
||||
will request the content on the flight. This is the
|
||||
only option for repositories bigger than
|
||||
{{site_options.MAX_REPO_SIZE * 1024| humanFileSize}}.
|
||||
Download will download the repository the repository
|
||||
on the anonymous.4open.science server, it is faster
|
||||
and offer more features.</small
|
||||
>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<form class="form needs-validation" name="default" novalidate>
|
||||
<section id="settings-terms" class="paper-settings-section">
|
||||
<div class="paper-section-eyebrow">Default anonymization options</div>
|
||||
<div class="form-group">
|
||||
<label class="paper-field-label" for="terms">Terms to anonymize</label>
|
||||
<textarea
|
||||
class="form-control"
|
||||
id="terms"
|
||||
name="terms"
|
||||
rows="4"
|
||||
ng-model="terms"
|
||||
ng-model-options="{ debounce: 250 }"
|
||||
></textarea>
|
||||
<small id="termsHelp" class="form-text text-muted">One term per line. Each term will be replaced by XXX.</small>
|
||||
<div class="invalid-feedback" ng-show="anonymize.terms.$error.format">
|
||||
Terms are in an invalid format
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card">
|
||||
<div class="card-header" id="headingThree">
|
||||
<h2 class="mb-0">
|
||||
<button
|
||||
class="btn btn-block text-left collapsed"
|
||||
type="button"
|
||||
data-toggle="collapse"
|
||||
data-target="#collapseThree"
|
||||
aria-expanded="false"
|
||||
aria-controls="collapseThree"
|
||||
>
|
||||
Expiration
|
||||
</button>
|
||||
</h2>
|
||||
</div>
|
||||
<div
|
||||
id="collapseThree"
|
||||
class="collapse"
|
||||
aria-labelledby="headingThree"
|
||||
data-parent="#options"
|
||||
>
|
||||
<div class="card-body">
|
||||
<div class="form-group">
|
||||
<label for="expiration">Expiration options</label>
|
||||
<select
|
||||
class="form-control"
|
||||
id="expiration"
|
||||
name="expiration"
|
||||
ng-model="options.expirationMode"
|
||||
>
|
||||
<option value="never" selected>Never expire</option>
|
||||
<option value="redirect">Redirect to GitHub</option>
|
||||
<option value="remove">Remove anonymized repository</option>
|
||||
</select>
|
||||
<small class="form-text text-muted"
|
||||
>Define the expiration strategy for the repository.</small
|
||||
>
|
||||
</section>
|
||||
|
||||
<section id="settings-rendering" class="paper-settings-section">
|
||||
<div class="paper-section-eyebrow">Rendering</div>
|
||||
<div class="form-group">
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" id="link" name="link" ng-model="options.link" />
|
||||
<label class="form-check-label" for="link">Keep links</label>
|
||||
<small class="form-text text-muted">Keep or remove all the links.</small>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" id="image" name="image" ng-model="options.image" />
|
||||
<label class="form-check-label" for="image">Display images</label>
|
||||
<small class="form-text text-muted">Images are not anonymized.</small>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" id="pdf" name="pdf" ng-model="options.pdf" />
|
||||
<label class="form-check-label" for="pdf">Display PDFs</label>
|
||||
<small class="form-text text-muted">PDFs are not anonymized.</small>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" id="notebook" name="notebook" ng-model="options.notebook" />
|
||||
<label class="form-check-label" for="notebook">Display Notebooks</label>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="settings-features" class="paper-settings-section">
|
||||
<div class="paper-section-eyebrow">Features</div>
|
||||
<div class="form-group">
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" id="page" name="page" ng-model="options.page" />
|
||||
<label class="form-check-label" for="page">Github page</label>
|
||||
<small class="form-text text-muted">Enable anonymized GitHub pages. Currently only supported for pages defined in the same branch.</small>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" id="loc" name="loc" ng-model="options.loc" />
|
||||
<label class="form-check-label" for="loc">Line of code</label>
|
||||
<small class="form-text text-muted">Display the number of lines of code in the repository.</small>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" id="update" name="update" ng-model="options.update" />
|
||||
<label class="form-check-label" for="update">Auto update</label>
|
||||
<small class="form-text text-muted">Automatically update the anonymized repository with the latest commit (once per hour maximum).</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="paper-field-label" for="mode">Proxy mode</label>
|
||||
<select class="form-control" id="mode" name="mode" ng-model="options.mode">
|
||||
<option value="GitHubStream" selected>Stream</option>
|
||||
<option value="GitHubDownload">Download</option>
|
||||
</select>
|
||||
<small class="form-text text-muted">Stream mode requests content on the fly. This is the only option for repositories bigger than {{site_options.MAX_REPO_SIZE * 1024| humanFileSize}}. Download fetches to the server — faster and with more features.</small>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="settings-expiration" class="paper-settings-section">
|
||||
<div class="paper-section-eyebrow">Expiration</div>
|
||||
<div class="form-group">
|
||||
<label class="paper-field-label" for="expiration">Expiration strategy</label>
|
||||
<select class="form-control" id="expiration" name="expiration" ng-model="options.expirationMode">
|
||||
<option value="never" selected>Never expire</option>
|
||||
<option value="redirect">Redirect to GitHub</option>
|
||||
<option value="remove">Remove anonymized repository</option>
|
||||
</select>
|
||||
<small class="form-text text-muted">Define the expiration strategy for new repositories.</small>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div class="paper-settings-footer">
|
||||
<div class="alert alert-danger" role="alert" ng-if="error" ng-bind="error"></div>
|
||||
<div class="alert alert-success" role="alert" ng-if="message" ng-bind="message"></div>
|
||||
<button id="save" type="submit" class="btn btn-ink" ng-click="saveDefault($event)">
|
||||
Save defaults
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div
|
||||
class="alert alert-danger"
|
||||
role="alert"
|
||||
ng-if="error"
|
||||
ng-bind="error"
|
||||
></div>
|
||||
<div
|
||||
class="alert alert-success"
|
||||
role="alert"
|
||||
ng-if="message"
|
||||
ng-bind="message"
|
||||
></div>
|
||||
<button
|
||||
id="save"
|
||||
type="submit"
|
||||
class="btn btn-primary"
|
||||
ng-click="saveDefault($event)"
|
||||
>
|
||||
Save
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -6,25 +6,20 @@
|
||||
Anonymization Date: {{details.anonymizeDate|date}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="overflow-auto mx-3">
|
||||
<div class="d-flex w-100 justify-content-between align-items-center">
|
||||
<h2 class="pr-title">
|
||||
<div class="overflow-auto paper-page" style="padding-top: 18px;">
|
||||
<div class="paper-crumbs">Reviewer / <span class="here">Pull request</span></div>
|
||||
<div class="d-flex w-100 justify-content-between align-items-end flex-wrap" style="gap: 12px;">
|
||||
<h1 class="paper-page-title pr-title" style="margin: 6px 0;">
|
||||
<span ng-if="details.title">{{details.title}}</span>
|
||||
<span
|
||||
class="badge"
|
||||
ng-class="{'badge-success':details.merged, 'badge-warning':details.state=='open', 'badge-danger':details.state=='closed' &&!details.merged}"
|
||||
>
|
||||
<span class="paper-pill" ng-class="{'good':details.merged, 'warn':details.state=='open', 'bad':details.state=='closed' && !details.merged}">
|
||||
{{details.merged?"merged":details.state | title}}
|
||||
</span>
|
||||
</h2>
|
||||
<small
|
||||
ng-if="details.updatedDate"
|
||||
ng-bind="details.updatedDate | date"
|
||||
></small>
|
||||
</h1>
|
||||
<small class="paper-pill" ng-if="details.updatedDate" ng-bind="details.updatedDate | date"></small>
|
||||
</div>
|
||||
<div class="paper-meta-rule" ng-if="details.baseRepositoryFullName">
|
||||
<span>on <b>{{details.baseRepositoryFullName}}</b></span>
|
||||
</div>
|
||||
<small ng-if="details.baseRepositoryFullName"
|
||||
>Pull Request on {{details.baseRepositoryFullName}}</small
|
||||
>
|
||||
<div
|
||||
class="pr-body shadow-sm p-3 mb-4 rounded border"
|
||||
ng-if="details.body"
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
<div class="container py-4">
|
||||
<h2>Status of <strong ng-bind="repoId"></strong></h2>
|
||||
<div class="container paper-page">
|
||||
<div class="paper-crumbs">Anonymization / <span class="here">Status</span></div>
|
||||
<h1 class="paper-page-title">Status of <em>{{repoId}}</em></h1>
|
||||
<p class="paper-page-lede">Track progress as your anonymization is prepared.</p>
|
||||
<div class="paper-meta-rule"></div>
|
||||
|
||||
<section class="py-4">
|
||||
<h2>Progress</h2>
|
||||
<div class="paper-section-eyebrow">Progress</div>
|
||||
|
||||
<p>
|
||||
The current status of your repository. The repository will take few
|
||||
@@ -39,7 +42,7 @@
|
||||
|
||||
<p class="text-center">
|
||||
<a
|
||||
class="btn btn-primary"
|
||||
class="btn btn-ink"
|
||||
href="/r/{{repoId}}/"
|
||||
target="__self"
|
||||
ng-if="repo.status == 'ready'"
|
||||
@@ -56,7 +59,7 @@
|
||||
</section>
|
||||
|
||||
<section class="py-4">
|
||||
<h2 class="md-1">Support Anonymous GitHub</h2>
|
||||
<div class="paper-section-eyebrow">Support Anonymous GitHub</div>
|
||||
|
||||
<iframe
|
||||
id="kofiframe"
|
||||
|
||||
+29
-20
@@ -726,10 +726,19 @@ angular
|
||||
}
|
||||
getQuota();
|
||||
|
||||
let loadedRepos = null;
|
||||
let loadedPRs = null;
|
||||
|
||||
function mergeItems() {
|
||||
$scope.items = (loadedRepos || []).concat(loadedPRs || []);
|
||||
}
|
||||
|
||||
function loadAll() {
|
||||
loadedRepos = null;
|
||||
loadedPRs = null;
|
||||
$http.get("/api/user/anonymized_repositories").then(
|
||||
(res) => {
|
||||
const repos = res.data.map((repo) => {
|
||||
loadedRepos = res.data.map((repo) => {
|
||||
if (!repo.pageView) repo.pageView = 0;
|
||||
if (!repo.lastView) repo.lastView = "";
|
||||
repo.options.terms = repo.options.terms.filter((f) => f);
|
||||
@@ -741,25 +750,25 @@ angular
|
||||
repo._viewUrl = "/r/" + repo.repoId + "/";
|
||||
return repo;
|
||||
});
|
||||
$http.get("/api/user/anonymized_pull_requests").then(
|
||||
(res2) => {
|
||||
const prs = res2.data.map((pr) => {
|
||||
if (!pr.pageView) pr.pageView = 0;
|
||||
if (!pr.lastView) pr.lastView = "";
|
||||
pr.options.terms = pr.options.terms.filter((f) => f);
|
||||
pr._type = "pr";
|
||||
pr._id = pr.pullRequestId;
|
||||
pr._name = pr.pullRequestId;
|
||||
pr._source = pr.source.repositoryFullName + "#" + pr.source.pullRequestId;
|
||||
pr._editUrl = "/pull-request-anonymize/" + pr.pullRequestId;
|
||||
pr._viewUrl = "/pr/" + pr.pullRequestId + "/";
|
||||
pr.anonymizeDate = pr.anonymizeDate;
|
||||
return pr;
|
||||
});
|
||||
$scope.items = repos.concat(prs);
|
||||
},
|
||||
(err) => { console.error(err); }
|
||||
);
|
||||
mergeItems();
|
||||
},
|
||||
(err) => { console.error(err); }
|
||||
);
|
||||
$http.get("/api/user/anonymized_pull_requests").then(
|
||||
(res2) => {
|
||||
loadedPRs = res2.data.map((pr) => {
|
||||
if (!pr.pageView) pr.pageView = 0;
|
||||
if (!pr.lastView) pr.lastView = "";
|
||||
pr.options.terms = pr.options.terms.filter((f) => f);
|
||||
pr._type = "pr";
|
||||
pr._id = pr.pullRequestId;
|
||||
pr._name = pr.pullRequestId;
|
||||
pr._source = pr.source.repositoryFullName + "#" + pr.source.pullRequestId;
|
||||
pr._editUrl = "/pull-request-anonymize/" + pr.pullRequestId;
|
||||
pr._viewUrl = "/pr/" + pr.pullRequestId + "/";
|
||||
return pr;
|
||||
});
|
||||
mergeItems();
|
||||
},
|
||||
(err) => { console.error(err); }
|
||||
);
|
||||
|
||||
Vendored
+1
-1
File diff suppressed because one or more lines are too long
@@ -4,6 +4,8 @@ import { ensureAuthenticated } from "./connection";
|
||||
import { handleError, getUser, isOwnerOrAdmin } from "./route-utils";
|
||||
import UserModel from "../../core/model/users/users.model";
|
||||
import User from "../../core/User";
|
||||
import FileModel from "../../core/model/files/files.model";
|
||||
import { isConnected } from "../database";
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
@@ -40,22 +42,63 @@ router.get("/quota", async (req: express.Request, res: express.Response) => {
|
||||
try {
|
||||
const user = await getUser(req);
|
||||
const repositories = await user.getRepositories();
|
||||
const sizes = await Promise.all(
|
||||
repositories
|
||||
.filter((r) => r.status == "ready")
|
||||
.map((r) => r.computeSize())
|
||||
);
|
||||
const ready = repositories.filter((r) => r.status == "ready");
|
||||
|
||||
let totalStorage = 0;
|
||||
let totalFiles = 0;
|
||||
const uncachedIds: string[] = [];
|
||||
for (const r of ready) {
|
||||
const cached = r.model.size;
|
||||
if (cached && cached.file) {
|
||||
totalStorage += cached.storage;
|
||||
totalFiles += cached.file;
|
||||
} else {
|
||||
uncachedIds.push(r.repoId);
|
||||
}
|
||||
}
|
||||
|
||||
if (uncachedIds.length) {
|
||||
const agg = await FileModel.aggregate([
|
||||
{ $match: { repoId: { $in: uncachedIds } } },
|
||||
{
|
||||
$group: {
|
||||
_id: "$repoId",
|
||||
storage: { $sum: "$size" },
|
||||
file: { $sum: 1 },
|
||||
},
|
||||
},
|
||||
]);
|
||||
const byId = new Map<string, { storage: number; file: number }>();
|
||||
for (const row of agg) {
|
||||
byId.set(row._id, { storage: row.storage || 0, file: row.file || 0 });
|
||||
}
|
||||
for (const r of ready) {
|
||||
if (!uncachedIds.includes(r.repoId)) continue;
|
||||
const size = byId.get(r.repoId) || { storage: 0, file: 0 };
|
||||
totalStorage += size.storage;
|
||||
totalFiles += size.file;
|
||||
r.model.size = size;
|
||||
}
|
||||
if (isConnected) {
|
||||
await Promise.all(
|
||||
ready
|
||||
.filter((r) => uncachedIds.includes(r.repoId))
|
||||
.map((r) => r.model.save())
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
res.json({
|
||||
storage: {
|
||||
used: sizes.reduce((sum, i) => sum + i.storage, 0),
|
||||
used: totalStorage,
|
||||
total: config.DEFAULT_QUOTA,
|
||||
},
|
||||
file: {
|
||||
used: sizes.reduce((sum, i) => sum + i.file, 0),
|
||||
used: totalFiles,
|
||||
total: 0,
|
||||
},
|
||||
repository: {
|
||||
used: repositories.filter((f) => f.status == "ready").length,
|
||||
used: ready.length,
|
||||
total: 20,
|
||||
},
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user