2
0
jdcaballerov 5 жил өмнө
parent
commit
254d2502fd

+ 32 - 1
archivebox/core/admin.py

@@ -96,6 +96,13 @@ class SnapshotAdmin(SearchResultsAdminMixin, admin.ModelAdmin):
     actions_template = 'admin/actions_as_select.html'
     form = SnapshotAdminForm
 
+    def get_urls(self):
+        urls = super().get_urls()
+        custom_urls = [
+            path('grid/', self.admin_site.admin_view(self.grid_view),name='grid')
+        ]
+        return custom_urls + urls
+
     def get_queryset(self, request):
         return super().get_queryset(request).prefetch_related('tags')
 
@@ -153,6 +160,31 @@ class SnapshotAdmin(SearchResultsAdminMixin, admin.ModelAdmin):
             obj.url.split('://www.', 1)[-1].split('://', 1)[-1][:64],
         )
 
+    def grid_view(self, request):
+
+        # cl = self.get_changelist_instance(request)
+
+        # Save before monkey patching to restore for changelist list view
+        saved_change_list_template = self.change_list_template
+        saved_list_per_page = self.list_per_page
+        saved_list_max_show_all = self.list_max_show_all
+
+        # Monkey patch here plus core_tags.py
+        self.change_list_template = 'admin/grid_change_list.html'
+        self.list_per_page = 20
+        self.list_max_show_all = self.list_per_page
+
+        # Call monkey patched view
+        rendered_response = self.changelist_view(request)
+
+        # Restore values
+        self.change_list_template =  saved_change_list_template
+        self.list_per_page = saved_list_per_page
+        self.list_max_show_all = saved_list_max_show_all
+
+        return rendered_response
+        
+
     id_str.short_description = 'ID'
     title_str.short_description = 'Title'
     url_str.short_description = 'Original URL'
@@ -218,7 +250,6 @@ class ArchiveBoxAdmin(admin.AdminSite):
 
         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)

+ 0 - 0
archivebox/core/templatetags/__init__.py


+ 47 - 0
archivebox/core/templatetags/core_tags.py

@@ -0,0 +1,47 @@
+from django import template
+from django.urls import reverse
+from django.contrib.admin.templatetags.base import InclusionAdminNode
+from django.templatetags.static import static
+
+
+from typing import Union
+
+from core.models import ArchiveResult
+
+register = template.Library()
+
[email protected]_tag
+def snapshot_image(snapshot):
+    result = ArchiveResult.objects.filter(snapshot=snapshot, extractor='screenshot', status='succeeded').first()
+    if result:
+        return reverse('LinkAssets', args=[f'{str(snapshot.timestamp)}/{result.output}'])
+    
+    return static('archive.png')
+
[email protected]
+def file_size(num_bytes: Union[int, float]) -> str:
+    for count in ['Bytes','KB','MB','GB']:
+        if num_bytes > -1024.0 and num_bytes < 1024.0:
+            return '%3.1f %s' % (num_bytes, count)
+        num_bytes /= 1024.0
+    return '%3.1f %s' % (num_bytes, 'TB')
+
+def result_list(cl):
+    """
+    Monkey patched result
+    """
+    num_sorted_fields = 0
+    return {
+        'cl': cl,
+        'num_sorted_fields': num_sorted_fields,
+        'results': cl.result_list,
+    }
+
[email protected](name='snapshots_grid')
+def result_list_tag(parser, token):
+    return InclusionAdminNode(
+        parser, token,
+        func=result_list,
+        template_name='snapshots_grid.html',
+        takes_context=False,
+    )

+ 3 - 0
archivebox/themes/admin/base.html

@@ -107,6 +107,9 @@
                 <a href="{% url 'admin:password_change' %}">{% trans 'Change password' %}</a> /
                 {% endif %}
                 <a href="{% url 'admin:logout' %}">{% trans 'Log out' %}</a>
+                |
+                <a href="{% url 'admin:core_snapshot_changelist' %}">☰</a> 
+                <a href="{% url 'admin:grid' %}"><span style="letter-spacing: -.4em">⣿⣿</span>&nbsp;&nbsp;</a> 
             {% endblock %}
         </div>
         {% endif %}

+ 91 - 0
archivebox/themes/admin/grid_change_list.html

@@ -0,0 +1,91 @@
+{% 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 %}
+          {% comment %} {% if action_form and actions_on_top and cl.show_admin_actions %}{% admin_actions %}{% endif %} {% endcomment %}
+          {% comment %} 
+             Table grid
+            {% result_list cl %} 
+          {% endcomment %}
+          {% snapshots_grid cl %}
+          {% comment %} {% if action_form and actions_on_bottom and cl.show_admin_actions %}{% admin_actions %}{% endif %} {% endcomment %}
+        {% 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 %}

