Merge branch 'dev' into link-removal2

This commit is contained in:
Nick Sweeting 2021-01-30 05:51:39 -05:00
commit b7273a07e5
39 changed files with 273 additions and 276 deletions

View file

@ -76,7 +76,6 @@ CONFIG_SCHEMA: Dict[str, ConfigDefaultDict] = {
'PUBLIC_SNAPSHOTS': {'type': bool, 'default': True}, 'PUBLIC_SNAPSHOTS': {'type': bool, 'default': True},
'PUBLIC_ADD_VIEW': {'type': bool, 'default': False}, 'PUBLIC_ADD_VIEW': {'type': bool, 'default': False},
'FOOTER_INFO': {'type': str, 'default': 'Content is hosted for personal archiving purposes only. Contact server owner for any takedown requests.'}, 'FOOTER_INFO': {'type': str, 'default': 'Content is hosted for personal archiving purposes only. Contact server owner for any takedown requests.'},
'ACTIVE_THEME': {'type': str, 'default': 'default'},
}, },
'ARCHIVE_METHOD_TOGGLES': { 'ARCHIVE_METHOD_TOGGLES': {
@ -204,12 +203,11 @@ def get_real_name(key: str) -> str:
################################ Constants ##################################### ################################ Constants #####################################
PACKAGE_DIR_NAME = 'archivebox' PACKAGE_DIR_NAME = 'archivebox'
TEMPLATES_DIR_NAME = 'themes' TEMPLATES_DIR_NAME = 'templates'
ARCHIVE_DIR_NAME = 'archive' ARCHIVE_DIR_NAME = 'archive'
SOURCES_DIR_NAME = 'sources' SOURCES_DIR_NAME = 'sources'
LOGS_DIR_NAME = 'logs' LOGS_DIR_NAME = 'logs'
STATIC_DIR_NAME = 'static'
SQL_INDEX_FILENAME = 'index.sqlite3' SQL_INDEX_FILENAME = 'index.sqlite3'
JSON_INDEX_FILENAME = 'index.json' JSON_INDEX_FILENAME = 'index.json'
HTML_INDEX_FILENAME = 'index.html' HTML_INDEX_FILENAME = 'index.html'
@ -702,7 +700,7 @@ def get_code_locations(config: ConfigDict) -> SimpleConfigValueDict:
'TEMPLATES_DIR': { 'TEMPLATES_DIR': {
'path': (config['TEMPLATES_DIR']).resolve(), 'path': (config['TEMPLATES_DIR']).resolve(),
'enabled': True, 'enabled': True,
'is_valid': (config['TEMPLATES_DIR'] / config['ACTIVE_THEME'] / 'static').exists(), 'is_valid': (config['TEMPLATES_DIR'] / 'static').exists(),
}, },
# 'NODE_MODULES_DIR': { # 'NODE_MODULES_DIR': {
# 'path': , # 'path': ,

View file

@ -50,7 +50,6 @@ class ConfigDict(BaseConfig, total=False):
PUBLIC_INDEX: bool PUBLIC_INDEX: bool
PUBLIC_SNAPSHOTS: bool PUBLIC_SNAPSHOTS: bool
FOOTER_INFO: str FOOTER_INFO: str
ACTIVE_THEME: str
SAVE_TITLE: bool SAVE_TITLE: bool
SAVE_FAVICON: bool SAVE_FAVICON: bool

View file

@ -24,6 +24,16 @@ from main import add, remove
from config import OUTPUT_DIR from config import OUTPUT_DIR
from extractors import archive_snapshots from extractors import archive_snapshots
# Admin URLs
# /admin/
# /admin/login/
# /admin/core/
# /admin/core/snapshot/
# /admin/core/snapshot/:uuid/
# /admin/core/tag/
# /admin/core/tag/:uuid/
# TODO: https://stackoverflow.com/questions/40760880/add-custom-button-to-django-admin-panel # TODO: https://stackoverflow.com/questions/40760880/add-custom-button-to-django-admin-panel
def update_snapshots(modeladmin, request, queryset): def update_snapshots(modeladmin, request, queryset):
@ -171,7 +181,7 @@ class SnapshotAdmin(SearchResultsAdminMixin, admin.ModelAdmin):
saved_list_max_show_all = self.list_max_show_all saved_list_max_show_all = self.list_max_show_all
# Monkey patch here plus core_tags.py # Monkey patch here plus core_tags.py
self.change_list_template = 'admin/grid_change_list.html' self.change_list_template = 'private_index_grid.html'
self.list_per_page = 20 self.list_per_page = 20
self.list_max_show_all = self.list_per_page self.list_max_show_all = self.list_per_page
@ -249,7 +259,7 @@ class ArchiveBoxAdmin(admin.AdminSite):
else: else:
context["form"] = form context["form"] = form
return render(template_name='add_links.html', request=request, context=context) return render(template_name='add.html', request=request, context=context)
admin.site = ArchiveBoxAdmin() admin.site = ArchiveBoxAdmin()
admin.site.register(get_user_model()) admin.site.register(get_user_model())

View file

@ -11,7 +11,6 @@ from ..config import (
SECRET_KEY, SECRET_KEY,
ALLOWED_HOSTS, ALLOWED_HOSTS,
PACKAGE_DIR, PACKAGE_DIR,
ACTIVE_THEME,
TEMPLATES_DIR_NAME, TEMPLATES_DIR_NAME,
SQL_INDEX_FILENAME, SQL_INDEX_FILENAME,
OUTPUT_DIR, OUTPUT_DIR,
@ -69,13 +68,12 @@ AUTHENTICATION_BACKENDS = [
STATIC_URL = '/static/' STATIC_URL = '/static/'
STATICFILES_DIRS = [ STATICFILES_DIRS = [
str(Path(PACKAGE_DIR) / TEMPLATES_DIR_NAME / ACTIVE_THEME / 'static'), str(Path(PACKAGE_DIR) / TEMPLATES_DIR_NAME / 'static'),
str(Path(PACKAGE_DIR) / TEMPLATES_DIR_NAME / 'default' / 'static'),
] ]
TEMPLATE_DIRS = [ TEMPLATE_DIRS = [
str(Path(PACKAGE_DIR) / TEMPLATES_DIR_NAME / ACTIVE_THEME), str(Path(PACKAGE_DIR) / TEMPLATES_DIR_NAME / 'core'),
str(Path(PACKAGE_DIR) / TEMPLATES_DIR_NAME / 'default'), str(Path(PACKAGE_DIR) / TEMPLATES_DIR_NAME / 'admin'),
str(Path(PACKAGE_DIR) / TEMPLATES_DIR_NAME), str(Path(PACKAGE_DIR) / TEMPLATES_DIR_NAME),
] ]

View file

@ -14,7 +14,7 @@ register = template.Library()
def snapshot_image(snapshot): def snapshot_image(snapshot):
result = ArchiveResult.objects.filter(snapshot=snapshot, extractor='screenshot', status='succeeded').first() result = ArchiveResult.objects.filter(snapshot=snapshot, extractor='screenshot', status='succeeded').first()
if result: if result:
return reverse('LinkAssets', args=[f'{str(snapshot.timestamp)}/{result.output}']) return reverse('Snapshot', args=[f'{str(snapshot.timestamp)}/{result.output}'])
return static('archive.png') return static('archive.png')

View file

@ -5,22 +5,24 @@ from django.views import static
from django.conf import settings from django.conf import settings
from django.views.generic.base import RedirectView from django.views.generic.base import RedirectView
from core.views import MainIndex, LinkDetails, PublicArchiveView, AddView from core.views import HomepageView, SnapshotView, PublicIndexView, AddView
# print('DEBUG', settings.DEBUG) # print('DEBUG', settings.DEBUG)
urlpatterns = [ urlpatterns = [
path('public/', PublicIndexView.as_view(), name='public-index'),
path('robots.txt', static.serve, {'document_root': settings.OUTPUT_DIR, 'path': 'robots.txt'}), path('robots.txt', static.serve, {'document_root': settings.OUTPUT_DIR, 'path': 'robots.txt'}),
path('favicon.ico', static.serve, {'document_root': settings.OUTPUT_DIR, 'path': 'favicon.ico'}), path('favicon.ico', static.serve, {'document_root': settings.OUTPUT_DIR, 'path': 'favicon.ico'}),
path('docs/', RedirectView.as_view(url='https://github.com/ArchiveBox/ArchiveBox/wiki'), name='Docs'), path('docs/', RedirectView.as_view(url='https://github.com/ArchiveBox/ArchiveBox/wiki'), name='Docs'),
path('archive/', RedirectView.as_view(url='/')), path('archive/', RedirectView.as_view(url='/')),
path('archive/<path:path>', LinkDetails.as_view(), name='LinkAssets'), path('archive/<path:path>', SnapshotView.as_view(), name='Snapshot'),
path('admin/core/snapshot/add/', RedirectView.as_view(url='/add/')), path('admin/core/snapshot/add/', RedirectView.as_view(url='/add/')),
path('add/', AddView.as_view()), path('add/', AddView.as_view(), name='add'),
path('accounts/login/', RedirectView.as_view(url='/admin/login/')), path('accounts/login/', RedirectView.as_view(url='/admin/login/')),
path('accounts/logout/', RedirectView.as_view(url='/admin/logout/')), path('accounts/logout/', RedirectView.as_view(url='/admin/logout/')),
@ -31,6 +33,37 @@ urlpatterns = [
path('index.html', RedirectView.as_view(url='/')), path('index.html', RedirectView.as_view(url='/')),
path('index.json', static.serve, {'document_root': settings.OUTPUT_DIR, 'path': 'index.json'}), path('index.json', static.serve, {'document_root': settings.OUTPUT_DIR, 'path': 'index.json'}),
path('', MainIndex.as_view(), name='Home'), path('', HomepageView.as_view(), name='Home'),
path('public/', PublicArchiveView.as_view(), name='public-index'),
] ]
# # Proposed UI URLs spec
# path('', HomepageView)
# path('/add', AddView)
# path('/public', PublicIndexView)
# path('/snapshot/:slug', SnapshotView)
# path('/admin', admin.site.urls)
# path('/accounts', django.contrib.auth.urls)
# # Prposed REST API spec
# # :slugs can be uuid, short_uuid, or any of the unique index_fields
# path('api/v1/'),
# path('api/v1/core/' [GET])
# path('api/v1/core/snapshot/', [GET, POST, PUT]),
# path('api/v1/core/snapshot/:slug', [GET, PATCH, DELETE]),
# path('api/v1/core/archiveresult', [GET, POST, PUT]),
# path('api/v1/core/archiveresult/:slug', [GET, PATCH, DELETE]),
# path('api/v1/core/tag/', [GET, POST, PUT]),
# path('api/v1/core/tag/:slug', [GET, PATCH, DELETE]),
# path('api/v1/cli/', [GET])
# path('api/v1/cli/{add,list,config,...}', [POST]), # pass query as kwargs directly to `run_subcommand` and return stdout, stderr, exitcode
# path('api/v1/extractors/', [GET])
# path('api/v1/extractors/:extractor/', [GET]),
# path('api/v1/extractors/:extractor/:func', [GET, POST]), # pass query as args directly to chosen function
# future, just an idea:
# path('api/v1/scheduler/', [GET])
# path('api/v1/scheduler/task/', [GET, POST, PUT]),
# path('api/v1/scheduler/task/:slug', [GET, PATCH, DELETE]),

View file

@ -28,20 +28,19 @@ from ..util import base_url, ansi_to_html
from ..index.html import snapshot_icons from ..index.html import snapshot_icons
class MainIndex(View): class HomepageView(View):
template = 'main_index.html'
def get(self, request): def get(self, request):
if request.user.is_authenticated: if request.user.is_authenticated:
return redirect('/admin/core/snapshot/') return redirect('/admin/core/snapshot/')
if PUBLIC_INDEX: if PUBLIC_INDEX:
return redirect('public-index') return redirect('/public')
return redirect(f'/admin/login/?next={request.path}') return redirect(f'/admin/login/?next={request.path}')
class LinkDetails(View): class SnapshotView(View):
# render static html index from filesystem archive/<timestamp>/index.html
def get(self, request, path): def get(self, request, path):
# missing trailing slash -> redirect to index # missing trailing slash -> redirect to index
@ -92,8 +91,8 @@ class LinkDetails(View):
status=404, status=404,
) )
class PublicArchiveView(ListView): class PublicIndexView(ListView):
template = 'snapshot_list.html' template_name = 'public_index.html'
model = Snapshot model = Snapshot
paginate_by = 100 paginate_by = 100
ordering = ['title'] ordering = ['title']
@ -123,7 +122,7 @@ class PublicArchiveView(ListView):
class AddView(UserPassesTestMixin, FormView): class AddView(UserPassesTestMixin, FormView):
template_name = "add_links.html" template_name = "add.html"
form_class = AddLinkForm form_class = AddLinkForm
def get_initial(self): def get_initial(self):

View file

@ -26,9 +26,9 @@ from ..config import (
HTML_INDEX_FILENAME, HTML_INDEX_FILENAME,
) )
MAIN_INDEX_TEMPLATE = 'main_index.html' MAIN_INDEX_TEMPLATE = 'static_index.html'
MINIMAL_INDEX_TEMPLATE = 'main_index_minimal.html' MINIMAL_INDEX_TEMPLATE = 'minimal_index.html'
LINK_DETAILS_TEMPLATE = 'link_details.html' LINK_DETAILS_TEMPLATE = 'snapshot.html'
TITLE_LOADING_MSG = 'Not yet archived...' TITLE_LOADING_MSG = 'Not yet archived...'
@ -144,7 +144,13 @@ def snapshot_icons(snapshot) -> str:
for extractor, _ in EXTRACTORS: for extractor, _ in EXTRACTORS:
if extractor not in exclude: if extractor not in exclude:
exists = extractor_items[extractor] is not None exists = False
if extractor_items[extractor] is not None:
outpath = (Path(path) / canon[f"{extractor}_path"])
if outpath.is_dir():
exists = any(outpath.glob('*.*'))
elif outpath.is_file():
exists = outpath.stat().st_size > 100
output += format_html(output_template, path, canon[f"{extractor}_path"], str(exists), output += format_html(output_template, path, canon[f"{extractor}_path"], str(exists),
extractor, icons.get(extractor, "?")) extractor, icons.get(extractor, "?"))
if extractor == "wget": if extractor == "wget":

View file

@ -409,4 +409,41 @@ class Link:
def canonical_outputs(self) -> Dict[str, Optional[str]]:
"""predict the expected output paths that should be present after archiving"""
from ..extractors.wget import wget_output_path
canonical = {
'index_path': 'index.html',
'favicon_path': 'favicon.ico',
'google_favicon_path': 'https://www.google.com/s2/favicons?domain={}'.format(self.domain),
'wget_path': wget_output_path(self),
'warc_path': 'warc/',
'singlefile_path': 'singlefile.html',
'readability_path': 'readability/content.html',
'mercury_path': 'mercury/content.html',
'pdf_path': 'output.pdf',
'screenshot_path': 'screenshot.png',
'dom_path': 'output.html',
'archive_org_path': 'https://web.archive.org/web/{}'.format(self.base_url),
'git_path': 'git/',
'media_path': 'media/',
}
if self.is_static:
# static binary files like PDF and images are handled slightly differently.
# they're just downloaded once and aren't archived separately multiple times,
# so the wget, screenshot, & pdf urls should all point to the same file
static_path = wget_output_path(self)
canonical.update({
'title': self.basename,
'wget_path': static_path,
'pdf_path': static_path,
'screenshot_path': static_path,
'dom_path': static_path,
'singlefile_path': static_path,
'readability_path': static_path,
'mercury_path': static_path,
})
return canonical

View file

@ -79,7 +79,6 @@ from .config import (
ARCHIVE_DIR_NAME, ARCHIVE_DIR_NAME,
SOURCES_DIR_NAME, SOURCES_DIR_NAME,
LOGS_DIR_NAME, LOGS_DIR_NAME,
STATIC_DIR_NAME,
JSON_INDEX_FILENAME, JSON_INDEX_FILENAME,
HTML_INDEX_FILENAME, HTML_INDEX_FILENAME,
SQL_INDEX_FILENAME, SQL_INDEX_FILENAME,
@ -125,10 +124,10 @@ ALLOWED_IN_OUTPUT_DIR = {
'.virtualenv', '.virtualenv',
'node_modules', 'node_modules',
'package-lock.json', 'package-lock.json',
'static',
ARCHIVE_DIR_NAME, ARCHIVE_DIR_NAME,
SOURCES_DIR_NAME, SOURCES_DIR_NAME,
LOGS_DIR_NAME, LOGS_DIR_NAME,
STATIC_DIR_NAME,
SQL_INDEX_FILENAME, SQL_INDEX_FILENAME,
JSON_INDEX_FILENAME, JSON_INDEX_FILENAME,
HTML_INDEX_FILENAME, HTML_INDEX_FILENAME,

View file

@ -0,0 +1,150 @@
{% extends "base.html" %}
{% load static %}
{% block body %}
<div id="toolbar">
<form id="changelist-search" action="{% url 'public-index' %}" method="get">
<div>
<label for="searchbar"><img src="/static/admin/img/search.svg" alt="Search"></label>
<input type="text" size="40" name="q" value="" id="searchbar" autofocus placeholder="Title, URL, tags, timestamp, or content...".>
<input type="submit" value="Search" style="height: 36px; padding-top: 6px; margin: 8px"/>
<input type="button"
value="♺"
title="Refresh..."
onclick="location.href='{% url 'public-index' %}'"
style="background-color: rgba(121, 174, 200, 0.8); height: 30px; font-size: 0.8em; margin-top: 12px; padding-top: 6px; float:right">
</input>
</div>
</form>
</div>
<table id="table-bookmarks">
<thead>
<tr>
<th style="width: 100px;">Bookmarked</th>
<th style="width: 26vw;">Snapshot ({{object_list|length}})</th>
<th style="width: 140px">Files</th>
<th style="width: 16vw;whitespace:nowrap;overflow-x:hidden;">Original URL</th>
</tr>
</thead>
<tbody>
{% for link in object_list %}
{% include 'main_index_row.html' with link=link %}
{% endfor %}
</tbody>
</table>
<center>
<span class="step-links">
{% if page_obj.has_previous %}
<a href="{% url 'public-index' %}?page=1">&laquo; first</a>
<a href="{% url 'public-index' %}?page={{ page_obj.previous_page_number }}">previous</a>
{% endif %}
<span class="current">
Page {{ page_obj.number }} of {{ page_obj.paginator.num_pages }}.
</span>
{% if page_obj.has_next %}
<a href="{% url 'public-index' %}?page={{ page_obj.next_page_number }}">next </a>
<a href="{% url 'public-index' %}?page={{ page_obj.paginator.num_pages }}">last &raquo;</a>
{% endif %}
</span>
{% if page_obj.has_next %}
<a href="{% url 'public-index' %}?page={{ page_obj.next_page_number }}">next </a>
<a href="{% url 'public-index' %}?page={{ page_obj.paginator.num_pages }}">last &raquo;</a>
{% endif %}
</span>
<br>
</center>
{% endblock %}
{% extends "admin/base_site.html" %}
{% load i18n admin_urls static admin_list %}
{% load core_tags %}
{% block extrastyle %}
{{ block.super }}
<link rel="stylesheet" type="text/css" href="{% static "admin/css/changelists.css" %}">
{% if cl.formset %}
<link rel="stylesheet" type="text/css" href="{% static "admin/css/forms.css" %}">
{% endif %}
{% if cl.formset or action_form %}
<script src="{% url 'admin:jsi18n' %}"></script>
{% endif %}
{{ media.css }}
{% if not actions_on_top and not actions_on_bottom %}
<style>
#changelist table thead th:first-child {width: inherit}
</style>
{% endif %}
{% endblock %}
{% block extrahead %}
{{ block.super }}
{{ media.js }}
{% endblock %}
{% block bodyclass %}{{ block.super }} app-{{ opts.app_label }} model-{{ opts.model_name }} change-list{% endblock %}
{% if not is_popup %}
{% block breadcrumbs %}
<div class="breadcrumbs">
<a href="{% url 'admin:index' %}">{% translate 'Home' %}</a>
&rsaquo; <a href="{% url 'admin:app_list' app_label=cl.opts.app_label %}">{{ cl.opts.app_config.verbose_name }}</a>
&rsaquo; {{ cl.opts.verbose_name_plural|capfirst }}
</div>
{% endblock %}
{% endif %}
{% block coltype %}{% endblock %}
{% block content %}
<div id="content-main">
{% block object-tools %}
<ul class="object-tools">
{% block object-tools-items %}
{% change_list_object_tools %}
{% endblock %}
</ul>
{% endblock %}
{% if cl.formset and cl.formset.errors %}
<p class="errornote">
{% if cl.formset.total_error_count == 1 %}{% translate "Please correct the error below." %}{% else %}{% translate "Please correct the errors below." %}{% endif %}
</p>
{{ cl.formset.non_form_errors }}
{% endif %}
<div class="module{% if cl.has_filters %} filtered{% endif %}" id="changelist">
<div class="changelist-form-container">
{% block search %}{% search_form cl %}{% endblock %}
{% block date_hierarchy %}{% if cl.date_hierarchy %}{% date_hierarchy cl %}{% endif %}{% endblock %}
<form id="changelist-form" method="post"{% if cl.formset and cl.formset.is_multipart %} enctype="multipart/form-data"{% endif %} novalidate>{% csrf_token %}
{% if cl.formset %}
<div>{{ cl.formset.management_form }}</div>
{% endif %}
{% block result_list %}
{% if action_form and actions_on_top and cl.show_admin_actions %}{% admin_actions %}{% endif %}
{% comment %}
Table grid
{% result_list cl %}
{% endcomment %}
{% snapshots_grid cl %}
{% if action_form and actions_on_bottom and cl.show_admin_actions %}{% admin_actions %}{% endif %}
{% endblock %}
{% block pagination %}{% pagination cl %}{% endblock %}
</form>
</div>
{% block filters %}
{% if cl.has_filters %}
<div id="changelist-filter">
<h2>{% translate 'Filter' %}</h2>
{% if cl.has_active_filters %}<h3 id="changelist-filter-clear">
<a href="{{ cl.clear_all_filters_qs }}">&#10006; {% translate "Clear all filters" %}</a>
</h3>{% endif %}
{% for spec in cl.filter_specs %}{% admin_list_filter cl spec %}{% endfor %}
</div>
{% endif %}
{% endblock %}
</div>
</div>
{% endblock %}

View file

@ -1,4 +1,4 @@
{% extends "base.html" %} {% extends "core/base.html" %}
{% load static %} {% load static %}
{% load i18n %} {% load i18n %}

View file

@ -6,9 +6,9 @@
<td title="{{snapshot.timestamp}}"> {% if snapshot.bookmarked_date %} {{ snapshot.bookmarked_date }} {% else %} {{ snapshot.added }} {% endif %} </td> <td title="{{snapshot.timestamp}}"> {% if snapshot.bookmarked_date %} {{ snapshot.bookmarked_date }} {% else %} {{ snapshot.added }} {% endif %} </td>
<td class="title-col"> <td class="title-col">
{% if snapshot.is_archived %} {% if snapshot.is_archived %}
<a href="archive/{{snapshot.timestamp}}/index.html"><img src="archive/{{snapshot.timestamp}}/favicon.ico" class="snapshot-favicon" decoding="async"></a> <a href="archive/{{snapshot.timestamp}}/index.html"><img src="archive/{{snapshot.timestamp}}/favicon.ico" onerror="this.style.display='none'" class="snapshot-favicon" decoding="async"></a>
{% else %} {% else %}
<a href="archive/{{snapshot.timestamp}}/index.html"><img src="{% static 'spinner.gif' %}" class="snapshot-favicon" decoding="async"></a> <a href="archive/{{snapshot.timestamp}}/index.html"><img src="{% static 'spinner.gif' %}" onerror="this.style.display='none'" class="snapshot-favicon" decoding="async"></a>
{% endif %} {% endif %}
<a href="archive/{{snapshot.timestamp}}/{{snapshot.canonical_outputs.wget_path}}" title="{{snapshot.title}}"> <a href="archive/{{snapshot.timestamp}}/{{snapshot.canonical_outputs.wget_path}}" title="{{snapshot.title}}">
<span data-title-for="{{snapshot.url}}" data-archived="{{snapshot.is_archived}}">{{snapshot.title|default:'Loading...'}}</span> <span data-title-for="{{snapshot.url}}" data-archived="{{snapshot.is_archived}}">{{snapshot.title|default:'Loading...'}}</span>
@ -26,9 +26,9 @@
<td title="{{snapshot.timestamp}}"> {% if snapshot.bookmarked_date %} {{ snapshot.bookmarked_date }} {% else %} {{ snapshot.added }} {% endif %} </td> <td title="{{snapshot.timestamp}}"> {% if snapshot.bookmarked_date %} {{ snapshot.bookmarked_date }} {% else %} {{ snapshot.added }} {% endif %} </td>
<td class="title-col" style="opacity: {% if snapshot.title %}1{% else %}0.3{% endif %}"> <td class="title-col" style="opacity: {% if snapshot.title %}1{% else %}0.3{% endif %}">
{% if snapshot.is_archived %} {% if snapshot.is_archived %}
<a href="archive/{{snapshot.timestamp}}/index.html"><img src="archive/{{snapshot.timestamp}}/favicon.ico" class="snapshot-favicon" decoding="async"></a> <a href="archive/{{snapshot.timestamp}}/index.html"><img src="archive/{{snapshot.timestamp}}/favicon.ico" onerror="this.style.display='none'" class="snapshot-favicon" decoding="async"></a>
{% else %} {% else %}
<a href="archive/{{snapshot.timestamp}}/index.html"><img src="{% static 'spinner.gif' %}" class="snapshot-favicon" decoding="async" style="height: 15px"></a> <a href="archive/{{snapshot.timestamp}}/index.html"><img src="{% static 'spinner.gif' %}" onerror="this.style.display='none'" class="snapshot-favicon" decoding="async" style="height: 15px"></a>
{% endif %} {% endif %}
<a href="archive/{{snapshot.timestamp}}/index.html" title="{{snapshot.title|default:'Not yet archived...'}}"> <a href="archive/{{snapshot.timestamp}}/index.html" title="{{snapshot.title|default:'Not yet archived...'}}">
@ -50,7 +50,7 @@
{{snapshot.icons}} <small style="float:right; opacity: 0.5">{{snapshot.num_outputs}}</small> {{snapshot.icons}} <small style="float:right; opacity: 0.5">{{snapshot.num_outputs}}</small>
{% else %} {% else %}
<a href="archive/{{snapshot.timestamp}}/index.html">📄 <a href="archive/{{snapshot.timestamp}}/index.html">📄
{{snapshot.num_outputs}} <img src="{% static 'spinner.gif' %}" class="files-spinner" decoding="async" style="height: 15px"/> {{snapshot.num_outputs}} <img src="{% static 'spinner.gif' %}" onerror="this.style.display='none'" class="files-spinner" decoding="async" style="height: 15px"/>
</a> </a>
{% endif %} {% endif %}
</span> </span>

View file

@ -22,7 +22,7 @@
</thead> </thead>
<tbody> <tbody>
{% for snapshot in snapshots %} {% for snapshot in snapshots %}
{% include "main_index_row.html" with snapshot=snapshot %} {% include "index_row.html" with snapshot=snapshot %}
{% endfor %} {% endfor %}
</tbody> </tbody>
</table> </table>

View file

@ -28,7 +28,7 @@
</thead> </thead>
<tbody> <tbody>
{% for link in object_list %} {% for link in object_list %}
{% include 'main_index_row.html' with link=link %} {% include 'index_row.html' with link=link %}
{% endfor %} {% endfor %}
</tbody> </tbody>
</table> </table>

View file

@ -234,7 +234,7 @@
</thead> </thead>
<tbody> <tbody>
{% for snapshot in snapshots %} {% for snapshot in snapshots %}
{% include 'main_index_row.html' with snapshot=snapshot %} {% include 'index_row.html' with snapshot=snapshot %}
{% endfor %} {% endfor %}
</tbody> </tbody>
</table> </table>

View file

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 17 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 158 B

After

Width:  |  Height:  |  Size: 158 B

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 201 B

After

Width:  |  Height:  |  Size: 201 B

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 157 B

After

Width:  |  Height:  |  Size: 157 B

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Before After
Before After

View file

@ -1,215 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>Archived Sites</title>
<meta charset="utf-8" name="viewport" content="width=device-width, initial-scale=1">
<style>
html, body {
width: 100%;
height: 100%;
font-size: 18px;
font-weight: 200;
text-align: center;
margin: 0px;
padding: 0px;
font-family: "Gill Sans", Helvetica, sans-serif;
}
.header-top small {
font-weight: 200;
color: #efefef;
}
.header-top {
width: 100%;
height: auto;
min-height: 40px;
margin: 0px;
text-align: center;
color: white;
font-size: calc(11px + 0.84vw);
font-weight: 200;
padding: 4px 4px;
border-bottom: 3px solid #aa1e55;
background-color: #aa1e55;
}
input[type=search] {
width: 22vw;
border-radius: 4px;
border: 1px solid #aeaeae;
padding: 3px 5px;
}
.nav > div {
min-height: 30px;
}
.header-top a {
text-decoration: none;
color: rgba(0,0,0,0.6);
}
.header-top a:hover {
text-decoration: none;
color: rgba(0,0,0,0.9);
}
.header-top .col-lg-4 {
text-align: center;
padding-top: 4px;
padding-bottom: 4px;
}
.header-archivebox img {
display: inline-block;
margin-right: 3px;
height: 30px;
margin-left: 12px;
margin-top: -4px;
margin-bottom: 2px;
}
.header-archivebox img:hover {
opacity: 0.5;
}
#table-bookmarks_length, #table-bookmarks_filter {
padding-top: 12px;
opacity: 0.8;
padding-left: 24px;
padding-right: 22px;
margin-bottom: -16px;
}
table {
padding: 6px;
width: 100%;
}
table thead th {
font-weight: 400;
}
table tr {
height: 35px;
}
tbody tr:nth-child(odd) {
background-color: #ffebeb !important;
}
table tr td {
white-space: nowrap;
overflow: hidden;
/*padding-bottom: 0.4em;*/
/*padding-top: 0.4em;*/
padding-left: 2px;
text-align: center;
}
table tr td a {
text-decoration: none;
}
table tr td img, table tr td object {
display: inline-block;
margin: auto;
height: 24px;
width: 24px;
padding: 0px;
padding-right: 5px;
vertical-align: middle;
margin-left: 4px;
}
#table-bookmarks {
width: 100%;
overflow-y: scroll;
table-layout: fixed;
}
.dataTables_wrapper {
background-color: #fafafa;
}
table tr a span[data-archived~=False] {
opacity: 0.4;
}
.files-spinner {
height: 15px;
width: auto;
opacity: 0.5;
vertical-align: -2px;
}
.in-progress {
display: none;
}
tr td a.favicon img {
padding-left: 6px;
padding-right: 12px;
vertical-align: -4px;
}
tr td a.title {
font-size: 1.4em;
text-decoration:none;
color:black;
}
tr td a.title small {
background-color: #efefef;
border-radius: 4px;
float:right
}
input[type=search]::-webkit-search-cancel-button {
-webkit-appearance: searchfield-cancel-button;
}
.title-col {
text-align: left;
}
.title-col a {
color: black;
}
</style>
<link rel="stylesheet" href="static/bootstrap.min.css">
<link rel="stylesheet" href="static/jquery.dataTables.min.css"/>
<script src="static/jquery.min.js"></script>
<script src="static/jquery.dataTables.min.js"></script>
<script>
document.addEventListener('error', function(e) {
e.target.style.opacity = 0;
}, true)
jQuery(document).ready(function() {
jQuery('#table-bookmarks').DataTable({
stateSave: true, // save state (filtered input, number of entries shown, etc) in localStorage
dom: '<lf<t>ip>', // how to show the table and its helpers (filter, etc) in the DOM
order: [[0, 'desc']],
iDisplayLength: 100,
});
});
</script>
</head>
<body>
<header>
<div class="header-top container-fluid">
<div class="row nav">
<div class="col-sm-2">
<a href="?" class="header-archivebox" title="Last updated: $time_updated">
<img src="static/archive.png" alt="Logo"/>
ArchiveBox
</a>
</div>
<div class="col-sm-10" style="text-align: right">
<a href="https://github.com/ArchiveBox/ArchiveBox/wiki">Documentation</a> &nbsp; | &nbsp;
<a href="https://github.com/ArchiveBox/ArchiveBox">Source</a> &nbsp; | &nbsp;
<a href="https://archivebox.io">Website</a>
</div>
</div>
</div>
</header>
<table id="table-bookmarks">
<thead>
<tr class="thead-tr">
<th style="width: 100px;">Bookmarked</th>
<th style="width: 26vw;">Snapshot ($num_links)</th>
<th style="width: 50px">Files</th>
<th style="width: 16vw;whitespace:nowrap;overflow-x:hidden;">Original URL</th>
</tr>
</thead>
<tbody>$rows</tbody>
</table>
<footer>
<br/>
<center>
<small>
Created using <a href="https://github.com/ArchiveBox/ArchiveBox" title="Github">ArchiveBox</a>
version <a href="https://github.com/ArchiveBox/ArchiveBox/releases" title="Releases">v$version</a>.
<br/><br/>
$footer_info
</small>
</center>
<br/>
</footer>
</body>
</html>

View file

@ -1,16 +0,0 @@
<tr>
<td title="$timestamp">$bookmarked_date</td>
<td class="title-col">
<a href="$archive_path/index.html" class="link-url"><img src="$favicon_url" class="link-favicon" decoding="async"></a>
<a href="$archive_path/$wget_url" title="$title">
<span data-title-for="$url" data-archived="$is_archived">$title</span>
<small style="float:right">$tags</small>
</a>
</td>
<td>
<a href="$archive_path/index.html">📄
<span data-number-for="$url" title="Number of extractor outputs">$num_outputs</span>
</a>
</td>
<td style="text-align:left"><a href="$url">$url</a></td>
</tr>

View file

@ -24,7 +24,6 @@
# PUBLIC_INDEX = True # PUBLIC_INDEX = True
# PUBLIC_SNAPSHOTS = True # PUBLIC_SNAPSHOTS = True
# FOOTER_INFO = Content is hosted for personal archiving purposes only. Contact server owner for any takedown requests. # FOOTER_INFO = Content is hosted for personal archiving purposes only. Contact server owner for any takedown requests.
# ACTIVE_THEME = default
[ARCHIVE_METHOD_TOGGLES] [ARCHIVE_METHOD_TOGGLES]