fix pending titles and favicons, improve add page, custom admin

This commit is contained in:
Nick Sweeting 2020-07-27 23:26:45 -04:00
parent 022231b362
commit 3aeca0e450
23 changed files with 387 additions and 316 deletions

View file

@ -1,19 +1,31 @@
from django.contrib import admin __package__ = 'archivebox.core'
from django.utils.html import format_html
from io import StringIO
from contextlib import redirect_stdout
from django.contrib import admin
from django.urls import path
from django.utils.html import format_html
from django.shortcuts import render
from django.contrib.auth import get_user_model
from util import htmldecode, urldecode
from core.models import Snapshot from core.models import Snapshot
from archivebox.logging_util import printable_filesize from core.forms import AddLinkForm
from ..util import htmldecode, urldecode, ansi_to_html
from ..logging_util import printable_filesize
from ..main import add
from ..config import OUTPUT_DIR
# 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
class SnapshotAdmin(admin.ModelAdmin): class SnapshotAdmin(admin.ModelAdmin):
list_display = ('title_str', 'url_str', 'tags', 'files', 'size', 'added', 'updated') list_display = ('added', 'title_str', 'url_str', 'tags', 'files', 'size', 'updated')
sort_fields = ('title_str', 'url_str', 'tags', 'added', 'updated') sort_fields = ('title_str', 'url_str', 'tags', 'added', 'updated')
readonly_fields = ('id', 'num_outputs', 'is_archived', 'url_hash', 'added', 'updated') readonly_fields = ('id', 'url', 'timestamp', 'num_outputs', 'is_archived', 'url_hash', 'added', 'updated')
search_fields = ('url', 'timestamp', 'title', 'tags') search_fields = ('url', 'timestamp', 'title', 'tags')
fields = ('url', 'timestamp', 'title', 'tags', *readonly_fields) fields = ('title', 'tags', *readonly_fields)
list_filter = ('added', 'updated', 'tags') list_filter = ('added', 'updated', 'tags')
ordering = ['-added'] ordering = ['-added']
@ -27,15 +39,16 @@ class SnapshotAdmin(admin.ModelAdmin):
canon = obj.as_link().canonical_outputs() canon = obj.as_link().canonical_outputs()
return format_html( return format_html(
'<a href="/{}">' '<a href="/{}">'
'<img src="/{}/{}" style="height: 20px; width: 20px;" onerror="this.remove()">' '<img src="/{}/{}" class="favicon" onerror="this.remove()">'
' &nbsp; &nbsp; '
'</a>' '</a>'
'<a href="/{}/{}">' '<a href="/{}/{}">'
'<b>{}</b></a>', '<b class="status-{}">{}</b>'
'</a>',
obj.archive_path, obj.archive_path,
obj.archive_path, canon['favicon_path'], obj.archive_path, canon['favicon_path'],
obj.archive_path, canon['wget_path'] or '', obj.archive_path, canon['wget_path'] or '',
urldecode(htmldecode(obj.latest_title or obj.title or '-'))[:128], 'fetched' if obj.latest_title or obj.title else 'pending',
urldecode(htmldecode(obj.latest_title or obj.title or ''))[:128] or 'Pending...',
) )
def files(self, obj): def files(self, obj):
@ -68,17 +81,68 @@ class SnapshotAdmin(admin.ModelAdmin):
def url_str(self, obj): def url_str(self, obj):
return format_html( return format_html(
'<a href="{}"><code>{}</code></a>', '<a href="{}">{}</a>',
obj.url, obj.url,
obj.url.split('://www.', 1)[-1].split('://', 1)[-1][:64], obj.url.split('://www.', 1)[-1].split('://', 1)[-1][:64],
) )
id_str.short_description = 'ID' id_str.short_description = 'ID'
title_str.short_description = 'Title' title_str.short_description = 'Title'
url_str.short_description = 'URL' url_str.short_description = 'Original URL'
id_str.admin_order_field = 'id' id_str.admin_order_field = 'id'
title_str.admin_order_field = 'title' title_str.admin_order_field = 'title'
url_str.admin_order_field = 'url' url_str.admin_order_field = 'url'
class ArchiveBoxAdmin(admin.AdminSite):
site_header = 'ArchiveBox'
index_title = 'Links'
site_title = 'Index'
def get_urls(self):
return [
path('core/snapshot/add/', self.add_view, name='add'),
] + super().get_urls()
def add_view(self, request):
request.current_app = self.name
context = {
**self.each_context(request),
'title': 'Add URLs',
}
if request.method == 'GET':
context['form'] = AddLinkForm()
elif request.method == 'POST':
form = AddLinkForm(request.POST)
if form.is_valid():
url = form.cleaned_data["url"]
print(f'[+] Adding URL: {url}')
depth = 0 if form.cleaned_data["depth"] == "0" else 1
input_kwargs = {
"urls": url,
"depth": depth,
"update_all": False,
"out_dir": OUTPUT_DIR,
}
add_stdout = StringIO()
with redirect_stdout(add_stdout):
add(**input_kwargs)
print(add_stdout.getvalue())
context.update({
"stdout": ansi_to_html(add_stdout.getvalue().strip()),
"form": AddLinkForm()
})
else:
context["form"] = form
return render(template_name='add_links.html', request=request, context=context)
admin.site = ArchiveBoxAdmin()
admin.site.register(get_user_model())
admin.site.register(Snapshot, SnapshotAdmin) admin.site.register(Snapshot, SnapshotAdmin)

