diff --git a/archivebox/config/__init__.py b/archivebox/config/__init__.py index 3fa5e090..af69e94d 100644 --- a/archivebox/config/__init__.py +++ b/archivebox/config/__init__.py @@ -69,6 +69,7 @@ CONFIG_DEFAULTS: Dict[str, ConfigDefaultDict] = { 'DEBUG': {'type': bool, 'default': False}, 'PUBLIC_INDEX': {'type': bool, 'default': True}, 'PUBLIC_SNAPSHOTS': {'type': bool, 'default': True}, + '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.'}, 'ACTIVE_THEME': {'type': str, 'default': 'default'}, }, diff --git a/archivebox/core/admin.py b/archivebox/core/admin.py index f69b809d..4337e4a3 100644 --- a/archivebox/core/admin.py +++ b/archivebox/core/admin.py @@ -2,7 +2,6 @@ __package__ = 'archivebox.core' from io import StringIO from contextlib import redirect_stdout -from pathlib import Path from django.contrib import admin from django.urls import path @@ -13,6 +12,7 @@ from django.contrib.auth import get_user_model from core.models import Snapshot from core.forms import AddLinkForm +from core.utils import get_icons from util import htmldecode, urldecode, ansi_to_html from logging_util import printable_filesize @@ -93,34 +93,7 @@ class SnapshotAdmin(admin.ModelAdmin): ) + mark_safe(f'{tags}') def files(self, obj): - link = obj.as_link() - canon = link.canonical_outputs() - out_dir = Path(link.link_dir) - - link_tuple = lambda link, method: (link.archive_path, canon[method] or '', canon[method] and (out_dir / (canon[method] or 'notdone')).exists()) - - return format_html( - '' - '🌐 ' - '📄 ' - '🖥 ' - '🅷 ' - '🆆 ' - '🗜 ' - '📼 ' - '📦 ' - '🏛 ' - '', - *link_tuple(link, 'wget_path'), - *link_tuple(link, 'pdf_path'), - *link_tuple(link, 'screenshot_path'), - *link_tuple(link, 'dom_path'), - *link_tuple(link, 'warc_path')[:2], any((out_dir / canon['warc_path']).glob('*.warc.gz')), - *link_tuple(link, 'singlefile_path'), - *link_tuple(link, 'media_path')[:2], any((out_dir / canon['media_path']).glob('*')), - *link_tuple(link, 'git_path')[:2], any((out_dir / canon['git_path']).glob('*')), - canon['archive_org_path'], (out_dir / 'archive.org.txt').exists(), - ) + return get_icons(obj) def size(self, obj): return format_html( diff --git a/archivebox/core/urls.py b/archivebox/core/urls.py index b830de68..e11653fd 100644 --- a/archivebox/core/urls.py +++ b/archivebox/core/urls.py @@ -5,7 +5,7 @@ from django.views import static from django.conf import settings from django.views.generic.base import RedirectView -from core.views import MainIndex, OldIndex, LinkDetails +from core.views import MainIndex, LinkDetails, PublicArchiveView, AddView # print('DEBUG', settings.DEBUG) @@ -18,7 +18,9 @@ urlpatterns = [ path('archive/', RedirectView.as_view(url='/')), path('archive/', LinkDetails.as_view(), name='LinkAssets'), - path('add/', RedirectView.as_view(url='/admin/core/snapshot/add/')), + + path('admin/core/snapshot/add/', RedirectView.as_view(url='/add/')), + path('add/', AddView.as_view()), path('accounts/login/', RedirectView.as_view(url='/admin/login/')), path('accounts/logout/', RedirectView.as_view(url='/admin/logout/')), @@ -27,8 +29,8 @@ urlpatterns = [ path('accounts/', include('django.contrib.auth.urls')), path('admin/', admin.site.urls), - path('old.html', OldIndex.as_view(), name='OldHome'), path('index.html', RedirectView.as_view(url='/')), path('index.json', static.serve, {'document_root': settings.OUTPUT_DIR, 'path': 'index.json'}), path('', MainIndex.as_view(), name='Home'), + path('public/', PublicArchiveView.as_view(), name='public-index'), ] diff --git a/archivebox/core/utils.py b/archivebox/core/utils.py new file mode 100644 index 00000000..902eef01 --- /dev/null +++ b/archivebox/core/utils.py @@ -0,0 +1,36 @@ +from pathlib import Path + +from django.utils.html import format_html + +from core.models import Snapshot + + +def get_icons(snapshot: Snapshot) -> str: + link = snapshot.as_link() + canon = link.canonical_outputs() + out_dir = Path(link.link_dir) + + link_tuple = lambda link, method: (link.archive_path, canon[method] or '', canon[method] and (out_dir / (canon[method] or 'notdone')).exists()) + + return format_html( + '' + '🌐 ' + '📄 ' + '🖥 ' + '🅷 ' + '🆆 ' + '🗜 ' + '📼 ' + '📦 ' + '🏛 ' + '', + *link_tuple(link, 'wget_path'), + *link_tuple(link, 'pdf_path'), + *link_tuple(link, 'screenshot_path'), + *link_tuple(link, 'dom_path'), + *link_tuple(link, 'warc_path')[:2], any((out_dir / canon['warc_path']).glob('*.warc.gz')), + *link_tuple(link, 'singlefile_path'), + *link_tuple(link, 'media_path')[:2], any((out_dir / canon['media_path']).glob('*')), + *link_tuple(link, 'git_path')[:2], any((out_dir / canon['git_path']).glob('*')), + canon['archive_org_path'], (out_dir / 'archive.org.txt').exists(), + ) diff --git a/archivebox/core/views.py b/archivebox/core/views.py index 399f368e..4144b2db 100644 --- a/archivebox/core/views.py +++ b/archivebox/core/views.py @@ -1,21 +1,28 @@ __package__ = 'archivebox.core' +from io import StringIO +from contextlib import redirect_stdout + from django.shortcuts import render, redirect from django.http import HttpResponse from django.views import View, static +from django.views.generic.list import ListView +from django.views.generic import FormView +from django.contrib.auth.mixins import UserPassesTestMixin from core.models import Snapshot +from core.utils import get_icons +from core.forms import AddLinkForm -from ..index import load_main_index, load_main_index_meta from ..config import ( OUTPUT_DIR, - VERSION, - FOOTER_INFO, PUBLIC_INDEX, PUBLIC_SNAPSHOTS, + PUBLIC_ADD_VIEW ) -from ..util import base_url +from main import add +from ..util import base_url, ansi_to_html class MainIndex(View): @@ -26,32 +33,10 @@ class MainIndex(View): return redirect('/admin/core/snapshot/') if PUBLIC_INDEX: - return redirect('OldHome') + return redirect('public-index') return redirect(f'/admin/login/?next={request.path}') - - -class OldIndex(View): - template = 'main_index.html' - - def get(self, request): - if PUBLIC_INDEX or request.user.is_authenticated: - all_links = load_main_index(out_dir=OUTPUT_DIR) - meta_info = load_main_index_meta(out_dir=OUTPUT_DIR) - - context = { - 'updated': meta_info['updated'], - 'num_links': meta_info['num_links'], - 'links': all_links, - 'VERSION': VERSION, - 'FOOTER_INFO': FOOTER_INFO, - } - - return render(template_name=self.template, request=request, context=context) - - return redirect(f'/admin/login/?next={request.path}') - class LinkDetails(View): def get(self, request, path): @@ -102,3 +87,60 @@ class LinkDetails(View): content_type="text/plain", status=404, ) + +class PublicArchiveView(ListView): + template = 'snapshot_list.html' + model = Snapshot + paginate_by = 100 + + def get_queryset(self, **kwargs): + qs = super().get_queryset(**kwargs) + query = self.request.GET.get('q') + if query: + qs = Snapshot.objects.filter(title__icontains=query) + for snapshot in qs: + snapshot.icons = get_icons(snapshot) + return qs + + def get(self, *args, **kwargs): + if PUBLIC_INDEX or self.request.user.is_authenticated: + response = super().get(*args, **kwargs) + return response + else: + return redirect(f'/admin/login/?next={self.request.path}') + + +class AddView(UserPassesTestMixin, FormView): + template_name = "add_links.html" + form_class = AddLinkForm + + def test_func(self): + return PUBLIC_ADD_VIEW or self.request.user.is_authenticated + + def get_context_data(self, *args, **kwargs): + context = super().get_context_data(*args, **kwargs) + context["title"] = "Add URLs" + return context + + def form_valid(self, form): + 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 = self.get_context_data() + + context.update({ + "stdout": ansi_to_html(add_stdout.getvalue().strip()), + "form": AddLinkForm() + }) + return render(template_name=self.template_name, request=self.request, context=context) diff --git a/archivebox/themes/admin/base.html b/archivebox/themes/admin/base.html index efe63e00..5870f4ae 100644 --- a/archivebox/themes/admin/base.html +++ b/archivebox/themes/admin/base.html @@ -89,7 +89,6 @@ Add ➕ / Snapshots / Users / - Old UI / Docs     {% block welcome-msg %} diff --git a/archivebox/themes/default/add_links.html b/archivebox/themes/default/add_links.html index 80a4b1fc..cb6f4341 100644 --- a/archivebox/themes/default/add_links.html +++ b/archivebox/themes/default/add_links.html @@ -1,4 +1,6 @@ -{% extends "admin/index.html" %} +{% extends "base.html" %} + +{% load static %} {% load i18n %} {% block breadcrumbs %} @@ -8,48 +10,11 @@ {% endblock %} -{% block content %} - +{% block extra_head %} + +{% endblock %} + +{% block body %}


{% if stdout %} @@ -63,7 +28,7 @@   Add more URLs ➕ {% else %} -
{% csrf_token %} + {% csrf_token %}

Add new URLs to your archive


{{ form.as_p }} diff --git a/archivebox/themes/default/base.html b/archivebox/themes/default/base.html new file mode 100644 index 00000000..ed7d1be9 --- /dev/null +++ b/archivebox/themes/default/base.html @@ -0,0 +1,286 @@ +{% load static %} + + + + + + Archived Sites + + + + + + {% block extra_head %} + {% endblock %} + + + + + + +
+
+ +
+
+ {% block body %} + {% endblock %} +
+
+
+
+ + Archive created using ArchiveBox   | +   + Download index as JSON +

+ {{FOOTER_INFO}} +
+
+
+
+ + + \ No newline at end of file diff --git a/archivebox/themes/default/core/snapshot_list.html b/archivebox/themes/default/core/snapshot_list.html new file mode 100644 index 00000000..8cb4cec5 --- /dev/null +++ b/archivebox/themes/default/core/snapshot_list.html @@ -0,0 +1,64 @@ +{% extends "base.html" %} +{% load static %} + +{% block body %} +
+ + + + +
+ + + + + + + + + + + {% for link in object_list %} + + + + + + + {% endfor %} + +
BookmarkedSaved Link ({{num_links}})FilesOriginal URL
{{link.added}} + {% if link.is_archived %} + + {% else %} + + {% endif %} + + {{link.title|default:'Loading...'}} + {{link.tags|default:''}} + + + 📄 + {{link.icons}} + + {{link.url}}
+
+ + {% if page_obj.has_previous %} + « first + previous + {% endif %} + + + Page {{ page_obj.number }} of {{ page_obj.paginator.num_pages }}. + + + {% if page_obj.has_next %} + next + last » + {% endif %} + +
+
+ {% endblock %} \ No newline at end of file diff --git a/archivebox/themes/default/static/add.css b/archivebox/themes/default/static/add.css new file mode 100644 index 00000000..b128bf4b --- /dev/null +++ b/archivebox/themes/default/static/add.css @@ -0,0 +1,62 @@ +.dashboard #content { + width: 100%; + margin-right: 0px; + margin-left: 0px; +} +#submit { + border: 1px solid rgba(0, 0, 0, 0.2); + padding: 10px; + border-radius: 4px; + background-color: #f5dd5d; + color: #333; + font-size: 18px; + font-weight: 800; +} +#add-form button[role="submit"]:hover { + background-color: #e5cd4d; +} +#add-form label { + display: block; + font-size: 16px; +} +#add-form textarea { + width: 100%; + min-height: 300px; +} +#delay-warning div { + border: 1px solid red; + border-radius: 4px; + margin: 10px; + padding: 10px; + font-size: 15px; + background-color: #f5dd5d; +} +#stdout { + background-color: #ded; + padding: 10px 10px; + border-radius: 4px; + white-space: normal; +} +ul#id_depth { + list-style-type: none; + padding: 0; +} + +@keyframes spin { + 0% { + transform: rotate(0deg); + } + 100% { + transform: rotate(360deg); + } +} + +.loader { + border: 16px solid #f3f3f3; /* Light grey */ + border-top: 16px solid #3498db; /* Blue */ + border-radius: 50%; + width: 30px; + height: 30px; + box-sizing: border-box; + animation: spin 2s linear infinite; +}