admin.py 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
  1. __package__ = 'archivebox.core'
  2. from io import StringIO
  3. from contextlib import redirect_stdout
  4. from django.contrib import admin
  5. from django.urls import path
  6. from django.utils.html import format_html
  7. from django.shortcuts import render, redirect
  8. from django.contrib.auth import get_user_model
  9. from core.models import Snapshot
  10. from core.forms import AddLinkForm
  11. from ..util import htmldecode, urldecode, ansi_to_html
  12. from ..logging_util import printable_filesize
  13. from ..main import add
  14. from ..config import OUTPUT_DIR
  15. # TODO: https://stackoverflow.com/questions/40760880/add-custom-button-to-django-admin-panel
  16. class SnapshotAdmin(admin.ModelAdmin):
  17. list_display = ('added', 'title_str', 'url_str', 'tags', 'files', 'size', 'updated')
  18. sort_fields = ('title_str', 'url_str', 'tags', 'added', 'updated')
  19. readonly_fields = ('id', 'url', 'timestamp', 'num_outputs', 'is_archived', 'url_hash', 'added', 'updated')
  20. search_fields = ('url', 'timestamp', 'title', 'tags')
  21. fields = ('title', 'tags', *readonly_fields)
  22. list_filter = ('added', 'updated', 'tags')
  23. ordering = ['-added']
  24. def id_str(self, obj):
  25. return format_html(
  26. '<code style="font-size: 10px">{}</code>',
  27. obj.url_hash[:8],
  28. )
  29. def title_str(self, obj):
  30. canon = obj.as_link().canonical_outputs()
  31. return format_html(
  32. '<a href="/{}">'
  33. '<img src="/{}/{}" class="favicon" onerror="this.remove()">'
  34. '</a>'
  35. '<a href="/{}/{}">'
  36. '<b class="status-{}">{}</b>'
  37. '</a>',
  38. obj.archive_path,
  39. obj.archive_path, canon['favicon_path'],
  40. obj.archive_path, canon['wget_path'] or '',
  41. 'fetched' if obj.latest_title or obj.title else 'pending',
  42. urldecode(htmldecode(obj.latest_title or obj.title or ''))[:128] or 'Pending...',
  43. )
  44. def files(self, obj):
  45. canon = obj.as_link().canonical_outputs()
  46. return format_html(
  47. '<span style="font-size: 1.2em; opacity: 0.8">'
  48. '<a href="/{}/{}" title="Wget clone">🌐 </a> '
  49. '<a href="/{}/{}" title="PDF">📄</a> '
  50. '<a href="/{}/{}" title="Screenshot">🖥 </a> '
  51. '<a href="/{}/{}" title="HTML dump">🅷 </a> '
  52. '<a href="/{}/{}" title="Media files">📼 </a> '
  53. '<a href="/{}/{}" title="Git repos">📦 </a> '
  54. '<a href="/{}/{}" title="Archive.org snapshot">🏛 </a> '
  55. '</span>',
  56. obj.archive_path, canon['wget_path'] or '',
  57. obj.archive_path, canon['pdf_path'],
  58. obj.archive_path, canon['screenshot_path'],
  59. obj.archive_path, canon['dom_path'],
  60. obj.archive_path, canon['media_path'],
  61. obj.archive_path, canon['git_path'],
  62. obj.archive_path, canon['archive_org_path'],
  63. )
  64. def size(self, obj):
  65. return format_html(
  66. '<a href="/{}" title="View all files">{}</a>',
  67. obj.archive_path,
  68. printable_filesize(obj.archive_size) if obj.archive_size else 'pending',
  69. )
  70. def url_str(self, obj):
  71. return format_html(
  72. '<a href="{}">{}</a>',
  73. obj.url,
  74. obj.url.split('://www.', 1)[-1].split('://', 1)[-1][:64],
  75. )
  76. id_str.short_description = 'ID'
  77. title_str.short_description = 'Title'
  78. url_str.short_description = 'Original URL'
  79. id_str.admin_order_field = 'id'
  80. title_str.admin_order_field = 'title'
  81. url_str.admin_order_field = 'url'
  82. class ArchiveBoxAdmin(admin.AdminSite):
  83. site_header = 'ArchiveBox'
  84. index_title = 'Links'
  85. site_title = 'Index'
  86. def get_urls(self):
  87. return [
  88. path('core/snapshot/add/', self.add_view, name='Add'),
  89. ] + super().get_urls()
  90. def add_view(self, request):
  91. if not request.user.is_authenticated:
  92. return redirect(f'/admin/login/?next={request.path}')
  93. request.current_app = self.name
  94. context = {
  95. **self.each_context(request),
  96. 'title': 'Add URLs',
  97. }
  98. if request.method == 'GET':
  99. context['form'] = AddLinkForm()
  100. elif request.method == 'POST':
  101. form = AddLinkForm(request.POST)
  102. if form.is_valid():
  103. url = form.cleaned_data["url"]
  104. print(f'[+] Adding URL: {url}')
  105. depth = 0 if form.cleaned_data["depth"] == "0" else 1
  106. input_kwargs = {
  107. "urls": url,
  108. "depth": depth,
  109. "update_all": False,
  110. "out_dir": OUTPUT_DIR,
  111. }
  112. add_stdout = StringIO()
  113. with redirect_stdout(add_stdout):
  114. add(**input_kwargs)
  115. print(add_stdout.getvalue())
  116. context.update({
  117. "stdout": ansi_to_html(add_stdout.getvalue().strip()),
  118. "form": AddLinkForm()
  119. })
  120. else:
  121. context["form"] = form
  122. return render(template_name='add_links.html', request=request, context=context)
  123. admin.site = ArchiveBoxAdmin()
  124. admin.site.register(get_user_model())
  125. admin.site.register(Snapshot, SnapshotAdmin)
  126. admin.site.disable_action('delete_selected')