View file

@ -1,10 +1,14 @@
__package__ = 'archivebox.core'
from django import forms from django import forms
from ..util import URL_REGEX
CHOICES = ( CHOICES = (
('0', 'depth=0 (archive just this url)'), ('0', 'depth = 0 (archive just these URLs)'),
('1', 'depth=1 (archive this url and all sites one link away)'), ('1', 'depth = 1 (archive these URLs and all URLs one hop away)'),
) )
class AddLinkForm(forms.Form): class AddLinkForm(forms.Form):
url = forms.URLField() url = forms.RegexField(label="URLs (one per line)", regex=URL_REGEX, min_length='6', strip=True, widget=forms.Textarea, required=True)
depth = forms.ChoiceField(choices=CHOICES, widget=forms.RadioSelect, initial='0') depth = forms.ChoiceField(label="Archive depth", choices=CHOICES, widget=forms.RadioSelect, initial='0')

View file

@ -0,0 +1,28 @@
# Generated by Django 3.0.7 on 2020-07-28 03:26
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('core', '0004_auto_20200713_1552'),
]
operations = [
migrations.AlterField(
model_name='snapshot',
name='tags',
field=models.CharField(blank=True, db_index=True, max_length=256, null=True),
),
migrations.AlterField(
model_name='snapshot',
name='title',
field=models.CharField(blank=True, db_index=True, max_length=128, null=True),
),
migrations.AlterField(
model_name='snapshot',
name='updated',
field=models.DateTimeField(blank=True, db_index=True, null=True),
),
]

View file

@ -15,11 +15,11 @@ class Snapshot(models.Model):
url = models.URLField(unique=True) url = models.URLField(unique=True)
timestamp = models.CharField(max_length=32, unique=True, db_index=True) timestamp = models.CharField(max_length=32, unique=True, db_index=True)
title = models.CharField(max_length=128, null=True, default=None, db_index=True) title = models.CharField(max_length=128, null=True, blank=True, db_index=True)
tags = models.CharField(max_length=256, null=True, default=None, db_index=True) tags = models.CharField(max_length=256, null=True, blank=True, db_index=True)
added = models.DateTimeField(auto_now_add=True, db_index=True) added = models.DateTimeField(auto_now_add=True, db_index=True)
updated = models.DateTimeField(null=True, default=None, db_index=True) updated = models.DateTimeField(null=True, blank=True, db_index=True)
# bookmarked = models.DateTimeField() # bookmarked = models.DateTimeField()
keys = ('url', 'timestamp', 'title', 'tags', 'updated') keys = ('url', 'timestamp', 'title', 'tags', 'updated')

View file