+ 158 - 0
archivebox/themes/admin/snapshots_grid.html

@@ -0,0 +1,158 @@
+{% load i18n admin_urls static admin_list %}
+{% load core_tags %}
+
+{% block extrastyle %}
+<style>
+* {
+  -webkit-box-sizing: border-box;
+    -moz-box-sizing: border-box;
+    box-sizing: border-box;
+}
+
+a {
+  text-decoration: none;
+  color: orange;
+}
+h2 {
+  color: #000;
+  margin: 2rem 0 .5rem;
+  font-size: 1.25rem;
+  font-weight: 400;
+  {% comment %} text-transform: uppercase; {% endcomment %}
+}
+
+card.img {
+  display: block;
+    border: 0;
+    width: 100%;
+    height: auto;
+}
+
+/***************************           Cards            *******************************/
+
+.cards {
+  display: grid;
+  grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); /* see notes below */
+  grid-auto-rows: minmax(200px, auto);
+  grid-gap: 1rem;
+}
+
+.card {
+  /*height: 200px;*/
+  /*background: red;*/
+  border: 2px solid #e7e7e7;
+  border-radius: 4px;
+  -webkit-box-shadow: 0 2px 2px rgba(0, 0, 0, 0.15);
+  box-shadow: 0 2px 2px rgba(0, 0, 0, 0.15);
+  display: flex;
+  /* -webkit-box-orient: vertical; */
+  /* -webkit-box-direction: normal; */
+  -ms-flex-direction: column;
+  flex-direction: column;
+  position: relative;
+  color: #5d5e5e;
+} /* li item */
+
+.thumbnail img {
+    height: 100%;
+    box-sizing: border-box;
+    max-width: 100%;
+    max-height: 100%;
+    width: 100%;
+}
+
+.card-content {
+    font-size: .75rem;
+    padding: .5rem;
+    display: flex;
+    -webkit-box-orient: vertical;
+    -webkit-box-direction: normal;
+    -ms-flex-direction: column;
+    flex-direction: column;
+    -webkit-box-flex: 1;
+    -ms-flex: 1;
+    flex: 1;
+    
+}
+
+.card-content h4{
+vertical-align:bottom;
+margin: 1.2em 0 0em 0;
+}
+
+.category {
+  font-size: .75rem;
+  text-transform: uppercase;
+}
+.category {
+    position: absolute;
+    top: 5%;
+    right: 0;
+    color: #fff;
+    background: #e74c3c;
+    padding: 10px 15px;
+    font-size: 10px;
+    font-weight: 600;
+    text-transform: uppercase;
+}
+
+.category__01 {
+  background-color: #50c6db;
+  
+}
+
+.tags{
+  opacity: 0.8;
+}
+
+footer {
+  border-top: 2px solid #e7e7e7;
+  {% comment %} margin: .5rem 0 0; {% endcomment %}
+  {% comment %} min-height: 30px; {% endcomment %}
+  font-size: .5rem;
+}
+.post-meta {
+  padding: .3rem;
+}
+
+.comments {
+  margin-left: .5rem;
+}
+
+
+</style>
+
+{% endblock %}
+
+{% block content %}
+    <section class="cards">
+    {% for obj in results %}
+      <article class="card">
+          <picture class="thumbnail">
+            <a href="/{{obj.archive_path}}/index.html">
+              <img class="category__01" src="{% snapshot_image obj%}" alt="" /> 
+            </a>
+          </picture>
+          <div class="card-content">
+            {% if obj.tags_str %}
+            <p class="category category__01 tags">{{obj.tags_str}}</p>
+            {% endif %}
+            {% if obj.title  %}
+            <a href="{% url 'admin:core_snapshot_change' obj.id %}">
+              <h4>{{obj.title|truncatechars:55 }}</h4>
+            </a>
+            {% endif %}
+            {% comment %} <p> TEXT If needed.</p> {% endcomment %}
+        </div><!-- .card-content -->
+        <footer>
+            <div class="post-meta">
+                <span class="timestamp">&#128337 {{obj.added}}</span>
+                <span class="comments">📖{{obj.num_outputs}}</span>
+                <span>🗄️{{ obj.archive_size | file_size }}</span>
+            </div>
+        </footer> 
+      </article>
+    {% endfor %}
+    </section>
+
+{% endblock %}