@ -5,16 +5,16 @@ import sys
from django.utils.crypto import get_random_string from django.utils.crypto import get_random_string
from ..config import ( from ..config import ( # noqa: F401
OUTPUT_DIR, DEBUG,
SECRET_KEY, SECRET_KEY,
ALLOWED_HOSTS, ALLOWED_HOSTS,
PYTHON_DIR, PYTHON_DIR,
ACTIVE_THEME, ACTIVE_THEME,
SQL_INDEX_FILENAME, SQL_INDEX_FILENAME,
OUTPUT_DIR,
) )
ALLOWED_HOSTS = ALLOWED_HOSTS.split(',') ALLOWED_HOSTS = ALLOWED_HOSTS.split(',')
IS_SHELL = 'shell' in sys.argv[:3] or 'shell_plus' in sys.argv[:3] IS_SHELL = 'shell' in sys.argv[:3] or 'shell_plus' in sys.argv[:3]
@ -25,8 +25,8 @@ INSTALLED_APPS = [
'django.contrib.contenttypes', 'django.contrib.contenttypes',
'django.contrib.sessions', 'django.contrib.sessions',
'django.contrib.messages', 'django.contrib.messages',
'django.contrib.admin',
'django.contrib.staticfiles', 'django.contrib.staticfiles',
'django.contrib.admin',
'core', 'core',
@ -121,5 +121,4 @@ STATIC_URL = '/static/'
STATICFILES_DIRS = [ STATICFILES_DIRS = [
os.path.join(PYTHON_DIR, 'themes', ACTIVE_THEME, 'static'), os.path.join(PYTHON_DIR, 'themes', ACTIVE_THEME, 'static'),
os.path.join(PYTHON_DIR, 'themes', 'default', 'static'), os.path.join(PYTHON_DIR, 'themes', 'default', 'static'),
os.path.join(PYTHON_DIR, 'themes', 'static'),
] ]

View file

@ -3,15 +3,12 @@ from django.contrib import admin
from django.urls import path, include from django.urls import path, include
from django.views import static from django.views import static
from django.conf import settings from django.conf import settings
from django.contrib.staticfiles.views import serve as serve_static
from django.views.generic.base import RedirectView from django.views.generic.base import RedirectView
from core.views import MainIndex, AddLinks, LinkDetails from core.views import MainIndex, LinkDetails
admin.site.site_header = 'ArchiveBox'
admin.site.index_title = 'Links'
admin.site.site_title = 'Index'
# print('DEBUG', settings.DEBUG)
urlpatterns = [ urlpatterns = [
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'}),
@ -19,14 +16,11 @@ urlpatterns = [
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>', LinkDetails.as_view(), name='LinkAssets'),
path('add/', AddLinks.as_view(), name='AddLinks'), path('add/', RedirectView.as_view(url='/admin/core/snapshot/add/')),
path('static/<path>', serve_static),
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/')),
path('admin/core/snapshot/add/', RedirectView.as_view(url='/add/')),
path('accounts/', include('django.contrib.auth.urls')), path('accounts/', include('django.contrib.auth.urls')),
path('admin/', admin.site.urls), path('admin/', admin.site.urls),

View file

@ -7,9 +7,6 @@ from django.views import View, static
from core.models import Snapshot from core.models import Snapshot
from contextlib import redirect_stdout
from io import StringIO
from ..index import load_main_index, load_main_index_meta from ..index import load_main_index, load_main_index_meta
from ..config import ( from ..config import (
OUTPUT_DIR, OUTPUT_DIR,
@ -18,10 +15,7 @@ from ..config import (
PUBLIC_INDEX, PUBLIC_INDEX,
PUBLIC_SNAPSHOTS, PUBLIC_SNAPSHOTS,
) )
from ..util import base_url, ansi_to_html from ..util import base_url
from .. main import add
from .forms import AddLinkForm
class MainIndex(View): class MainIndex(View):
@ -45,48 +39,6 @@ class MainIndex(View):
return render(template_name=self.template, request=request, context=context) return render(template_name=self.template, request=request, context=context)
class AddLinks(View):
template = 'add_links.html'
def get(self, request):
if not request.user.is_authenticated and not PUBLIC_INDEX:
return redirect(f'/admin/login/?next={request.path}')
context = {
"form": AddLinkForm()
}
return render(template_name=self.template, request=request, context=context)
def post(self, request):
if not request.user.is_authenticated and not PUBLIC_INDEX:
return redirect(f'/admin/login/?next={request.path}')
form = AddLinkForm(request.POST)
if form.is_valid():
url = form.cleaned_data["url"]
print(f'[+] Adding URL: {url}')
depth = 0 if form.cleaned_data["depth"] == "0" else 0
input_kwargs = {
"urls": url,
"depth": depth,
"update_all": False,
"out_dir": OUTPUT_DIR,
}
add_stdout = StringIO()
with redirect_stdout(add_stdout):
add(**input_kwargs)
print(add_stdout.getvalue())
context = {
"stdout": ansi_to_html(add_stdout.getvalue()),
"form": AddLinkForm()
}
else:
context = {"form": form}
return render(template_name=self.template, request=request, context=context)
class LinkDetails(View): class LinkDetails(View):
def get(self, request, path): def get(self, request, path):
# missing trailing slash -> redirect to index # missing trailing slash -> redirect to index

View file

@ -136,8 +136,8 @@ def link_details_template(link: Link) -> str:
'url_str': htmlencode(urldecode(link.base_url)), 'url_str': htmlencode(urldecode(link.base_url)),
'archive_url': urlencode( 'archive_url': urlencode(
wget_output_path(link) wget_output_path(link)
or (link.domain if link.is_archived else 'about:blank') or (link.domain if link.is_archived else '')
), ) or 'about:blank',
'extension': link.extension or 'html', 'extension': link.extension or 'html',
'tags': link.tags or 'untagged', 'tags': link.tags or 'untagged',
'status': 'archived' if link.is_archived else 'not yet archived', 'status': 'archived' if link.is_archived else 'not yet archived',

View file

@ -83,6 +83,7 @@ from .config import (
EXTERNAL_LOCATIONS, EXTERNAL_LOCATIONS,
DATA_LOCATIONS, DATA_LOCATIONS,
DEPENDENCIES, DEPENDENCIES,
DEBUG,
load_all_config, load_all_config,
CONFIG, CONFIG,
USER_CONFIG, USER_CONFIG,
@ -987,13 +988,19 @@ def server(runserver_args: Optional[List[str]]=None,
"""Run the ArchiveBox HTTP server""" """Run the ArchiveBox HTTP server"""
runserver_args = runserver_args or [] runserver_args = runserver_args or []
check_data_folder(out_dir=out_dir)
from . import config
config.SHOW_PROGRESS = False
if debug: if debug:
os.environ['DEBUG'] = 'True' # if --debug is passed, patch config.DEBUG to be True for this run
config.DEBUG = True
else: else:
# force staticfiles to be served when DEBUG=False
# TODO: do this using nginx or another server instead of django?
runserver_args.append('--insecure') runserver_args.append('--insecure')
check_data_folder(out_dir=out_dir)
setup_django(out_dir) setup_django(out_dir)
from django.core.management import call_command from django.core.management import call_command
from django.contrib.auth.models import User from django.contrib.auth.models import User

View file

@ -2,7 +2,7 @@
{% get_current_language as LANGUAGE_CODE %}{% get_current_language_bidi as LANGUAGE_BIDI %} {% get_current_language as LANGUAGE_CODE %}{% get_current_language_bidi as LANGUAGE_BIDI %}
<html lang="{{ LANGUAGE_CODE|default:"en-us" }}" {% if LANGUAGE_BIDI %}dir="rtl"{% endif %}> <html lang="{{ LANGUAGE_CODE|default:"en-us" }}" {% if LANGUAGE_BIDI %}dir="rtl"{% endif %}>
<head> <head>
<title>{% block title %}{% endblock %}</title> <title>{% block title %}{% endblock %} | ArchiveBox</title>
<link rel="stylesheet" type="text/css" href="{% block stylesheet %}{% static "admin/css/base.css" %}{% endblock %}"> <link rel="stylesheet" type="text/css" href="{% block stylesheet %}{% static "admin/css/base.css" %}{% endblock %}">
{% block extrastyle %}{% endblock %} {% block extrastyle %}{% endblock %}
{% if LANGUAGE_BIDI %}<link rel="stylesheet" type="text/css" href="{% block stylesheet_rtl %}{% static "admin/css/rtl.css" %}{% endblock %}">{% endif %} {% if LANGUAGE_BIDI %}<link rel="stylesheet" type="text/css" href="{% block stylesheet_rtl %}{% static "admin/css/rtl.css" %}{% endblock %}">{% endif %}
@ -13,6 +13,7 @@
{% if LANGUAGE_BIDI %}<link rel="stylesheet" type="text/css" href="{% static "admin/css/responsive_rtl.css" %}">{% endif %} {% if LANGUAGE_BIDI %}<link rel="stylesheet" type="text/css" href="{% static "admin/css/responsive_rtl.css" %}">{% endif %}
{% endblock %} {% endblock %}
{% block blockbots %}<meta name="robots" content="NONE,NOARCHIVE">{% endblock %} {% block blockbots %}<meta name="robots" content="NONE,NOARCHIVE">{% endblock %}
<link rel="stylesheet" type="text/css" href="{% static "admin.css" %}">
</head> </head>
{% load i18n %} {% load i18n %}
@ -26,13 +27,14 @@
<!-- Header --> <!-- Header -->
<div id="header"> <div id="header">
<div id="branding"> <div id="branding">
{% block branding %}{% endblock %} {% block branding %}{% endblock %}
</div> </div>
{% block usertools %} {% block usertools %}
{% if has_permission %} {% if has_permission %}
<div id="user-tools"> <div id="user-tools">
<a href="/add/">Add Links</a> / <a href="/add/">Add Links</a> /
<a href="/">Main Index</a> / <a href="/">Main Index</a> /
<a href="/admin/">Admin</a> /
<a href="https://github.com/pirate/ArchiveBox/wiki">Docs</a> <a href="https://github.com/pirate/ArchiveBox/wiki">Docs</a>
&nbsp; &nbsp; &nbsp; &nbsp;
{% block welcome-msg %} {% block welcome-msg %}
@ -76,7 +78,7 @@
<!-- Content --> <!-- Content -->
<div id="content" class="{% block coltype %}colM{% endblock %}"> <div id="content" class="{% block coltype %}colM{% endblock %}">
{% block pretitle %}{% endblock %} {% block pretitle %}{% endblock %}
{% block content_title %}{% if title %}<h1>{{ title }}</h1>{% endif %}{% endblock %} {% block content_title %}{# {% if title %}<h1>{{ title }}</h1>{% endif %} #}{% endblock %}
{% block content %} {% block content %}
{% block object-tools %}{% endblock %} {% block object-tools %}{% endblock %}
{{ content }} {{ content }}

View file

@ -1,218 +1,113 @@
{% load static %} {% extends "admin/index.html" %}
{% load i18n %}
<!DOCTYPE html> {% block breadcrumbs %}
<html lang="en"> <div class="breadcrumbs">
<head> <a href="{% url 'admin:index' %}">{% trans 'Home' %}</a>
<title>Archived Sites</title> {% if title %} &rsaquo; {{ title }}{% endif %}
<meta charset="utf-8" name="viewport" content="width=device-width, initial-scale=1"> </div>
<style> {% endblock %}
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 { {% block content %}
width: 100%; <style>
height: auto; .dashboard #content {
min-height: 40px; width: 100%;
margin: 0px; margin-right: 0px;
text-align: center; margin-left: 0px;
color: white; }
font-size: calc(11px + 0.84vw); #submit {
font-weight: 200; border: 1px solid rgba(0,0,0,0.2);
padding: 4px 4px; padding: 10px;
border-bottom: 3px solid #aa1e55; border-radius: 4px;
background-color: #aa1e55; background-color: #f5dd5d;
} color: #333;
input[type=search] { font-size: 18px;
width: 22vw; font-weight: 800;
border-radius: 4px; }
border: 1px solid #aeaeae; #add-form button[role=submit]:hover {
padding: 3px 5px; background-color: #e5cd4d;
} }
.nav > div { #add-form label {
min-height: 30px; display: block;
} font-size: 16px;
.header-top a { }
text-decoration: none; #add-form textarea {
color: rgba(0,0,0,0.6); width: 100%;
} min-height: 300px;
.header-top a:hover { }
text-decoration: none; #delay-warning div {
color: rgba(0,0,0,0.9); border: 1px solid red;
} border-radius: 4px;
.header-top .col-lg-4 { margin: 10px;
text-align: center; padding: 10px;
padding-top: 4px; font-size: 15px;
padding-bottom: 4px; background-color: #F5DD5D;
} }
.header-archivebox img { #stdout {
display: inline-block; background-color: #ded;
margin-right: 3px; padding: 10px 10px;
height: 30px; border-radius: 4px;
margin-left: 12px; white-space: normal;
margin-top: -4px; }
margin-bottom: 2px; .loader {
} border: 16px solid #f3f3f3; /* Light grey */
.header-archivebox img:hover { border-top: 16px solid #3498db; /* Blue */
opacity: 0.5; border-radius: 50%;
} width: 120px;
height: 120px;
animation: spin 2s linear infinite;
}
#table-bookmarks_length, #table-bookmarks_filter { @keyframes spin {
padding-top: 12px; 0% { transform: rotate(0deg); }
opacity: 0.8; 100% { transform: rotate(360deg); }
padding-left: 24px; }
padding-right: 22px; </style>
margin-bottom: -16px; <div style="max-width: 550px; margin: auto; float: none">
} <br/><br/>
table { {% if stdout %}
padding: 6px; <h1>Add new URLs to your archive: results</h1>
width: 100%; <pre id="stdout">
} {{ stdout | safe }}
table thead th { <br/><br/>
font-weight: 400; </pre>
} <br/>
table tr { <center>
height: 35px; <a href="/add" id="submit">&nbsp; Add more URLs </a>
} </center>
tbody tr:nth-child(odd) { {% else %}
background-color: #ffebeb !important; <form id="add-form" action="?" method="POST" class="p-form">{% csrf_token %}
} <h1>Add new URLs to your archive</h1>
table tr td { <br/>
white-space: nowrap; {{ form.as_p }}
overflow: hidden; <center>
/*padding-bottom: 0.4em;*/ <button role="submit" id="submit">&nbsp; Add URLs and archive </button>
/*padding-top: 0.4em;*/ </center>
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;
}
body[data-status~=finished] .files-spinner {
display: none;
}
/*body[data-status~=running] .in-progress {
display: inline-block;
}*/
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;
}
.ul-form {
list-style: none;
}
.ul-form li {
list-style: none;
}
</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 data-status="finished">
<header>
<div class="header-top container-fluid">
<div class="row nav">
<div class="col-sm-2">
<a href="/" class="header-archivebox" title="Last updated: {{updated}}">
<img src="{% static 'archive.png' %}" alt="Logo"/>
ArchiveBox: Add
</a>
</div>
<div class="col-sm-10" style="text-align: right">
<a href="/">Main Index</a> &nbsp; | &nbsp;
<a href="/admin/">Admin</a> &nbsp; | &nbsp;
<a href="https://github.com/pirate/ArchiveBox/wiki">Docs</a>
</div>
</div>
</div>
</header>
<center>
{{ stdout | safe }}
<br/><br/>
<form action="?" method="POST" class="ul-form">{% csrf_token %}
Add new links...<br/>
{{ form.as_ul }}
<button role="submit">Add</button>
</form> </form>
</center> <br/><br/><br/>
<center id="delay-warning" style="display: none">
<b><i>This page will be unresponsive until the process is completely finished.</i></b>
<br/><br/>
<div>
Warning: it may take several minutes to finish adding!<br/>
<br/>
Progress will be displayed in the <code>archivebox server</code> stdout,<br/>
and on this page once the archiving process completes.<br/>
<br/>
<small>(it's safe to leave this page, adding will continue in the background)</small>
</div>
</center>
<script>
document.getElementById('add-form').addEventListener('submit', function(event) {
setTimeout(function() {
document.getElementById('add-form').innerHTML = '<center><h3>Adding URLs to index and running archive methods...<h3><br/><div class="loader"></div><br/>(see terminal for progress)</center>'
document.getElementById('delay-warning').style.display = 'block'
}, 200)
return true
})
</script>
{% endif %}
</div>
{% endblock %}
<a href="{% url 'admin:core_snapshot_changelist' %}">Go back to Main Index</a> {% block sidebar %}{% endblock %}
</body>
</html>

View file

@ -0,0 +1,126 @@
#header {
background: #aa1e55;
padding: 6px 14px;
}
#content {
padding: 8px 8px;
}
#user-tools {
font-size: 13px;
}
div.breadcrumbs {
background: #772948;
color: #f5dd5d;
padding: 6px 15px;
}
.module h2, .module caption, .inline-group h2 {
background: #772948;
}
#content .object-tools {
margin-top: -35px;
margin-right: -10px;
float: right;
}
#content .object-tools a:link, #content .object-tools a:visited {
border-radius: 0px;
background-color: #f5dd5d;
color: #333;
font-size: 12px;
font-weight: 800;
}
#content .object-tools a.addlink {
background-blend-mode: difference;
}
#content #changelist #toolbar {
padding: 0px;
background: none;
margin-bottom: 10px;
border-top: 0px;
border-bottom: 0px;
}
#content #changelist #toolbar form input[type="submit"] {
border-color: #aa1e55;
}
#content #changelist-filter li.selected a {
color: #aa1e55;
}
#content #changelist .actions {
position: fixed;
bottom: 0px;
z-index: 800;
}
#content #changelist .actions .button {
border-color: #aa1e55;
}
#content #changelist-filter h2 {
border-radius: 4px 4px 0px 0px;
}
@media (min-width: 767px) {
#content #changelist-filter {
top: 35px;
width: 110px;
}
.change-list .filtered .results,
.change-list .filtered .paginator,
.filtered #toolbar,
.filtered div.xfull {
margin-right: 115px;
}
}
#content a img.favicon {
height: 20px;
width: 20px;
vertical-align: -5px;
padding-right: 6px;
}
#content td, #content th {
vertical-align: middle;
}
#content #changelist table input {
vertical-align: -2px;
}
#content th.field-added, #content td.field-updated {
word-break: break-word;
min-width: 85px;
white-space: normal;
}
#content th.field-title_str {
min-width: 300px;
}
#content td.field-files {
white-space: nowrap;
}
#content td.field-size {
white-space: nowrap;
}
#content td.field-url_str {
word-break: break-all;
min-width: 200px;
}
#content tr b.status-pending {
font-weight: 200;
opacity: 0.6;
}

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

@ -359,18 +359,6 @@
</div> </div>
</div> </div>
</div> </div>
<div class="col-lg-2">
<div class="card">
<iframe class="card-img-top" src="$url" sandbox="allow-same-origin allow-scripts allow-forms" scrolling="no"></iframe>
<div class="card-body">
<a href="$url" style="float:right" title="Open in new tab..." target="_blank" rel="noopener">
<img src="../../static/external.png" class="external"/>
</a>
<a href="$url" target="preview"><h4 class="card-title">Original</h4></a>
<p class="card-text">$domain</p>
</div>
</div>
</div>
<div class="col-lg-2"> <div class="col-lg-2">
<div class="card"> <div class="card">
<iframe class="card-img-top" src="$archive_org_path" sandbox="allow-same-origin allow-scripts allow-forms" scrolling="no"></iframe> <iframe class="card-img-top" src="$archive_org_path" sandbox="allow-same-origin allow-scripts allow-forms" scrolling="no"></iframe>
@ -383,6 +371,18 @@
</div> </div>
</div> </div>
</div> </div>
<div class="col-lg-2">
<div class="card">
<iframe class="card-img-top" src="$url" sandbox="allow-same-origin allow-scripts allow-forms" scrolling="no"></iframe>
<div class="card-body">
<a href="$url" style="float:right" title="Open in new tab..." target="_blank" rel="noopener">
<img src="../../static/external.png" class="external"/>
</a>
<a href="$url" target="preview"><h4 class="card-title">Original</h4></a>
<p class="card-text">$domain</p>
</div>
</div>
</div>
</div> </div>
</div> </div>
</header> </header>