+
3
+from django.db import migrations, models
4
+import django.db.models.deletion
5
+
6
+
7
+class Migration(migrations.Migration):
8
+
9
+    dependencies = [
10
+        ('backend', '0030_alter_points_dest'),
11
+    ]
12
+
13
+    operations = [
14
+        migrations.AddField(
15
+            model_name='points',
16
+            name='ticket',
17
+            field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='backend.ambulanceticket'),
18
+        ),
19
+    ]

+ 17 - 0
app/backend/migrations/0032_remove_points_ticket.py

@@ -0,0 +1,17 @@
1
+# Generated by Django 3.2.5 on 2021-07-23 09:51
2
+
3
+from django.db import migrations
4
+
5
+
6
+class Migration(migrations.Migration):
7
+
8
+    dependencies = [
9
+        ('backend', '0031_points_ticket'),
10
+    ]
11
+
12
+    operations = [
13
+        migrations.RemoveField(
14
+            model_name='points',
15
+            name='ticket',
16
+        ),
17
+    ]

+ 18 - 0
app/backend/migrations/0033_patient_address_text.py

@@ -0,0 +1,18 @@
1
+# Generated by Django 3.2.5 on 2021-07-25 04:21
2
+
3
+from django.db import migrations, models
4
+
5
+
6
+class Migration(migrations.Migration):
7
+
8
+    dependencies = [
9
+        ('backend', '0032_remove_points_ticket'),
10
+    ]
11
+
12
+    operations = [
13
+        migrations.AddField(
14
+            model_name='patient',
15
+            name='address_text',
16
+            field=models.TextField(blank=True, null=True),
17
+        ),
18
+    ]

+ 23 - 0
app/backend/migrations/0034_auto_20210725_1125.py

@@ -0,0 +1,23 @@
1
+# Generated by Django 3.2.5 on 2021-07-25 04:25
2
+
3
+from django.db import migrations, models
4
+
5
+
6
+class Migration(migrations.Migration):
7
+
8
+    dependencies = [
9
+        ('backend', '0033_patient_address_text'),
10
+    ]
11
+
12
+    operations = [
13
+        migrations.AddField(
14
+            model_name='patient',
15
+            name='birth_date',
16
+            field=models.DateField(null=True),
17
+        ),
18
+        migrations.AlterField(
19
+            model_name='patient',
20
+            name='age',
21
+            field=models.IntegerField(blank=True, null=True),
22
+        ),
23
+    ]

+ 18 - 0
app/backend/migrations/0035_patient_patient_status.py

@@ -0,0 +1,18 @@
1
+# Generated by Django 3.2.5 on 2021-07-25 05:01
2
+
3
+from django.db import migrations, models
4
+
5
+
6
+class Migration(migrations.Migration):
7
+
8
+    dependencies = [
9
+        ('backend', '0034_auto_20210725_1125'),
10
+    ]
11
+
12
+    operations = [
13
+        migrations.AddField(
14
+            model_name='patient',
15
+            name='patient_status',
16
+            field=models.CharField(choices=[('request', 'Request'), ('process', 'Process'), ('complete', 'Complete')], max_length=30, null=True),
17
+        ),
18
+    ]

+ 23 - 0
app/backend/migrations/0036_auto_20210725_1207.py

@@ -0,0 +1,23 @@
1
+# Generated by Django 3.2.5 on 2021-07-25 05:07
2
+
3
+from django.db import migrations, models
4
+
5
+
6
+class Migration(migrations.Migration):
7
+
8
+    dependencies = [
9
+        ('backend', '0035_patient_patient_status'),
10
+    ]
11
+
12
+    operations = [
13
+        migrations.AddField(
14
+            model_name='patient',
15
+            name='created_at',
16
+            field=models.DateTimeField(auto_now_add=True, null=True),
17
+        ),
18
+        migrations.AddField(
19
+            model_name='patient',
20
+            name='updated_at',
21
+            field=models.DateTimeField(auto_now=True),
22
+        ),
23
+    ]

BIN
app/backend/migrations/__pycache__/0030_alter_points_dest.cpython-39.pyc


BIN
app/backend/migrations/__pycache__/0031_points_ticket.cpython-39.pyc


BIN
app/backend/migrations/__pycache__/0032_remove_points_ticket.cpython-39.pyc


BIN
app/backend/migrations/__pycache__/0033_patient_address_text.cpython-39.pyc


BIN
app/backend/migrations/__pycache__/0034_auto_20210725_1125.cpython-39.pyc


BIN
app/backend/migrations/__pycache__/0035_patient_patient_status.cpython-39.pyc


BIN
app/backend/migrations/__pycache__/0036_auto_20210725_1207.cpython-39.pyc


+ 65 - 20
app/backend/models.py

@@ -8,11 +8,13 @@ from smart_selects.db_fields import (
8 8
     ChainedManyToManyField,
9 9
     GroupedForeignKey,
10 10
 )
11
+from django.db.models import Q
11 12
 import googlemaps
12 13
 from django.contrib.gis.geos import fromstr
13 14
 
14 15
 from django.conf import settings
15 16
 import csv
17
+import haversine as hs
16 18
 
17 19
 gmaps = googlemaps.Client(key=settings.GOOGLE_MAPS_API_KEY)
18 20
 # Create your models here.
@@ -37,7 +39,6 @@ class Ambulance(models.Model):
37 39
         choices=(("working", "Working"), ("free", "Free"), ("ma", "MA")),
38 40
         null=True,
39 41
     )
40
-
41 42
     created_at = models.DateTimeField(auto_now_add=True, null=True)
42 43
     updated_at = models.DateTimeField(auto_now=True)
43 44
 
@@ -105,7 +106,8 @@ class AmbulanceTicket(models.Model):
105 106
 class Patient(models.Model):
106 107
     first_name = models.CharField(max_length=100)
107 108
     last_name = models.CharField(max_length=100)
108
-    age = models.IntegerField()
109
+    birth_date = models.DateField(null=True)
110
+    age = models.IntegerField(null=True, blank=True)
109 111
     idcard = models.CharField(max_length=20, null=True, blank=False)
110 112
     prefix = models.CharField(
111 113
         max_length=30,
@@ -127,13 +129,50 @@ class Patient(models.Model):
127 129
         null=True,
128 130
     )
129 131
     comment  = models.TextField(blank=True, null=True)
132
+    address_text  = models.TextField(blank=True, null=True)
130 133
     #test
134
+    patient_status = models.CharField(
135
+        max_length=30,
136
+        choices=(("request", "Request"), ("process", "Process"), ("complete", "Complete")),
137
+        null=True,
138
+    )
139
+    created_at = models.DateTimeField(auto_now_add=True, null=True)
140
+    updated_at = models.DateTimeField(auto_now=True)
141
+
131 142
     def __str__(self):
132 143
         #self.nearby()
133 144
         return f"{self.first_name} {self.last_name}"
134 145
 
135 146
 
147
+
148
+    def nearby_from_db(self, dlimit):
149
+        bd = ""
150
+        temps = []
151
+        for h0 in  Hospital.objects.all():
152
+            p2 = (h0.geolocation.lat, h0.geolocation.lon)
153
+            p1 = (self.geolocation.lat, self.geolocation.lon)
154
+            d = round(hs.haversine(p1,p2), 2)
155
+            if d < dlimit:
156
+                #bd += f"<tr><td>{h0.title}</td><td>{d}km</td></tr>"
157
+                temps.append({'title': h0.title, 'd': d, 'id': h0.id, 'beds': h0.free_beds()})
158
+                #print(f"to {h0.title} => {hs.haversine(p1, p2)}km")
159
+
160
+        temps.sort(key= lambda s: s['d'], reverse=False)
161
+        for t in temps:
162
+            bd += f"<tr><td><a href='/admin/backend/hospital/{t['id']}/change/' target='_blank'>{t['title']}</a></td><td>{t['d']} km</td><td>{t['beds']}</td></tr>"
163
+
164
+        rt = f'''
165
+<br>
166
+        <table><thead><tr><th>Hospital</th><th>Distance</th><th>Free Beds</th></tr></thead>
167
+        <tbody>
168
+            {bd}
169
+        </tbody>
170
+        </table>
171
+        '''
172
+        return rt
173
+
136 174
     def nearby(self):
175
+        #self.nearby_from_db()
137 176
         r = gmaps.places_nearby(location=(self.geolocation.lat, self.geolocation.lon), type="hospital", radius=10000)
138 177
         bd = ""
139 178
         for r0 in r['results']:
@@ -199,9 +238,32 @@ class Place(models.Model):
199 238
 
200 239
     def __str__(self):
201 240
         return f"{self.address} ({self.geolocation})"
241
+
242
+
243
+
244
+
245
+class Hospital(models.Model):
246
+    title = models.CharField(max_length=200)
247
+    location = models.PointField(blank=True, null=True)
248
+    address_text = models.TextField(blank=True, null=True)
249
+    #address = models.CharField(max_length=100)
250
+    address = map_fields.AddressField(max_length=200)
251
+    geolocation = map_fields.GeoLocationField(max_length=100)
252
+
253
+    created_at = models.DateTimeField(auto_now_add=True, null=True)
254
+    updated_at = models.DateTimeField(auto_now=True)
255
+
256
+    def __str__(self):
257
+        return f"{self.title} {self.address_text}"
258
+
259
+    def free_beds(self):
260
+        return self.bed_set.filter(~Q(occupy=True)).count()
261
+
262
+
202 263
 class Points(models.Model):
203 264
     #src  = models.ForeignKey(Place, on_delete=models.SET_NULL, null=True, blank=False, related_name='src')
204
-    dest  = models.ForeignKey(Place, on_delete=models.SET_NULL, null=True, blank=False, related_name='dest')
265
+    #ticket = models.ForeignKey(AmbulanceTicket, on_delete=models.SET_NULL, null=True)
266
+    dest  = models.ForeignKey(Hospital, on_delete=models.SET_NULL, null=True, blank=False)
205 267
     address = map_fields.AddressField(max_length=200, null=True)
206 268
     geolocation = map_fields.GeoLocationField(max_length=100, null=True)
207 269
 
@@ -225,23 +287,6 @@ class Points(models.Model):
225 287
         print(dst)
226 288
         super(Points, self).save(*args, **kwargs)
227 289
 
228
-
229
-
230
-class Hospital(models.Model):
231
-    title = models.CharField(max_length=200)
232
-    location = models.PointField(blank=True, null=True)
233
-    address_text = models.TextField(blank=True, null=True)
234
-    #address = models.CharField(max_length=100)
235
-    address = map_fields.AddressField(max_length=200)
236
-    geolocation = map_fields.GeoLocationField(max_length=100)
237
-
238
-    created_at = models.DateTimeField(auto_now_add=True, null=True)
239
-    updated_at = models.DateTimeField(auto_now=True)
240
-
241
-    def __str__(self):
242
-        return f"{self.title} {self.address_text}"
243
-
244
-
245 290
 class Bed(models.Model):
246 291
     code = models.CharField(max_length=30)
247 292
     occupy = models.BooleanField(default=False)

+ 1 - 1
app/backend/templates/backend/index.html

@@ -4,5 +4,5 @@
4 4
 
5 5
 {% block content %}
6 6
 Hello world
7
-<a href="{% url "import_file" %}">Import</a>
7
+<a href="{% url "backend.import_file" %}">Import</a>
8 8
 {% endblock %}

+ 6 - 1
app/backend/urls.py

@@ -1,8 +1,13 @@
1 1
 from django.urls import path
2
-
2
+from django.conf.urls import url
3 3
 from . import views
4 4
 
5 5
 urlpatterns = [
6 6
     path('', views.index, name='index'),
7 7
     path('import_file', views.import_file, name='import_file'),
8
+     url(
9
+        r'^hospital-autocomplete/$',
10
+        views.HospitalAutocomplete.as_view(),
11
+        name='hospital-autocomplete',
12
+    ),
8 13
 ]

+ 17 - 0
app/backend/views.py

@@ -1,5 +1,7 @@
1 1
 from django.shortcuts import render
2
+from dal import autocomplete
2 3
 
4
+from .models import Hospital
3 5
 # Create your views here.
4 6
 from django.http import HttpResponse
5 7
 
@@ -9,3 +11,18 @@ def index(request):
9 11
 
10 12
 def import_file(request):
11 13
     return render(request, 'backend/import_file.html')
14
+
15
+
16
+
17
+class HospitalAutocomplete(autocomplete.Select2QuerySetView):
18
+    def get_queryset(self):
19
+        # Don't forget to filter out results depending on the visitor !
20
+        if not self.request.user.is_authenticated:
21
+            return Hospital.objects.none()
22
+
23
+        qs = Hospital.objects.all()
24
+
25
+        if self.q:
26
+            qs = qs.filter(title__contains=self.q)
27
+
28
+        return qs

+ 26 - 0
app/cert.pem

@@ -0,0 +1,26 @@
1
+-----BEGIN CERTIFICATE-----
2
+MIIEdTCCAt2gAwIBAgIQHZxX+6y4OViy/tCHkaIX+jANBgkqhkiG9w0BAQsFADCB
3
+kTEeMBwGA1UEChMVbWtjZXJ0IGRldmVsb3BtZW50IENBMTMwMQYDVQQLDCpzaW1w
4
+bGljb2x0ZC5AbWFjaGluZS5sb2NhbCAoU2ltcGxpY28gTHRkLikxOjA4BgNVBAMM
5
+MW1rY2VydCBzaW1wbGljb2x0ZC5AbWFjaGluZS5sb2NhbCAoU2ltcGxpY28gTHRk
6
+LikwHhcNMjEwNzI0MTAzMjUzWhcNMjMxMDI0MTAzMjUzWjBdMScwJQYDVQQKEx5t
7
+a2NlcnQgZGV2ZWxvcG1lbnQgY2VydGlmaWNhdGUxMjAwBgNVBAsMKXJvb3RAbWFj
8
+aGluZS5sb2NhbCAoU3lzdGVtIEFkbWluaXN0cmF0b3IpMIIBIjANBgkqhkiG9w0B
9
+AQEFAAOCAQ8AMIIBCgKCAQEAzO3kDm8SonO2P/pSOQmFPYoj2+fz8nEd5ss+vL/e
10
+Rc5BaJsq1651glIdOyUX13B4E0viTF0aa6SP9jZLU6lPluJwW5UYCuVSW67wL0Ev
11
+C82TBpBz2hMGuC6DcpG0nJgzY04kuz0mJaqWtWD4AMq3BOwgBNy2PrB45cUGBBe3
12
+bvsCip9ykVW8vHEWKgHpdnZvUYnbbbea9jDR/nB0cDeKd5K45V/Lv2ECBAGa272C
13
+9a9uqXuzIcVFsdOb9ss6YNyiSs1AJeXTV16M5u6ZlVN95IU9AHqWxeTCVYyGZWZt
14
+g1XD7MB/M22/mDUjZg1fdK3v2n+RFxyo/FGoNE6ziACz6wIDAQABo3wwejAOBgNV
15
+HQ8BAf8EBAMCBaAwEwYDVR0lBAwwCgYIKwYBBQUHAwEwHwYDVR0jBBgwFoAUHmGB
16
+OP5O8TsFjAThvXx+jAsNHWQwMgYDVR0RBCswKYIJbG9jYWxob3N0hwQAAAAAhwR/
17
+AAABhxAAAAAAAAAAAAAAAAAAAAABMA0GCSqGSIb3DQEBCwUAA4IBgQA/wteJRFk9
18
+0NqaOKrD1YPXvhq+rXns5yEWgnomi11d5LkHvwtM9A9HmkW0/LB8FPoiY17NUPqR
19
+xZi2RAQcHn9j1wDuBKEMb5Y/xCt5k+Szhyvgfxyqkt4ef4yJ46vIcSXTLjb/+LWm
20
+LW+YLl4vbtYnq4zYtcczVQrnDoPNZGjTGIGDs15PzHZoFt7fY7dypFr/McZnI11z
21
+RRd+z7cjMoxs9UCO7O/f/E8Rpv6pkNuAVzPrezy84fcyN9elbr1eJVNKjLXWf0CR
22
+Ry2PxLkK/x7tWcE2eLVY+kZC5HfHzkiXgulBazhXprLwIcFwePmkQQ2b8ktY/+io
23
+a5DUZ56gZV2BpLK0KiokAmRZh5GWBkMwrJu5YgALiqJAGGg+JBPC6SMOp9vlXPqw
24
+zldZGjtb8jcFpCC6xMvsWag+MF6pIQrOSV+AaPf5/008jbrfwDlBXtMcxWmNtBJv
25
+CAFZlQ+UNIb/QHEM22WQNGacCmyX+hLy/GwCJVk8wyNfPOz2wYKVeAk=
26
+-----END CERTIFICATE-----

BIN
app/client.ks


+ 0 - 0
app/front/__init__.py


BIN
app/front/__pycache__/__init__.cpython-39.pyc


BIN
app/front/__pycache__/admin.cpython-39.pyc


BIN
app/front/__pycache__/apps.cpython-39.pyc


BIN
app/front/__pycache__/models.cpython-39.pyc


BIN
app/front/__pycache__/urls.cpython-39.pyc


BIN
app/front/__pycache__/views.cpython-39.pyc


+ 3 - 0
app/front/admin.py

@@ -0,0 +1,3 @@
1
+from django.contrib import admin
2
+
3
+# Register your models here.

+ 6 - 0
app/front/apps.py

@@ -0,0 +1,6 @@
1
+from django.apps import AppConfig
2
+
3
+
4
+class FrontConfig(AppConfig):
5
+    default_auto_field = 'django.db.models.BigAutoField'
6
+    name = 'front'

+ 0 - 0
app/front/migrations/__init__.py


BIN
app/front/migrations/__pycache__/__init__.cpython-39.pyc


+ 3 - 0
app/front/models.py

@@ -0,0 +1,3 @@
1
+from django.db import models
2
+
3
+# Create your models here.

+ 35 - 0
app/front/templates/front/index.html

@@ -0,0 +1,35 @@
1
+{% extends "base.html" %}
2
+
3
+{% load static %}
4
+{% block content %}
5
+<br>
6
+<div class='row'>
7
+    <form method='post' enctype="multipart/form-data">
8
+         {% csrf_token %}
9
+    <div class='col-md-12'>
10
+        <input type='text' name='firstName' class='form-control' placeholder='ชื่อ' required/>
11
+        <br>
12
+        <input type='text' name='lastName' class='form-control' placeholder='นามสกุล' required/>
13
+        <br>
14
+        <input type='text' name='idCard' class='form-control' placeholder='หมายเลขบัตรประชาชน' required />
15
+        <br>
16
+        <input type='date' name='bd' class='form-control' placeholder='วันเกิด' required />
17
+        <br>
18
+        <textarea name='address' class='form-control' placeholder='ที่อยู่' required></textarea>  
19
+        <br>
20
+        <textarea name='comment' class='form-control' placeholder='ข้อความฝากถึงเจ้าหน้าที่'></textarea>  
21
+        <br>
22
+        <input type='tel' name='tel' class='form-control' placeholder='Tel.' required/>
23
+        <br>
24
+        <label>อัพโหลดภาพ</label>
25
+        <input type="file" name='photo' accept="image/*;capture=camera" class='form-control' required> </br>
26
+        <span class="glyphicon glyphicon-map-marker"></span>
27
+        <a class='btn btn-primary form-control' id="currentLocationBtn">
28
+        <i class="bi bi-geo-alt-fill"></i>
29
+            คลิกเพื่อแสดงตำแหน่ง</a><br><br>
30
+        <input type='text' class='form-control' name='geo' id='geoText' readonly/><br>
31
+        <input type='submit' value='ส่งข้อมูล' class='btn-success form-control'/><br><br>
32
+    </div>
33
+    </form>
34
+</div>
35
+{% endblock %}

+ 8 - 0
app/front/templates/front/success.html

@@ -0,0 +1,8 @@
1
+
2
+{% extends "base.html" %}
3
+
4
+{% block content %}
5
+<div class="alert alert-success" role="alert">
6
+<h1>จะติดต่อกลับไปโดยเร็วครับ</h1>
7
+</div>
8
+{% endblock %}

+ 3 - 0
app/front/tests.py

@@ -0,0 +1,3 @@
1
+from django.test import TestCase
2
+
3
+# Create your tests here.

+ 9 - 0
app/front/urls.py

@@ -0,0 +1,9 @@
1
+
2
+from django.urls import path
3
+from django.conf.urls import url
4
+from . import views
5
+
6
+urlpatterns = [
7
+    path('', views.index, name='index'),
8
+    path('success', views.success, name='success'),
9
+]

+ 25 - 0
app/front/views.py

@@ -0,0 +1,25 @@
1
+from django.shortcuts import render, redirect
2
+from backend.models import Patient
3
+
4
+# Create your views here.
5
+
6
+def index(request):
7
+    if request.method == "POST":
8
+        print(request.POST)
9
+        print(request.FILES)
10
+        p = Patient()
11
+        p.first_name = request.POST.get('firstName')
12
+        p.last_name = request.POST.get('lastName')
13
+        p.idcard = request.POST.get('idCard')
14
+        p.address = request.POST.get('address')
15
+        p.geolocation = request.POST.get('geo')
16
+        p.birth_date = request.POST.get('bd')
17
+        p.comment = request.POST.get('comment')
18
+        p.photo = request.FILES.get('photo')
19
+        p.patient_status = "request"
20
+        p.save()
21
+        return redirect('success')
22
+    return render(request, 'front/index.html')
23
+
24
+def success(request):
25
+    return render(request, 'front/success.html')

+ 28 - 0
app/key.pem

@@ -0,0 +1,28 @@
1
+-----BEGIN PRIVATE KEY-----
2
+MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDM7eQObxKic7Y/
3
++lI5CYU9iiPb5/PycR3myz68v95FzkFomyrXrnWCUh07JRfXcHgTS+JMXRprpI/2
4
+NktTqU+W4nBblRgK5VJbrvAvQS8LzZMGkHPaEwa4LoNykbScmDNjTiS7PSYlqpa1
5
+YPgAyrcE7CAE3LY+sHjlxQYEF7du+wKKn3KRVby8cRYqAel2dm9Ridttt5r2MNH+
6
+cHRwN4p3krjlX8u/YQIEAZrbvYL1r26pe7MhxUWx05v2yzpg3KJKzUAl5dNXXozm
7
+7pmVU33khT0AepbF5MJVjIZlZm2DVcPswH8zbb+YNSNmDV90re/af5EXHKj8Uag0
8
+TrOIALPrAgMBAAECggEBALGtmMwC9c8wMFYsPVoCrSl8OjcSV2pfNSPEGLMiUB+K
9
+AyAlWPID6xKBC6MaOB+s/g8M/jpjhuLJnaBF1u3EoKMb1XsyO9RGnC+t78Wo6Jd9
10
+N/q7CBeN44eRnJqbRlN3iyaQvDwzen2x+FVuq9hT6nc0G1bb3o9gBpKBTwQBZCOt
11
+o4djb1dkSx3qB917VC4+0yK1pknliANxxdVcq8ZHfcY3G7xkgV8crn5UB95HmFAp
12
+9sJ1XxpXHqoD+lgLakQKhgHOTqQdb0mbMOPkHA5Zw2YRV89FaYc2m+if3rWyaK4z
13
+yk9hSd8/LK71rmYO0YlVDGwWkhUkDAD3q/T20kpeFxECgYEA/Uf5PqKc3Z9k6bTp
14
+3JnfMNrXY4Nk1pE8apZF1Saho8mKsCX0Vm3x4DC8xZgwE5gvMusopHWp/EoCvMet
15
+5/EJpNXzjLZB35IdAiGCYQPLEQTQjmCtsQIRoknhZc9nKPw/iWlev68Yrrf16jkj
16
+9FrvyyFEWQ4wUgQftPC/7u5Ad58CgYEAzyELXIxtXpXdjch0X9L7IRULuQ4Rs7q3
17
+7+TvFYd5P0X2zBBJ7AHjamaru7C+ytnT7bpSudRv36k9KySsHtqGKl9uSJrGGJeW
18
+kqQ9wOorwu9PDVvdL2DxG31bK5b+BcQhqSPYJEVe3FpETJgtbFNIIf9LEVg73v91
19
+iNiNqbwSEDUCgYEAq7poCQrSVwWisz7BrZv6kzJeBY/qB/1TPGWFFZ9qyxV0Xjht
20
+sUg8TihdZY/pUO/HWLvOw6svxOodbwfoJrHsOwIBbu+IPGDiIDa+Iq8iuPhNu6tb
21
+OP/RGvsCwzfblxNotO9nmYnLr3L1Xoi9kwkxOsXkhIk1Q/ad1N3DFOofdbsCgYAq
22
+X2kymqu5INF9MtfTzpZ/Uw3d4qnuabE9S0k5z0gXkJmHb4Gf3VcHqk9RizvMxbkc
23
+NfS8fWARkk6oJ81qVmwB+RnXkooZ99De2OilMYKYU1qJshRSn/NTG1buWOpIhbIZ
24
+JvMNoH9idrjoLm2EbpkgE1jpCHLfEMWbpCl+4rGTTQKBgQCvOqqWpwS0fJFwbx1h
25
+F+jcLf9eQHD5B/kh91f4dvB3Uywi2vP5pkAPkRS2dd6z5ZKNuHCCUYizKDR6d20g
26
+EnGUuN96uyby4vT5Xar1/6IWcJl2VpjHAUjGAKw2PHRA4ti+RO7oo4TP9J/tF/Ec
27
+xoO0p1jYKttMAiKBlw10sNBZmg==
28
+-----END PRIVATE KEY-----

BIN
app/media/uploads/2021/07/25/411452.jpg


BIN
app/media/uploads/2021/07/25/411452_XyreIWY.jpg


BIN
app/media/uploads/2021/07/25/78392.jpg


BIN
app/media/uploads/2021/07/25/78392_KFXeZqU.jpg


BIN
app/media/uploads/2021/07/25/heartbeat.png


BIN
app/media/uploads/2021/07/25/heartbeat_wFHVZDY.png


BIN
app/mycert.cer


+ 3 - 0
app/requirements.txt

@@ -6,3 +6,6 @@ django-colorfield
6 6
 googlemaps
7 7
 django-json-widget
8 8
 django-import-export
9
+haversine
10
+django-autocomplete-light==3.5.1
11
+django-sslserver

BIN
app/shaqfindbed/__pycache__/settings.cpython-39.pyc


BIN
app/shaqfindbed/__pycache__/urls.cpython-39.pyc


+ 9 - 5
app/shaqfindbed/settings.py

@@ -33,19 +33,23 @@ ALLOWED_HOSTS = []
33 33
 # Application definition
34 34
 
35 35
 INSTALLED_APPS = [
36
+    "sslserver",
36 37
     'django.contrib.staticfiles',
38
+    'django.contrib.gis',
39
+    'smart_selects',
40
+    'colorfield',
41
+    'django_json_widget',
42
+    'dal',
43
+    'dal_select2',
44
+    'django_google_maps',
37 45
     'django.contrib.admin',
38 46
     'django.contrib.auth',
39 47
     'django.contrib.contenttypes',
40 48
     'django.contrib.sessions',
41 49
     'django.contrib.messages',
42 50
     'import_export',
43
-    'django_google_maps',
44
-    'django.contrib.gis',
45
-    'smart_selects',
46
-    'colorfield',
47
-    'django_json_widget',
48 51
     'backend.apps.BackendConfig',
52
+    'front.apps.FrontConfig',
49 53
 ]
50 54
 
51 55
 MIDDLEWARE = [

+ 1 - 0
app/shaqfindbed/urls.py

@@ -21,6 +21,7 @@ from django.conf.urls.static import static
21 21
 
22 22
 urlpatterns = [
23 23
     path('backend/', include('backend.urls')),
24
+    path('front/', include('front.urls')),
24 25
     path('admin/', admin.site.urls),
25 26
     url(r'^chaining/', include('smart_selects.urls')),
26 27
 ] + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)

BIN
app/static/.DS_Store


+ 2 - 2
app/static/admin/css/forms.css

@@ -527,10 +527,10 @@ form .related-widget-wrapper ul {
527 527
 .form-row .field-get_map .readonly {
528 528
     margin:0px;
529 529
 }
530
-.field-get_map label, .field-nearby_places label {
530
+.field-get_map label, .field-nearby_places label, .field-nearby_from_db label{
531 531
     float:initial !important;
532 532
 }
533
-.field-get_map  .readonly, .field-nearby_places .readonly{
533
+.field-get_map  .readonly, .field-nearby_places .readonly, .field-nearby_from_db .readonly{
534 534
     margin-left:0px !important;
535 535
     overflow:auto;
536 536
 }

BIN
app/static/django_google_maps/.DS_Store


+ 11 - 0
app/static/django_google_maps/css/google-maps-admin.css

@@ -0,0 +1,11 @@
1
+@media (min-width: 848px) {
2
+    .map_canvas_wrapper {margin-left: 170px;}
3
+    .main.shifted .map_canvas_wrapper {margin-left: 0;}
4
+}
5
+@media (min-width: 1118px) {
6
+    .main.shifted .map_canvas_wrapper {margin-left: 170px;}
7
+}
8
+
9
+#id_address {width: 40em;}
10
+.map_canvas_wrapper {width: 100%;}
11
+#map_canvas {width: 100%; height: 40em;}

+ 180 - 0
app/static/django_google_maps/js/google-maps-admin.js

@@ -0,0 +1,180 @@
1
+
2
+/*
3
+Integration for Google Maps in the django admin.
4
+
5
+How it works:
6
+
7
+You have an address field on the page.
8
+Enter an address and an on change event will update the map
9
+with the address. A marker will be placed at the address.
10
+If the user needs to move the marker, they can and the geolocation
11
+field will be updated.
12
+
13
+Only one marker will remain present on the map at a time.
14
+
15
+This script expects:
16
+
17
+<input type="text" name="address" id="id_address" />
18
+<input type="text" name="geolocation" id="id_geolocation" />
19
+
20
+<script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=false"></script>
21
+
22
+*/
23
+
24
+function googleMapAdmin() {
25
+
26
+    var autocomplete;
27
+    var geocoder = new google.maps.Geocoder();
28
+    var map;
29
+    var marker;
30
+
31
+    var geolocationId = 'id_geolocation';
32
+    var addressId = 'id_address';
33
+
34
+    var self = {
35
+        initialize: function() {
36
+            var lat = 0;
37
+            var lng = 0;
38
+            var zoom = 2;
39
+            // set up initial map to be world view. also, add change
40
+            // event so changing address will update the map
41
+            var existinglocation = self.getExistingLocation();
42
+
43
+            if (existinglocation) {
44
+                lat = existinglocation[0];
45
+                lng = existinglocation[1];
46
+                zoom = 18;
47
+            }
48
+
49
+            var latlng = new google.maps.LatLng(lat,lng);
50
+            var myOptions = {
51
+              zoom: zoom,
52
+              center: latlng,
53
+              mapTypeId: self.getMapType()
54
+            };
55
+            map = new google.maps.Map(document.getElementById("map_canvas"), myOptions);
56
+            if (existinglocation) {
57
+                self.setMarker(latlng);
58
+            }
59
+
60
+            autocomplete = new google.maps.places.Autocomplete(
61
+                /** @type {!HTMLInputElement} */(document.getElementById(addressId)),
62
+                self.getAutoCompleteOptions());
63
+
64
+            // this only triggers on enter, or if a suggested location is chosen
65
+            // todo: if a user doesn't choose a suggestion and presses tab, the map doesn't update
66
+            autocomplete.addListener("place_changed", self.codeAddress);
67
+
68
+            // don't make enter submit the form, let it just trigger the place_changed event
69
+            // which triggers the map update & geocode
70
+            $("#" + addressId).keydown(function (e) {
71
+                if (e.keyCode == 13) {  // enter key
72
+                    e.preventDefault();
73
+                    return false;
74
+                }
75
+            });
76
+        },
77
+
78
+        getMapType : function() {
79
+            // https://developers.google.com/maps/documentation/javascript/maptypes
80
+            var geolocation = document.getElementById(addressId);
81
+            var allowedType = ['roadmap', 'satellite', 'hybrid', 'terrain'];
82
+            var mapType = geolocation.getAttribute('data-map-type');
83
+
84
+            if (mapType && -1 !== allowedType.indexOf(mapType)) {
85
+                return mapType;
86
+            }
87
+
88
+            return google.maps.MapTypeId.HYBRID;
89
+        },
90
+
91
+        getAutoCompleteOptions : function() {
92
+            var geolocation = document.getElementById(addressId);
93
+            var autocompleteOptions = geolocation.getAttribute('data-autocomplete-options');
94
+
95
+            if (!autocompleteOptions) {
96
+                return {
97
+                   types: ['geocode']
98
+                };
99
+            }
100
+
101
+            return JSON.parse(autocompleteOptions);
102
+        },
103
+
104
+        getExistingLocation: function() {
105
+            var geolocation = document.getElementById(geolocationId).value;
106
+            if (geolocation) {
107
+                return geolocation.split(',');
108
+            }
109
+        },
110
+
111
+        codeAddress: function() {
112
+            var place = autocomplete.getPlace();
113
+
114
+            if(place.geometry !== undefined) {
115
+                self.updateWithCoordinates(place.geometry.location);
116
+            }
117
+            else {
118
+                geocoder.geocode({'address': place.name}, function(results, status) {
119
+                    if (status == google.maps.GeocoderStatus.OK) {
120
+                        var latlng = results[0].geometry.location;
121
+                        self.updateWithCoordinates(latlng);
122
+                    } else {
123
+                        alert("Geocode was not successful for the following reason: " + status);
124
+                    }
125
+                });
126
+            }
127
+        },
128
+
129
+        updateWithCoordinates: function(latlng) {
130
+            map.setCenter(latlng);
131
+            map.setZoom(18);
132
+            self.setMarker(latlng);
133
+            self.updateGeolocation(latlng);
134
+        },
135
+
136
+        setMarker: function(latlng) {
137
+            if (marker) {
138
+                self.updateMarker(latlng);
139
+            } else {
140
+                self.addMarker({'latlng': latlng, 'draggable': true});
141
+            }
142
+        },
143
+
144
+        addMarker: function(Options) {
145
+            marker = new google.maps.Marker({
146
+                map: map,
147
+                position: Options.latlng
148
+            });
149
+
150
+            var draggable = Options.draggable || false;
151
+            if (draggable) {
152
+                self.addMarkerDrag(marker);
153
+            }
154
+        },
155
+
156
+        addMarkerDrag: function() {
157
+            marker.setDraggable(true);
158
+            google.maps.event.addListener(marker, 'dragend', function(new_location) {
159
+                self.updateGeolocation(new_location.latLng);
160
+            });
161
+        },
162
+
163
+        updateMarker: function(latlng) {
164
+            marker.setPosition(latlng);
165
+        },
166
+
167
+        updateGeolocation: function(latlng) {
168
+            document.getElementById(geolocationId).value = latlng.lat() + "," + latlng.lng();
169
+            $("#" + geolocationId).trigger('change');
170
+        }
171
+    };
172
+
173
+    return self;
174
+}
175
+
176
+//$(document).ready(function() {
177
+jQuery(function($) {
178
+    var googlemap = googleMapAdmin();
179
+    googlemap.initialize();
180
+});

BIN
app/static/img/heartbeat.png


+ 15 - 0
app/static/js/main.js

@@ -0,0 +1,15 @@
1
+$(function(){
2
+    $("#currentLocationBtn").click(function(){
3
+        if ("geolocation" in navigator){ //check geolocation available
4
+            //try to get user current location using getCurrentPosition() method
5
+            console.log("current location");
6
+            navigator.geolocation.getCurrentPosition(function(position){
7
+                console.log("xxxx");
8
+                $("#geoText").val(position.coords.latitude+","+position.coords.longitude );
9
+
10
+            });
11
+        }else{
12
+            console.log("Browser doesn't support geolocation!");
13
+        }
14
+    });
15
+});

+ 422 - 0
app/stunnel.log

@@ -0,0 +1,422 @@
1
+2021.07.24 09:02:55 LOG5[ui]: stunnel 5.50 on aarch64-unknown-linux-gnu platform
2
+2021.07.24 09:02:55 LOG5[ui]: Compiled with OpenSSL 1.1.1b  26 Feb 2019
3
+2021.07.24 09:02:55 LOG5[ui]: Running  with OpenSSL 1.1.1d  10 Sep 2019
4
+2021.07.24 09:02:55 LOG5[ui]: Threading:PTHREAD Sockets:POLL,IPv6,SYSTEMD TLS:ENGINE,FIPS,OCSP,PSK,SNI Auth:LIBWRAP
5
+2021.07.24 09:02:55 LOG5[ui]: Reading configuration from file /code/stunnel/dev_https
6
+2021.07.24 09:02:55 LOG5[ui]: UTF-8 byte order mark not detected
7
+2021.07.24 09:02:55 LOG5[ui]: FIPS mode disabled
8
+2021.07.24 09:02:55 LOG4[ui]: Insecure file permissions on stunnel/stunnel.pem
9
+2021.07.24 09:02:55 LOG5[ui]: Configuration successful
10
+2021.07.24 09:10:14 LOG5[ui]: stunnel 5.50 on aarch64-unknown-linux-gnu platform
11
+2021.07.24 09:10:14 LOG5[ui]: Compiled with OpenSSL 1.1.1b  26 Feb 2019
12
+2021.07.24 09:10:14 LOG5[ui]: Running  with OpenSSL 1.1.1d  10 Sep 2019
13
+2021.07.24 09:10:14 LOG5[ui]: Threading:PTHREAD Sockets:POLL,IPv6,SYSTEMD TLS:ENGINE,FIPS,OCSP,PSK,SNI Auth:LIBWRAP
14
+2021.07.24 09:10:14 LOG5[ui]: Reading configuration from file /code/stunnel/dev_https
15
+2021.07.24 09:10:14 LOG5[ui]: UTF-8 byte order mark not detected
16
+2021.07.24 09:10:14 LOG5[ui]: FIPS mode disabled
17
+2021.07.24 09:10:14 LOG4[ui]: Insecure file permissions on stunnel/stunnel.pem
18
+2021.07.24 09:10:14 LOG5[ui]: Configuration successful
19
+2021.07.24 09:33:28 LOG5[ui]: stunnel 5.50 on aarch64-unknown-linux-gnu platform
20
+2021.07.24 09:33:28 LOG5[ui]: Compiled with OpenSSL 1.1.1b  26 Feb 2019
21
+2021.07.24 09:33:28 LOG5[ui]: Running  with OpenSSL 1.1.1d  10 Sep 2019
22
+2021.07.24 09:33:28 LOG5[ui]: Threading:PTHREAD Sockets:POLL,IPv6,SYSTEMD TLS:ENGINE,FIPS,OCSP,PSK,SNI Auth:LIBWRAP
23
+2021.07.24 09:33:28 LOG5[ui]: Reading configuration from file /code/stunnel/dev_https
24
+2021.07.24 09:33:28 LOG5[ui]: UTF-8 byte order mark not detected
25
+2021.07.24 09:33:28 LOG5[ui]: FIPS mode disabled
26
+2021.07.24 09:33:28 LOG4[ui]: Insecure file permissions on stunnel/stunnel.pem
27
+2021.07.24 09:33:28 LOG5[ui]: Configuration successful
28
+2021.07.24 09:35:04 LOG5[0]: Service [https] accepted connection from 172.18.0.1:60024
29
+2021.07.24 09:35:04 LOG5[1]: Service [https] accepted connection from 172.18.0.1:60028
30
+2021.07.24 09:35:04 LOG3[0]: SSL_accept: 141FC044: error:141FC044:SSL routines:tls_setup_handshake:internal error
31
+2021.07.24 09:35:04 LOG3[1]: SSL_accept: 141FC044: error:141FC044:SSL routines:tls_setup_handshake:internal error
32
+2021.07.24 09:35:04 LOG5[1]: Connection reset: 0 byte(s) sent to TLS, 0 byte(s) sent to socket
33
+2021.07.24 09:35:04 LOG5[0]: Connection reset: 0 byte(s) sent to TLS, 0 byte(s) sent to socket
34
+2021.07.24 09:35:05 LOG5[2]: Service [https] accepted connection from 172.18.0.1:60032
35
+2021.07.24 09:35:05 LOG3[2]: SSL_accept: 141FC044: error:141FC044:SSL routines:tls_setup_handshake:internal error
36
+2021.07.24 09:35:05 LOG5[2]: Connection reset: 0 byte(s) sent to TLS, 0 byte(s) sent to socket
37
+2021.07.24 09:35:10 LOG5[3]: Service [https] accepted connection from 172.18.0.1:60036
38
+2021.07.24 09:35:10 LOG3[3]: SSL_accept: 141FC044: error:141FC044:SSL routines:tls_setup_handshake:internal error
39
+2021.07.24 09:35:10 LOG5[3]: Connection reset: 0 byte(s) sent to TLS, 0 byte(s) sent to socket
40
+2021.07.24 09:35:26 LOG5[4]: Service [https] accepted connection from 172.18.0.1:60040
41
+2021.07.24 09:35:26 LOG3[4]: SSL_accept: 141FC044: error:141FC044:SSL routines:tls_setup_handshake:internal error
42
+2021.07.24 09:35:26 LOG5[4]: Connection reset: 0 byte(s) sent to TLS, 0 byte(s) sent to socket
43
+2021.07.24 09:35:31 LOG5[5]: Service [https] accepted connection from 172.18.0.1:60044
44
+2021.07.24 09:35:31 LOG3[5]: SSL_accept: 141FC044: error:141FC044:SSL routines:tls_setup_handshake:internal error
45
+2021.07.24 09:35:31 LOG5[5]: Connection reset: 0 byte(s) sent to TLS, 0 byte(s) sent to socket
46
+2021.07.24 09:35:34 LOG5[6]: Service [https] accepted connection from 172.18.0.1:60048
47
+2021.07.24 09:35:34 LOG3[6]: SSL_accept: 141FC044: error:141FC044:SSL routines:tls_setup_handshake:internal error
48
+2021.07.24 09:35:34 LOG5[6]: Connection reset: 0 byte(s) sent to TLS, 0 byte(s) sent to socket
49
+2021.07.24 09:35:35 LOG5[7]: Service [https] accepted connection from 172.18.0.1:60052
50
+2021.07.24 09:35:35 LOG3[7]: SSL_accept: 141FC044: error:141FC044:SSL routines:tls_setup_handshake:internal error
51
+2021.07.24 09:35:35 LOG5[7]: Connection reset: 0 byte(s) sent to TLS, 0 byte(s) sent to socket
52
+2021.07.24 09:35:37 LOG5[8]: Service [https] accepted connection from 172.18.0.1:60056
53
+2021.07.24 09:35:37 LOG3[8]: SSL_accept: 141FC044: error:141FC044:SSL routines:tls_setup_handshake:internal error
54
+2021.07.24 09:35:37 LOG5[8]: Connection reset: 0 byte(s) sent to TLS, 0 byte(s) sent to socket
55
+2021.07.24 09:35:38 LOG5[9]: Service [https] accepted connection from 172.18.0.1:60060
56
+2021.07.24 09:35:38 LOG3[9]: SSL_accept: 141FC044: error:141FC044:SSL routines:tls_setup_handshake:internal error
57
+2021.07.24 09:35:38 LOG5[9]: Connection reset: 0 byte(s) sent to TLS, 0 byte(s) sent to socket
58
+2021.07.24 09:35:43 LOG5[10]: Service [https] accepted connection from 172.18.0.1:60064
59
+2021.07.24 09:35:43 LOG3[10]: SSL_accept: 141FC044: error:141FC044:SSL routines:tls_setup_handshake:internal error
60
+2021.07.24 09:35:43 LOG5[10]: Connection reset: 0 byte(s) sent to TLS, 0 byte(s) sent to socket
61
+2021.07.24 09:36:01 LOG5[12]: Service [https] accepted connection from 172.18.0.1:60070
62
+2021.07.24 09:36:01 LOG3[12]: SSL_accept: 141FC044: error:141FC044:SSL routines:tls_setup_handshake:internal error
63
+2021.07.24 09:36:01 LOG5[11]: Service [https] accepted connection from 172.18.0.1:60072
64
+2021.07.24 09:36:01 LOG5[12]: Connection reset: 0 byte(s) sent to TLS, 0 byte(s) sent to socket
65
+2021.07.24 09:36:01 LOG5[14]: Service [https] accepted connection from 172.18.0.1:60080
66
+2021.07.24 09:36:01 LOG5[13]: Service [https] accepted connection from 172.18.0.1:60076
67
+2021.07.24 09:36:01 LOG3[11]: SSL_accept: 141FC044: error:141FC044:SSL routines:tls_setup_handshake:internal error
68
+2021.07.24 09:36:01 LOG3[14]: SSL_accept: 141FC044: error:141FC044:SSL routines:tls_setup_handshake:internal error
69
+2021.07.24 09:36:01 LOG3[13]: SSL_accept: 141FC044: error:141FC044:SSL routines:tls_setup_handshake:internal error
70
+2021.07.24 09:36:01 LOG5[11]: Connection reset: 0 byte(s) sent to TLS, 0 byte(s) sent to socket
71
+2021.07.24 09:36:01 LOG5[13]: Connection reset: 0 byte(s) sent to TLS, 0 byte(s) sent to socket
72
+2021.07.24 09:36:01 LOG5[14]: Connection reset: 0 byte(s) sent to TLS, 0 byte(s) sent to socket
73
+2021.07.24 09:36:39 LOG5[16]: Service [https] accepted connection from 172.18.0.1:60122
74
+2021.07.24 09:36:39 LOG3[16]: SSL_accept: 141FC044: error:141FC044:SSL routines:tls_setup_handshake:internal error
75
+2021.07.24 09:36:39 LOG5[15]: Service [https] accepted connection from 172.18.0.1:60120
76
+2021.07.24 09:36:39 LOG5[16]: Connection reset: 0 byte(s) sent to TLS, 0 byte(s) sent to socket
77
+2021.07.24 09:36:39 LOG5[17]: Service [https] accepted connection from 172.18.0.1:60126
78
+2021.07.24 09:36:39 LOG3[15]: SSL_accept: 141FC044: error:141FC044:SSL routines:tls_setup_handshake:internal error
79
+2021.07.24 09:36:39 LOG5[18]: Service [https] accepted connection from 172.18.0.1:60130
80
+2021.07.24 09:36:39 LOG3[17]: SSL_accept: 141FC044: error:141FC044:SSL routines:tls_setup_handshake:internal error
81
+2021.07.24 09:36:39 LOG5[17]: Connection reset: 0 byte(s) sent to TLS, 0 byte(s) sent to socket
82
+2021.07.24 09:36:39 LOG5[15]: Connection reset: 0 byte(s) sent to TLS, 0 byte(s) sent to socket
83
+2021.07.24 09:36:39 LOG3[18]: SSL_accept: 141FC044: error:141FC044:SSL routines:tls_setup_handshake:internal error
84
+2021.07.24 09:36:39 LOG5[18]: Connection reset: 0 byte(s) sent to TLS, 0 byte(s) sent to socket
85
+2021.07.24 09:37:05 LOG5[ui]: Terminated
86
+2021.07.24 09:37:21 LOG5[ui]: stunnel 5.50 on aarch64-unknown-linux-gnu platform
87
+2021.07.24 09:37:21 LOG5[ui]: Compiled with OpenSSL 1.1.1b  26 Feb 2019
88
+2021.07.24 09:37:21 LOG5[ui]: Running  with OpenSSL 1.1.1d  10 Sep 2019
89
+2021.07.24 09:37:21 LOG5[ui]: Threading:PTHREAD Sockets:POLL,IPv6,SYSTEMD TLS:ENGINE,FIPS,OCSP,PSK,SNI Auth:LIBWRAP
90
+2021.07.24 09:37:21 LOG5[ui]: Reading configuration from file /code/stunnel/dev_https
91
+2021.07.24 09:37:21 LOG5[ui]: UTF-8 byte order mark not detected
92
+2021.07.24 09:37:21 LOG5[ui]: FIPS mode disabled
93
+2021.07.24 09:37:21 LOG4[ui]: Insecure file permissions on stunnel/stunnel.pem
94
+2021.07.24 09:37:21 LOG5[ui]: Configuration successful
95
+2021.07.24 09:37:40 LOG5[1]: Service [https] accepted connection from 172.18.0.1:59516
96
+2021.07.24 09:37:40 LOG5[0]: Service [https] accepted connection from 172.18.0.1:59518
97
+2021.07.24 09:37:40 LOG3[1]: SSL_accept: 141FC044: error:141FC044:SSL routines:tls_setup_handshake:internal error
98
+2021.07.24 09:37:40 LOG5[1]: Connection reset: 0 byte(s) sent to TLS, 0 byte(s) sent to socket
99
+2021.07.24 09:37:40 LOG3[0]: SSL_accept: 141FC044: error:141FC044:SSL routines:tls_setup_handshake:internal error
100
+2021.07.24 09:37:40 LOG5[2]: Service [https] accepted connection from 172.18.0.1:59524
101
+2021.07.24 09:37:40 LOG3[2]: SSL_accept: 141FC044: error:141FC044:SSL routines:tls_setup_handshake:internal error
102
+2021.07.24 09:37:40 LOG5[3]: Service [https] accepted connection from 172.18.0.1:59528
103
+2021.07.24 09:37:40 LOG5[0]: Connection reset: 0 byte(s) sent to TLS, 0 byte(s) sent to socket
104
+2021.07.24 09:37:40 LOG5[2]: Connection reset: 0 byte(s) sent to TLS, 0 byte(s) sent to socket
105
+2021.07.24 09:37:40 LOG3[3]: SSL_accept: 141FC044: error:141FC044:SSL routines:tls_setup_handshake:internal error
106
+2021.07.24 09:37:40 LOG5[3]: Connection reset: 0 byte(s) sent to TLS, 0 byte(s) sent to socket
107
+2021.07.24 09:37:51 LOG5[5]: Service [https] accepted connection from 172.18.0.1:59536
108
+2021.07.24 09:37:51 LOG5[4]: Service [https] accepted connection from 172.18.0.1:59538
109
+2021.07.24 09:37:51 LOG3[5]: SSL_accept: 141FC044: error:141FC044:SSL routines:tls_setup_handshake:internal error
110
+2021.07.24 09:37:51 LOG5[5]: Connection reset: 0 byte(s) sent to TLS, 0 byte(s) sent to socket
111
+2021.07.24 09:37:51 LOG3[4]: SSL_accept: 141FC044: error:141FC044:SSL routines:tls_setup_handshake:internal error
112
+2021.07.24 09:37:51 LOG5[6]: Service [https] accepted connection from 172.18.0.1:59542
113
+2021.07.24 09:37:51 LOG5[7]: Service [https] accepted connection from 172.18.0.1:59546
114
+2021.07.24 09:37:51 LOG5[4]: Connection reset: 0 byte(s) sent to TLS, 0 byte(s) sent to socket
115
+2021.07.24 09:37:51 LOG3[6]: SSL_accept: 141FC044: error:141FC044:SSL routines:tls_setup_handshake:internal error
116
+2021.07.24 09:37:51 LOG5[6]: Connection reset: 0 byte(s) sent to TLS, 0 byte(s) sent to socket
117
+2021.07.24 09:37:51 LOG3[7]: SSL_accept: 141FC044: error:141FC044:SSL routines:tls_setup_handshake:internal error
118
+2021.07.24 09:37:51 LOG5[7]: Connection reset: 0 byte(s) sent to TLS, 0 byte(s) sent to socket
119
+2021.07.24 09:39:11 LOG5[ui]: stunnel 5.50 on aarch64-unknown-linux-gnu platform
120
+2021.07.24 09:39:11 LOG5[ui]: Compiled with OpenSSL 1.1.1b  26 Feb 2019
121
+2021.07.24 09:39:11 LOG5[ui]: Running  with OpenSSL 1.1.1d  10 Sep 2019
122
+2021.07.24 09:39:11 LOG5[ui]: Threading:PTHREAD Sockets:POLL,IPv6,SYSTEMD TLS:ENGINE,FIPS,OCSP,PSK,SNI Auth:LIBWRAP
123
+2021.07.24 09:39:11 LOG5[ui]: Reading configuration from file /code/stunnel/dev_https
124
+2021.07.24 09:39:11 LOG5[ui]: UTF-8 byte order mark not detected
125
+2021.07.24 09:39:11 LOG5[ui]: FIPS mode disabled
126
+2021.07.24 09:39:11 LOG4[ui]: Insecure file permissions on stunnel/stunnel.pem
127
+2021.07.24 09:39:11 LOG5[ui]: Configuration successful
128
+2021.07.24 09:39:23 LOG5[1]: Service [https] accepted connection from 172.18.0.1:60180
129
+2021.07.24 09:39:23 LOG5[0]: Service [https] accepted connection from 172.18.0.1:60182
130
+2021.07.24 09:39:23 LOG3[1]: SSL_accept: 14094416: error:14094416:SSL routines:ssl3_read_bytes:sslv3 alert certificate unknown
131
+2021.07.24 09:39:23 LOG3[0]: SSL_accept: 14094416: error:14094416:SSL routines:ssl3_read_bytes:sslv3 alert certificate unknown
132
+2021.07.24 09:39:23 LOG5[1]: Connection reset: 0 byte(s) sent to TLS, 0 byte(s) sent to socket
133
+2021.07.24 09:39:23 LOG5[0]: Connection reset: 0 byte(s) sent to TLS, 0 byte(s) sent to socket
134
+2021.07.24 09:39:42 LOG5[3]: Service [https] accepted connection from 172.18.0.1:60190
135
+2021.07.24 09:39:42 LOG5[2]: Service [https] accepted connection from 172.18.0.1:60192
136
+2021.07.24 09:39:42 LOG3[3]: SSL_accept: 14094416: error:14094416:SSL routines:ssl3_read_bytes:sslv3 alert certificate unknown
137
+2021.07.24 09:39:42 LOG5[3]: Connection reset: 0 byte(s) sent to TLS, 0 byte(s) sent to socket
138
+2021.07.24 09:39:42 LOG3[2]: SSL_accept: 14094416: error:14094416:SSL routines:ssl3_read_bytes:sslv3 alert certificate unknown
139
+2021.07.24 09:39:42 LOG5[2]: Connection reset: 0 byte(s) sent to TLS, 0 byte(s) sent to socket
140
+2021.07.24 09:39:57 LOG5[ui]: Terminated
141
+2021.07.24 09:40:13 LOG5[ui]: stunnel 5.50 on aarch64-unknown-linux-gnu platform
142
+2021.07.24 09:40:13 LOG5[ui]: Compiled with OpenSSL 1.1.1b  26 Feb 2019
143
+2021.07.24 09:40:13 LOG5[ui]: Running  with OpenSSL 1.1.1d  10 Sep 2019
144
+2021.07.24 09:40:13 LOG5[ui]: Threading:PTHREAD Sockets:POLL,IPv6,SYSTEMD TLS:ENGINE,FIPS,OCSP,PSK,SNI Auth:LIBWRAP
145
+2021.07.24 09:40:13 LOG5[ui]: Reading configuration from file /code/stunnel/dev_https
146
+2021.07.24 09:40:13 LOG5[ui]: UTF-8 byte order mark not detected
147
+2021.07.24 09:40:13 LOG5[ui]: FIPS mode disabled
148
+2021.07.24 09:40:13 LOG4[ui]: Insecure file permissions on stunnel/stunnel.pem
149
+2021.07.24 09:40:13 LOG5[ui]: Configuration successful
150
+2021.07.24 09:40:19 LOG5[0]: Service [https] accepted connection from 172.18.0.1:60204
151
+2021.07.24 09:40:19 LOG5[1]: Service [https] accepted connection from 172.18.0.1:60206
152
+2021.07.24 09:40:19 LOG3[0]: SSL_accept: 14094416: error:14094416:SSL routines:ssl3_read_bytes:sslv3 alert certificate unknown
153
+2021.07.24 09:40:19 LOG3[1]: SSL_accept: 14094416: error:14094416:SSL routines:ssl3_read_bytes:sslv3 alert certificate unknown
154
+2021.07.24 09:40:19 LOG5[1]: Connection reset: 0 byte(s) sent to TLS, 0 byte(s) sent to socket
155
+2021.07.24 09:40:19 LOG5[0]: Connection reset: 0 byte(s) sent to TLS, 0 byte(s) sent to socket
156
+2021.07.24 09:40:35 LOG5[2]: Service [https] accepted connection from 172.18.0.1:60212
157
+2021.07.24 09:40:35 LOG5[3]: Service [https] accepted connection from 172.18.0.1:60214
158
+2021.07.24 09:40:35 LOG3[3]: SSL_accept: 1408F09C: error:1408F09C:SSL routines:ssl3_get_record:http request
159
+2021.07.24 09:40:35 LOG5[3]: Connection reset: 0 byte(s) sent to TLS, 0 byte(s) sent to socket
160
+2021.07.24 09:40:35 LOG3[2]: SSL_accept: 1408F09C: error:1408F09C:SSL routines:ssl3_get_record:http request
161
+2021.07.24 09:40:35 LOG5[2]: Connection reset: 0 byte(s) sent to TLS, 0 byte(s) sent to socket
162
+2021.07.24 09:40:35 LOG5[4]: Service [https] accepted connection from 172.18.0.1:60218
163
+2021.07.24 09:40:35 LOG3[4]: SSL_accept: 1408F09C: error:1408F09C:SSL routines:ssl3_get_record:http request
164
+2021.07.24 09:40:35 LOG5[4]: Connection reset: 0 byte(s) sent to TLS, 0 byte(s) sent to socket
165
+2021.07.24 09:40:36 LOG5[6]: Service [https] accepted connection from 172.18.0.1:60226
166
+2021.07.24 09:40:36 LOG5[5]: Service [https] accepted connection from 172.18.0.1:60224
167
+2021.07.24 09:40:36 LOG3[5]: SSL_accept: 1408F09C: error:1408F09C:SSL routines:ssl3_get_record:http request
168
+2021.07.24 09:40:36 LOG5[5]: Connection reset: 0 byte(s) sent to TLS, 0 byte(s) sent to socket
169
+2021.07.24 09:40:36 LOG3[6]: SSL_accept: 1408F09C: error:1408F09C:SSL routines:ssl3_get_record:http request
170
+2021.07.24 09:40:36 LOG5[6]: Connection reset: 0 byte(s) sent to TLS, 0 byte(s) sent to socket
171
+2021.07.24 09:40:36 LOG5[7]: Service [https] accepted connection from 172.18.0.1:60230
172
+2021.07.24 09:40:36 LOG3[7]: SSL_accept: 1408F09C: error:1408F09C:SSL routines:ssl3_get_record:http request
173
+2021.07.24 09:40:36 LOG5[7]: Connection reset: 0 byte(s) sent to TLS, 0 byte(s) sent to socket
174
+2021.07.24 09:40:41 LOG5[9]: Service [https] accepted connection from 172.18.0.1:60240
175
+2021.07.24 09:40:41 LOG5[8]: Service [https] accepted connection from 172.18.0.1:60238
176
+2021.07.24 09:40:41 LOG3[9]: SSL_accept: 1408F09C: error:1408F09C:SSL routines:ssl3_get_record:http request
177
+2021.07.24 09:40:41 LOG5[9]: Connection reset: 0 byte(s) sent to TLS, 0 byte(s) sent to socket
178
+2021.07.24 09:40:42 LOG5[10]: Service [https] accepted connection from 172.18.0.1:60248
179
+2021.07.24 09:40:42 LOG5[11]: Service [https] accepted connection from 172.18.0.1:60250
180
+2021.07.24 09:40:42 LOG3[10]: SSL_accept: 14094416: error:14094416:SSL routines:ssl3_read_bytes:sslv3 alert certificate unknown
181
+2021.07.24 09:40:42 LOG5[10]: Connection reset: 0 byte(s) sent to TLS, 0 byte(s) sent to socket
182
+2021.07.24 09:40:42 LOG3[11]: SSL_accept: 14094416: error:14094416:SSL routines:ssl3_read_bytes:sslv3 alert certificate unknown
183
+2021.07.24 09:40:42 LOG5[11]: Connection reset: 0 byte(s) sent to TLS, 0 byte(s) sent to socket
184
+2021.07.24 09:40:49 LOG5[ui]: Terminated
185
+2021.07.24 09:43:36 LOG5[ui]: stunnel 5.50 on aarch64-unknown-linux-gnu platform
186
+2021.07.24 09:43:36 LOG5[ui]: Compiled with OpenSSL 1.1.1b  26 Feb 2019
187
+2021.07.24 09:43:36 LOG5[ui]: Running  with OpenSSL 1.1.1d  10 Sep 2019
188
+2021.07.24 09:43:36 LOG5[ui]: Threading:PTHREAD Sockets:POLL,IPv6,SYSTEMD TLS:ENGINE,FIPS,OCSP,PSK,SNI Auth:LIBWRAP
189
+2021.07.24 09:43:36 LOG5[ui]: Reading configuration from file /code/stunnel/dev_https
190
+2021.07.24 09:43:36 LOG5[ui]: UTF-8 byte order mark not detected
191
+2021.07.24 09:43:36 LOG5[ui]: FIPS mode disabled
192
+2021.07.24 09:43:36 LOG4[ui]: Insecure file permissions on stunnel/stunnel.pem
193
+2021.07.24 09:43:36 LOG5[ui]: Configuration successful
194
+2021.07.24 09:45:21 LOG3[ui]: Received SIGINT; terminating
195
+2021.07.24 09:45:40 LOG5[ui]: stunnel 5.50 on aarch64-unknown-linux-gnu platform
196
+2021.07.24 09:45:40 LOG5[ui]: Compiled with OpenSSL 1.1.1b  26 Feb 2019
197
+2021.07.24 09:45:40 LOG5[ui]: Running  with OpenSSL 1.1.1d  10 Sep 2019
198
+2021.07.24 09:45:40 LOG5[ui]: Threading:PTHREAD Sockets:POLL,IPv6,SYSTEMD TLS:ENGINE,FIPS,OCSP,PSK,SNI Auth:LIBWRAP
199
+2021.07.24 09:45:40 LOG5[ui]: Reading configuration from file /code/stunnel/dev_https
200
+2021.07.24 09:45:40 LOG5[ui]: UTF-8 byte order mark not detected
201
+2021.07.24 09:45:40 LOG5[ui]: FIPS mode disabled
202
+2021.07.24 09:45:40 LOG4[ui]: Insecure file permissions on stunnel/stunnel.pem
203
+2021.07.24 09:45:40 LOG5[ui]: Configuration successful
204
+2021.07.24 09:46:18 LOG3[ui]: Received SIGINT; terminating
205
+2021.07.24 09:46:35 LOG5[ui]: stunnel 5.50 on aarch64-unknown-linux-gnu platform
206
+2021.07.24 09:46:35 LOG5[ui]: Compiled with OpenSSL 1.1.1b  26 Feb 2019
207
+2021.07.24 09:46:35 LOG5[ui]: Running  with OpenSSL 1.1.1d  10 Sep 2019
208
+2021.07.24 09:46:35 LOG5[ui]: Threading:PTHREAD Sockets:POLL,IPv6,SYSTEMD TLS:ENGINE,FIPS,OCSP,PSK,SNI Auth:LIBWRAP
209
+2021.07.24 09:46:35 LOG5[ui]: Reading configuration from file /code/stunnel/dev_https
210
+2021.07.24 09:46:35 LOG5[ui]: UTF-8 byte order mark not detected
211
+2021.07.24 09:46:35 LOG5[ui]: FIPS mode disabled
212
+2021.07.24 09:46:35 LOG4[ui]: Insecure file permissions on stunnel/stunnel.pem
213
+2021.07.24 09:46:35 LOG5[ui]: Configuration successful
214
+2021.07.24 09:47:13 LOG3[ui]: Received SIGINT; terminating
215
+2021.07.24 09:47:18 LOG5[ui]: stunnel 5.50 on aarch64-unknown-linux-gnu platform
216
+2021.07.24 09:47:18 LOG5[ui]: Compiled with OpenSSL 1.1.1b  26 Feb 2019
217
+2021.07.24 09:47:18 LOG5[ui]: Running  with OpenSSL 1.1.1d  10 Sep 2019
218
+2021.07.24 09:47:18 LOG5[ui]: Threading:PTHREAD Sockets:POLL,IPv6,SYSTEMD TLS:ENGINE,FIPS,OCSP,PSK,SNI Auth:LIBWRAP
219
+2021.07.24 09:47:18 LOG5[ui]: Reading configuration from file /code/stunnel/dev_https
220
+2021.07.24 09:47:18 LOG5[ui]: UTF-8 byte order mark not detected
221
+2021.07.24 09:47:18 LOG5[ui]: FIPS mode disabled
222
+2021.07.24 09:47:18 LOG4[ui]: Insecure file permissions on stunnel/stunnel.pem
223
+2021.07.24 09:47:18 LOG5[ui]: Configuration successful
224
+2021.07.24 09:48:29 LOG5[ui]: Terminated
225
+2021.07.24 16:51:14 LOG5[ui]: stunnel 5.59 on x86_64-apple-darwin20.3.0 platform
226
+2021.07.24 16:51:14 LOG5[ui]: Compiled/running with OpenSSL 1.1.1k  25 Mar 2021
227
+2021.07.24 16:51:14 LOG5[ui]: Threading:PTHREAD Sockets:POLL,IPv6 TLS:ENGINE,OCSP,PSK,SNI
228
+2021.07.24 16:51:14 LOG5[ui]: Reading configuration from file /Users/simplicoltd./projects/shaqFindBed/app/stunnel/dev_https
229
+2021.07.24 16:51:14 LOG5[ui]: UTF-8 byte order mark not detected
230
+2021.07.24 16:51:14 LOG5[ui]: FIPS mode disabled
231
+2021.07.24 16:51:14 LOG4[ui]: Insecure file permissions on stunnel/stunnel.pem
232
+2021.07.24 16:51:14 LOG5[ui]: Configuration successful
233
+2021.07.24 16:51:14 LOG5[ui]: Binding service [https] to 0.0.0.0:8443: Address already in use (48)
234
+2021.07.24 16:51:21 LOG3[ui]: Received SIGINT; terminating
235
+2021.07.24 16:51:21 LOG5[ui]: Terminating 1 service thread(s)
236
+2021.07.24 16:51:21 LOG5[ui]: Service threads terminated
237
+2021.07.24 16:51:27 LOG5[ui]: stunnel 5.59 on x86_64-apple-darwin20.3.0 platform
238
+2021.07.24 16:51:27 LOG5[ui]: Compiled/running with OpenSSL 1.1.1k  25 Mar 2021
239
+2021.07.24 16:51:27 LOG5[ui]: Threading:PTHREAD Sockets:POLL,IPv6 TLS:ENGINE,OCSP,PSK,SNI
240
+2021.07.24 16:51:27 LOG5[ui]: Reading configuration from file /Users/simplicoltd./projects/shaqFindBed/app/stunnel/dev_https
241
+2021.07.24 16:51:27 LOG5[ui]: UTF-8 byte order mark not detected
242
+2021.07.24 16:51:27 LOG5[ui]: FIPS mode disabled
243
+2021.07.24 16:51:27 LOG4[ui]: Insecure file permissions on stunnel/stunnel.pem
244
+2021.07.24 16:51:27 LOG5[ui]: Configuration successful
245
+2021.07.24 16:51:27 LOG5[ui]: Binding service [https] to 0.0.0.0:8443: Address already in use (48)
246
+2021.07.24 16:51:30 LOG3[ui]: Received SIGINT; terminating
247
+2021.07.24 16:51:30 LOG5[ui]: Terminating 1 service thread(s)
248
+2021.07.24 16:51:30 LOG5[ui]: Service threads terminated
249
+2021.07.24 16:52:32 LOG5[ui]: stunnel 5.59 on x86_64-apple-darwin20.3.0 platform
250
+2021.07.24 16:52:32 LOG5[ui]: Compiled/running with OpenSSL 1.1.1k  25 Mar 2021
251
+2021.07.24 16:52:32 LOG5[ui]: Threading:PTHREAD Sockets:POLL,IPv6 TLS:ENGINE,OCSP,PSK,SNI
252
+2021.07.24 16:52:32 LOG5[ui]: Reading configuration from file /Users/simplicoltd./projects/shaqFindBed/app/stunnel/dev_https
253
+2021.07.24 16:52:32 LOG5[ui]: UTF-8 byte order mark not detected
254
+2021.07.24 16:52:32 LOG5[ui]: FIPS mode disabled
255
+2021.07.24 16:52:32 LOG4[ui]: Insecure file permissions on stunnel/stunnel.pem
256
+2021.07.24 16:52:32 LOG5[ui]: Configuration successful
257
+2021.07.24 16:52:32 LOG5[ui]: Binding service [https] to 0.0.0.0:8443: Address already in use (48)
258
+2021.07.24 16:52:43 LOG5[0]: Service [https] accepted connection from ::1:55636
259
+2021.07.24 16:52:43 LOG5[1]: Service [https] accepted connection from ::1:55637
260
+2021.07.24 16:52:43 LOG3[0]: SSL_accept: ssl/record/ssl3_record.c:322: error:1408F09C:SSL routines:ssl3_get_record:http request
261
+2021.07.24 16:52:43 LOG5[0]: Connection reset: 0 byte(s) sent to TLS, 0 byte(s) sent to socket
262
+2021.07.24 16:52:44 LOG5[2]: Service [https] accepted connection from ::1:55646
263
+2021.07.24 16:52:44 LOG3[1]: SSL_accept: ssl/record/ssl3_record.c:322: error:1408F09C:SSL routines:ssl3_get_record:http request
264
+2021.07.24 16:52:44 LOG5[1]: Connection reset: 0 byte(s) sent to TLS, 0 byte(s) sent to socket
265
+2021.07.24 16:52:44 LOG3[2]: SSL_accept: ssl/record/ssl3_record.c:322: error:1408F09C:SSL routines:ssl3_get_record:http request
266
+2021.07.24 16:52:44 LOG5[2]: Connection reset: 0 byte(s) sent to TLS, 0 byte(s) sent to socket
267
+2021.07.24 16:52:44 LOG5[3]: Service [https] accepted connection from ::1:55647
268
+2021.07.24 16:52:44 LOG3[3]: SSL_accept: ssl/record/ssl3_record.c:322: error:1408F09C:SSL routines:ssl3_get_record:http request
269
+2021.07.24 16:52:44 LOG5[3]: Connection reset: 0 byte(s) sent to TLS, 0 byte(s) sent to socket
270
+2021.07.24 16:52:46 LOG5[4]: Service [https] accepted connection from ::1:55652
271
+2021.07.24 16:52:46 LOG5[5]: Service [https] accepted connection from ::1:55653
272
+2021.07.24 16:52:46 LOG3[4]: SSL_accept: ssl/record/rec_layer_s3.c:1544: error:14094416:SSL routines:ssl3_read_bytes:sslv3 alert certificate unknown
273
+2021.07.24 16:52:46 LOG5[4]: Connection reset: 0 byte(s) sent to TLS, 0 byte(s) sent to socket
274
+2021.07.24 16:52:46 LOG3[5]: SSL_accept: ssl/record/rec_layer_s3.c:1544: error:14094416:SSL routines:ssl3_read_bytes:sslv3 alert certificate unknown
275
+2021.07.24 16:52:46 LOG5[5]: Connection reset: 0 byte(s) sent to TLS, 0 byte(s) sent to socket
276
+2021.07.24 16:52:47 LOG5[7]: Service [https] accepted connection from ::1:55655
277
+2021.07.24 16:52:47 LOG5[6]: Service [https] accepted connection from ::1:55654
278
+2021.07.24 16:52:47 LOG3[6]: SSL_accept: ssl/record/rec_layer_s3.c:1544: error:14094416:SSL routines:ssl3_read_bytes:sslv3 alert certificate unknown
279
+2021.07.24 16:52:47 LOG5[6]: Connection reset: 0 byte(s) sent to TLS, 0 byte(s) sent to socket
280
+2021.07.24 16:52:47 LOG3[7]: SSL_accept: ssl/record/rec_layer_s3.c:1544: error:14094416:SSL routines:ssl3_read_bytes:sslv3 alert certificate unknown
281
+2021.07.24 16:52:47 LOG5[7]: Connection reset: 0 byte(s) sent to TLS, 0 byte(s) sent to socket
282
+2021.07.24 16:53:00 LOG3[ui]: Received SIGINT; terminating
283
+2021.07.24 16:53:00 LOG5[ui]: Terminating 1 service thread(s)
284
+2021.07.24 16:53:00 LOG5[ui]: Service threads terminated
285
+2021.07.24 16:54:06 LOG5[ui]: stunnel 5.59 on x86_64-apple-darwin20.3.0 platform
286
+2021.07.24 16:54:06 LOG5[ui]: Compiled/running with OpenSSL 1.1.1k  25 Mar 2021
287
+2021.07.24 16:54:06 LOG5[ui]: Threading:PTHREAD Sockets:POLL,IPv6 TLS:ENGINE,OCSP,PSK,SNI
288
+2021.07.24 16:54:06 LOG5[ui]: Reading configuration from file /Users/simplicoltd./projects/shaqFindBed/app/stunnel/dev_https
289
+2021.07.24 16:54:06 LOG5[ui]: UTF-8 byte order mark not detected
290
+2021.07.24 16:54:06 LOG5[ui]: FIPS mode disabled
291
+2021.07.24 16:54:06 LOG4[ui]: Insecure file permissions on stunnel/stunnel.pem
292
+2021.07.24 16:54:06 LOG5[ui]: Configuration successful
293
+2021.07.24 16:54:06 LOG5[ui]: Binding service [https] to 0.0.0.0:8443: Address already in use (48)
294
+2021.07.24 16:54:15 LOG5[0]: Service [https] accepted connection from ::1:55712
295
+2021.07.24 16:54:15 LOG5[1]: Service [https] accepted connection from ::1:55713
296
+2021.07.24 16:54:15 LOG3[0]: SSL_accept: ssl/record/rec_layer_s3.c:1544: error:14094416:SSL routines:ssl3_read_bytes:sslv3 alert certificate unknown
297
+2021.07.24 16:54:15 LOG3[1]: SSL_accept: ssl/record/rec_layer_s3.c:1544: error:14094416:SSL routines:ssl3_read_bytes:sslv3 alert certificate unknown
298
+2021.07.24 16:54:15 LOG5[0]: Connection reset: 0 byte(s) sent to TLS, 0 byte(s) sent to socket
299
+2021.07.24 16:54:15 LOG5[1]: Connection reset: 0 byte(s) sent to TLS, 0 byte(s) sent to socket
300
+2021.07.24 16:54:26 LOG3[ui]: Received SIGINT; terminating
301
+2021.07.24 16:54:26 LOG5[ui]: Terminating 1 service thread(s)
302
+2021.07.24 16:54:26 LOG5[ui]: Service threads terminated
303
+2021.07.24 16:55:46 LOG5[ui]: stunnel 5.59 on x86_64-apple-darwin20.3.0 platform
304
+2021.07.24 16:55:46 LOG5[ui]: Compiled/running with OpenSSL 1.1.1k  25 Mar 2021
305
+2021.07.24 16:55:46 LOG5[ui]: Threading:PTHREAD Sockets:POLL,IPv6 TLS:ENGINE,OCSP,PSK,SNI
306
+2021.07.24 16:55:46 LOG5[ui]: Reading configuration from file /Users/simplicoltd./projects/shaqFindBed/app/stunnel/dev_https
307
+2021.07.24 16:55:46 LOG5[ui]: UTF-8 byte order mark not detected
308
+2021.07.24 16:55:46 LOG5[ui]: FIPS mode disabled
309
+2021.07.24 16:55:46 LOG4[ui]: Insecure file permissions on stunnel/stunnel.pem
310
+2021.07.24 16:55:46 LOG5[ui]: Configuration successful
311
+2021.07.24 16:55:46 LOG5[ui]: Binding service [https] to 0.0.0.0:8443: Address already in use (48)
312
+2021.07.24 16:55:57 LOG5[0]: Service [https] accepted connection from ::1:55754
313
+2021.07.24 16:55:57 LOG5[1]: Service [https] accepted connection from ::1:55755
314
+2021.07.24 16:55:57 LOG3[1]: SSL_accept: ssl/record/rec_layer_s3.c:1544: error:14094416:SSL routines:ssl3_read_bytes:sslv3 alert certificate unknown
315
+2021.07.24 16:55:57 LOG3[0]: SSL_accept: ssl/record/rec_layer_s3.c:1544: error:14094416:SSL routines:ssl3_read_bytes:sslv3 alert certificate unknown
316
+2021.07.24 16:55:57 LOG5[1]: Connection reset: 0 byte(s) sent to TLS, 0 byte(s) sent to socket
317
+2021.07.24 16:55:57 LOG5[0]: Connection reset: 0 byte(s) sent to TLS, 0 byte(s) sent to socket
318
+2021.07.24 16:56:28 LOG3[ui]: Received SIGINT; terminating
319
+2021.07.24 16:56:28 LOG5[ui]: Terminating 1 service thread(s)
320
+2021.07.24 16:56:28 LOG5[ui]: Service threads terminated
321
+2021.07.24 16:57:27 LOG5[ui]: stunnel 5.59 on x86_64-apple-darwin20.3.0 platform
322
+2021.07.24 16:57:27 LOG5[ui]: Compiled/running with OpenSSL 1.1.1k  25 Mar 2021
323
+2021.07.24 16:57:27 LOG5[ui]: Threading:PTHREAD Sockets:POLL,IPv6 TLS:ENGINE,OCSP,PSK,SNI
324
+2021.07.24 16:57:27 LOG5[ui]: Reading configuration from file /Users/simplicoltd./projects/shaqFindBed/app/stunnel/dev_https
325
+2021.07.24 16:57:27 LOG5[ui]: UTF-8 byte order mark not detected
326
+2021.07.24 16:57:27 LOG5[ui]: FIPS mode disabled
327
+2021.07.24 16:57:27 LOG4[ui]: Insecure file permissions on stunnel/stunnel.pem
328
+2021.07.24 16:57:27 LOG5[ui]: Configuration successful
329
+2021.07.24 16:57:27 LOG5[ui]: Binding service [https] to 0.0.0.0:8443: Address already in use (48)
330
+2021.07.24 16:57:31 LOG5[1]: Service [https] accepted connection from ::1:55812
331
+2021.07.24 16:57:31 LOG5[0]: Service [https] accepted connection from ::1:55811
332
+2021.07.24 16:57:31 LOG3[1]: SSL_accept: ssl/statem/statem_lib.c:110: error:141FC044:SSL routines:tls_setup_handshake:internal error
333
+2021.07.24 16:57:31 LOG3[0]: SSL_accept: ssl/statem/statem_lib.c:110: error:141FC044:SSL routines:tls_setup_handshake:internal error
334
+2021.07.24 16:57:31 LOG5[1]: Connection reset: 0 byte(s) sent to TLS, 0 byte(s) sent to socket
335
+2021.07.24 16:57:31 LOG5[2]: Service [https] accepted connection from ::1:55814
336
+2021.07.24 16:57:31 LOG3[2]: SSL_accept: ssl/statem/statem_lib.c:110: error:141FC044:SSL routines:tls_setup_handshake:internal error
337
+2021.07.24 16:57:31 LOG5[2]: Connection reset: 0 byte(s) sent to TLS, 0 byte(s) sent to socket
338
+2021.07.24 16:57:31 LOG5[3]: Service [https] accepted connection from ::1:55815
339
+2021.07.24 16:57:31 LOG3[3]: SSL_accept: ssl/statem/statem_lib.c:110: error:141FC044:SSL routines:tls_setup_handshake:internal error
340
+2021.07.24 16:57:31 LOG5[3]: Connection reset: 0 byte(s) sent to TLS, 0 byte(s) sent to socket
341
+2021.07.24 16:57:31 LOG5[0]: Connection reset: 0 byte(s) sent to TLS, 0 byte(s) sent to socket
342
+2021.07.24 16:57:32 LOG5[4]: Service [https] accepted connection from ::1:55816
343
+2021.07.24 16:57:32 LOG5[5]: Service [https] accepted connection from ::1:55817
344
+2021.07.24 16:57:32 LOG3[4]: SSL_accept: ssl/statem/statem_lib.c:110: error:141FC044:SSL routines:tls_setup_handshake:internal error
345
+2021.07.24 16:57:32 LOG3[5]: SSL_accept: ssl/statem/statem_lib.c:110: error:141FC044:SSL routines:tls_setup_handshake:internal error
346
+2021.07.24 16:57:32 LOG5[4]: Connection reset: 0 byte(s) sent to TLS, 0 byte(s) sent to socket
347
+2021.07.24 16:57:32 LOG5[5]: Connection reset: 0 byte(s) sent to TLS, 0 byte(s) sent to socket
348
+2021.07.24 16:57:32 LOG5[6]: Service [https] accepted connection from ::1:55818
349
+2021.07.24 16:57:32 LOG5[7]: Service [https] accepted connection from ::1:55819
350
+2021.07.24 16:57:32 LOG3[7]: SSL_accept: ssl/statem/statem_lib.c:110: error:141FC044:SSL routines:tls_setup_handshake:internal error
351
+2021.07.24 16:57:32 LOG3[6]: SSL_accept: ssl/statem/statem_lib.c:110: error:141FC044:SSL routines:tls_setup_handshake:internal error
352
+2021.07.24 16:57:32 LOG5[7]: Connection reset: 0 byte(s) sent to TLS, 0 byte(s) sent to socket
353
+2021.07.24 16:57:32 LOG5[6]: Connection reset: 0 byte(s) sent to TLS, 0 byte(s) sent to socket
354
+2021.07.24 16:58:05 LOG5[9]: Service [https] accepted connection from ::1:55829
355
+2021.07.24 16:58:05 LOG5[8]: Service [https] accepted connection from ::1:55828
356
+2021.07.24 16:58:05 LOG3[9]: SSL_accept: ssl/statem/statem_lib.c:110: error:141FC044:SSL routines:tls_setup_handshake:internal error
357
+2021.07.24 16:58:05 LOG5[9]: Connection reset: 0 byte(s) sent to TLS, 0 byte(s) sent to socket
358
+2021.07.24 16:58:05 LOG3[8]: SSL_accept: ssl/statem/statem_lib.c:110: error:141FC044:SSL routines:tls_setup_handshake:internal error
359
+2021.07.24 16:58:05 LOG5[8]: Connection reset: 0 byte(s) sent to TLS, 0 byte(s) sent to socket
360
+2021.07.24 16:58:05 LOG5[10]: Service [https] accepted connection from ::1:55831
361
+2021.07.24 16:58:05 LOG3[10]: SSL_accept: ssl/statem/statem_lib.c:110: error:141FC044:SSL routines:tls_setup_handshake:internal error
362
+2021.07.24 16:58:05 LOG5[10]: Connection reset: 0 byte(s) sent to TLS, 0 byte(s) sent to socket
363
+2021.07.24 16:58:05 LOG5[11]: Service [https] accepted connection from ::1:55832
364
+2021.07.24 16:58:05 LOG3[11]: SSL_accept: ssl/statem/statem_lib.c:110: error:141FC044:SSL routines:tls_setup_handshake:internal error
365
+2021.07.24 16:58:05 LOG5[11]: Connection reset: 0 byte(s) sent to TLS, 0 byte(s) sent to socket
366
+2021.07.24 16:58:14 LOG5[12]: Service [https] accepted connection from ::1:55833
367
+2021.07.24 16:58:14 LOG5[13]: Service [https] accepted connection from ::1:55834
368
+2021.07.24 16:58:14 LOG3[12]: SSL_accept: ssl/statem/statem_lib.c:110: error:141FC044:SSL routines:tls_setup_handshake:internal error
369
+2021.07.24 16:58:14 LOG3[13]: SSL_accept: ssl/statem/statem_lib.c:110: error:141FC044:SSL routines:tls_setup_handshake:internal error
370
+2021.07.24 16:58:14 LOG5[12]: Connection reset: 0 byte(s) sent to TLS, 0 byte(s) sent to socket
371
+2021.07.24 16:58:14 LOG5[13]: Connection reset: 0 byte(s) sent to TLS, 0 byte(s) sent to socket
372
+2021.07.24 16:58:14 LOG5[14]: Service [https] accepted connection from ::1:55835
373
+2021.07.24 16:58:14 LOG3[14]: SSL_accept: ssl/statem/statem_lib.c:110: error:141FC044:SSL routines:tls_setup_handshake:internal error
374
+2021.07.24 16:58:14 LOG5[14]: Connection reset: 0 byte(s) sent to TLS, 0 byte(s) sent to socket
375
+2021.07.24 16:58:14 LOG5[15]: Service [https] accepted connection from ::1:55836
376
+2021.07.24 16:58:14 LOG3[15]: SSL_accept: ssl/statem/statem_lib.c:110: error:141FC044:SSL routines:tls_setup_handshake:internal error
377
+2021.07.24 16:58:14 LOG5[15]: Connection reset: 0 byte(s) sent to TLS, 0 byte(s) sent to socket
378
+2021.07.24 16:58:44 LOG3[ui]: Received SIGINT; terminating
379
+2021.07.24 16:58:44 LOG5[ui]: Terminating 1 service thread(s)
380
+2021.07.24 16:58:44 LOG5[ui]: Service threads terminated
381
+2021.07.24 16:58:55 LOG5[ui]: stunnel 5.59 on x86_64-apple-darwin20.3.0 platform
382
+2021.07.24 16:58:55 LOG5[ui]: Compiled/running with OpenSSL 1.1.1k  25 Mar 2021
383
+2021.07.24 16:58:55 LOG5[ui]: Threading:PTHREAD Sockets:POLL,IPv6 TLS:ENGINE,OCSP,PSK,SNI
384
+2021.07.24 16:58:55 LOG5[ui]: Reading configuration from file /Users/simplicoltd./projects/shaqFindBed/app/stunnel/dev_https
385
+2021.07.24 16:58:55 LOG5[ui]: UTF-8 byte order mark not detected
386
+2021.07.24 16:58:55 LOG5[ui]: FIPS mode disabled
387
+2021.07.24 16:58:55 LOG4[ui]: Insecure file permissions on stunnel/stunnel.pem
388
+2021.07.24 16:58:55 LOG5[ui]: Configuration successful
389
+2021.07.24 16:58:55 LOG5[ui]: Binding service [https] to 0.0.0.0:8443: Address already in use (48)
390
+2021.07.24 16:58:58 LOG5[1]: Service [https] accepted connection from ::1:55856
391
+2021.07.24 16:58:58 LOG5[0]: Service [https] accepted connection from ::1:55855
392
+2021.07.24 16:58:59 LOG3[1]: SSL_accept: ssl/record/rec_layer_s3.c:1544: error:14094416:SSL routines:ssl3_read_bytes:sslv3 alert certificate unknown
393
+2021.07.24 16:58:59 LOG3[0]: SSL_accept: ssl/record/rec_layer_s3.c:1544: error:14094416:SSL routines:ssl3_read_bytes:sslv3 alert certificate unknown
394
+2021.07.24 16:58:59 LOG5[1]: Connection reset: 0 byte(s) sent to TLS, 0 byte(s) sent to socket
395
+2021.07.24 16:58:59 LOG5[0]: Connection reset: 0 byte(s) sent to TLS, 0 byte(s) sent to socket
396
+2021.07.24 16:59:10 LOG5[3]: Service [https] accepted connection from ::1:55859
397
+2021.07.24 16:59:10 LOG5[2]: Service [https] accepted connection from ::1:55858
398
+2021.07.24 16:59:10 LOG3[2]: SSL_accept: ssl/record/rec_layer_s3.c:1544: error:14094416:SSL routines:ssl3_read_bytes:sslv3 alert certificate unknown
399
+2021.07.24 16:59:10 LOG5[2]: Connection reset: 0 byte(s) sent to TLS, 0 byte(s) sent to socket
400
+2021.07.24 16:59:10 LOG3[3]: SSL_accept: ssl/record/rec_layer_s3.c:1544: error:14094416:SSL routines:ssl3_read_bytes:sslv3 alert certificate unknown
401
+2021.07.24 16:59:10 LOG5[3]: Connection reset: 0 byte(s) sent to TLS, 0 byte(s) sent to socket
402
+2021.07.24 16:59:19 LOG5[4]: Service [https] accepted connection from ::1:55862
403
+2021.07.24 16:59:19 LOG5[5]: Service [https] accepted connection from ::1:55863
404
+2021.07.24 16:59:19 LOG3[5]: SSL_accept: ssl/record/rec_layer_s3.c:1544: error:14094416:SSL routines:ssl3_read_bytes:sslv3 alert certificate unknown
405
+2021.07.24 16:59:19 LOG5[5]: Connection reset: 0 byte(s) sent to TLS, 0 byte(s) sent to socket
406
+2021.07.24 16:59:19 LOG3[4]: SSL_accept: ssl/record/rec_layer_s3.c:1544: error:14094416:SSL routines:ssl3_read_bytes:sslv3 alert certificate unknown
407
+2021.07.24 16:59:19 LOG5[4]: Connection reset: 0 byte(s) sent to TLS, 0 byte(s) sent to socket
408
+2021.07.24 16:59:20 LOG5[6]: Service [https] accepted connection from ::1:55864
409
+2021.07.24 16:59:20 LOG5[7]: Service [https] accepted connection from ::1:55865
410
+2021.07.24 16:59:20 LOG3[6]: SSL_accept: ssl/record/rec_layer_s3.c:1544: error:14094416:SSL routines:ssl3_read_bytes:sslv3 alert certificate unknown
411
+2021.07.24 16:59:20 LOG5[6]: Connection reset: 0 byte(s) sent to TLS, 0 byte(s) sent to socket
412
+2021.07.24 16:59:20 LOG3[7]: SSL_accept: ssl/record/rec_layer_s3.c:1544: error:14094416:SSL routines:ssl3_read_bytes:sslv3 alert certificate unknown
413
+2021.07.24 16:59:20 LOG5[7]: Connection reset: 0 byte(s) sent to TLS, 0 byte(s) sent to socket
414
+2021.07.24 16:59:24 LOG5[9]: Service [https] accepted connection from ::1:55867
415
+2021.07.24 16:59:24 LOG5[8]: Service [https] accepted connection from ::1:55866
416
+2021.07.24 16:59:24 LOG3[9]: SSL_accept: ssl/record/rec_layer_s3.c:1544: error:14094416:SSL routines:ssl3_read_bytes:sslv3 alert certificate unknown
417
+2021.07.24 16:59:24 LOG3[8]: SSL_accept: ssl/record/rec_layer_s3.c:1544: error:14094416:SSL routines:ssl3_read_bytes:sslv3 alert certificate unknown
418
+2021.07.24 16:59:24 LOG5[9]: Connection reset: 0 byte(s) sent to TLS, 0 byte(s) sent to socket
419
+2021.07.24 16:59:24 LOG5[8]: Connection reset: 0 byte(s) sent to TLS, 0 byte(s) sent to socket
420
+2021.07.24 17:02:53 LOG3[ui]: Received SIGINT; terminating
421
+2021.07.24 17:02:53 LOG5[ui]: Terminating 1 service thread(s)
422
+2021.07.24 17:02:53 LOG5[ui]: Service threads terminated

+ 12 - 0
app/stunnel/dev_https

@@ -0,0 +1,12 @@
1
+pid=
2
+
3
+cert = stunnel/stunnel.pem
4
+sslVersion = all
5
+options = NO_SSLv2
6
+foreground = yes
7
+output = stunnel.log
8
+
9
+[https]
10
+accept=8443
11
+connect=8000
12
+TIMEOUTclose=1

+ 16 - 0
app/stunnel/stunnel.cert

@@ -0,0 +1,16 @@
1
+-----BEGIN CERTIFICATE-----
2
+MIICljCCAX4CCQDaOf5rCaLNiTANBgkqhkiG9w0BAQUFADANMQswCQYDVQQGEwJU
3
+SDAeFw0yMTA3MjQwOTUzMzhaFw0yMjA3MjQwOTUzMzhaMA0xCzAJBgNVBAYTAlRI
4
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtRzaYRi1lPyQNJAH28Lm
5
+CuhM0d+2Mpdnox2GWYsIw5UgmgdXjev1po/SOaTR4R1sFdDDcGkM+yBM/Ei1fnG4
6
+rH84EQ8Q2Gqzna4mCyBov3uVn2Xw4m4gvkSVxjpSgeVPRl0LcJU1z0ksVjxDcddd
7
+sLc74MjP6dfX4P66ZBUyhf4E/nQG2EpoXUr3kHGHhyWLMjOQqA8CZfmLnxnqeDcC
8
+j5sCKDMcyEQM/5kT6Sg5cXfZTKWmBBMGc0/SUc+pUhS8GTUjY8Oub/6Ps6pwXiOc
9
+XVptNpEmnyXJzs082M8QGjHdA1DbpQoVF3qFbhjMmAKPzgl5U4qo65nTOWiWuav5
10
+vwIDAQABMA0GCSqGSIb3DQEBBQUAA4IBAQAuqUF+sV7/rrAtPAGvjlGwzNmK0ZWX
11
+cSP6poqxMOQesuq2DFr84EBEn5X19/8MNlYkDUCZJwaToXpJuobTlDiUaqccxa6a
12
+z+jweD3zr088Sfy5gyimZd9w2ZLmY2r5vNG/ccX2iAbCbMEbvX4/GApzbQEZZ0Xl
13
+SrCF1SIrmuJbDY80oP3bgS+Xi3y1ZVwAfuynHFXdefTvE0XD+XY5QsfapZ5l/+Wp
14
+iv/N2VZkb2KDFwsnlfIKGK7TcIYLCeMRT0RnHoS2SuI6dbj7TSnmv2oHl4bf03uQ
15
+RxH8PFexbaS7WgIteLGUFs3rCM7hFoO53oioiYjQYnCTqUsp/nN0h61Y
16
+-----END CERTIFICATE-----

+ 27 - 0
app/stunnel/stunnel.key

@@ -0,0 +1,27 @@
1
+-----BEGIN RSA PRIVATE KEY-----
2
+MIIEogIBAAKCAQEAtRzaYRi1lPyQNJAH28LmCuhM0d+2Mpdnox2GWYsIw5UgmgdX
3
+jev1po/SOaTR4R1sFdDDcGkM+yBM/Ei1fnG4rH84EQ8Q2Gqzna4mCyBov3uVn2Xw
4
+4m4gvkSVxjpSgeVPRl0LcJU1z0ksVjxDcdddsLc74MjP6dfX4P66ZBUyhf4E/nQG
5
+2EpoXUr3kHGHhyWLMjOQqA8CZfmLnxnqeDcCj5sCKDMcyEQM/5kT6Sg5cXfZTKWm
6
+BBMGc0/SUc+pUhS8GTUjY8Oub/6Ps6pwXiOcXVptNpEmnyXJzs082M8QGjHdA1Db
7
+pQoVF3qFbhjMmAKPzgl5U4qo65nTOWiWuav5vwIDAQABAoIBACSej90gBN757iJi
8
+mOQrVR4ReC7bP9ic2lyVxKtoPD5nca8TGvXcJtAltkjndXRB4a/LhSi+ZNyF3GsK
9
+PIAzeDaQhoKUfEB12plgM9r+E4/b6hXPo9P0lnRCI9JvymzvM4czmvOJh9bAodFR
10
+4AUtmYj4k4fQspFCjii0+HTyAEQtEBYHtUgOjfA8aZ39aXHtKmDuX3bRQ5LEhEYg
11
+NAmpgiqikjvamaQlceuXWk0ZTE15/dd4WaYa0dlNW/kEs0+hM5BVP2ndvbix/W2+
12
+mWrZedPk0OFAJHY0i+0sWUzwDU8jdegHGmKcbUfNyDbr4WsiUVjxuHQZrltm4xWl
13
+WOlCTAkCgYEA5E8GkyL1USjvWUOxEc8D4vT0RbT8ppc63NAfnpMRt8eF/xCXZClA
14
+SAppWTlGMtjvbGRTo7n6ZRXDR7dlRCil7N1S8PS8k1diZ6oElaDqUIymiAy2Cv15
15
+rr1QDlA+kshnRIj3WhvBtUOgygI8Usuit+pzCoQkcG4dDdeAFhQO4X0CgYEAyxRk
16
+6CZ4J77ynu34hBsI03RjqdKowHXkx5ohVm09VCEcR1bODsVSvtBDEeeqW+77KBVP
17
+sDZXxEXl7S6orJklfTGg8PpMLiflqfcP6gz51qfoQTzscWIXomfIcMsJCYQVfuPG
18
+XL0P4wU6u+QGggwiFuw9ycnGWDrtfV5/6pn/rOsCgYBdDHAri3Xb7AkQomwKTArT
19
+du4PcuH9q2kMEa6xXFM+SY0tFT/+TGmscsHY4WTg2FVMId+MvQF2LVZ3ZiFZlA97
20
+6AAjwDsS+exbP4m6yeh1h71feX7AH+p18yYrjzzRaefcoM3e5a0fCT8A1cRsIh5h
21
+QqY8RPrs75PbzlafqPEfqQKBgHsxJ/VcQM97mhqnKXaaH8SGel7ul8gIvHwJF+gh
22
+5G5Al7L/CYkUUpnGJKmb61BRrLIoG2s9zAgYjt5Oy6vIS2Gi1YrZi5UERuHQKitF
23
+K9n3iYDpwFUXuFagtosV36mSIqgS7KYdWqHQ7kxEi14glh1puiHK8TNcq+y9gsOC
24
+IAN5AoGACa3JWYinvJSMyKQfgywSr1Joi/0MwGXOoj3G7wxrxp220v9MxiSdedfH
25
+na3n1VoTrYro8Z8K9NqGqGScydLds/EtZb7HyZUU+07NYMSNDNbFQfozdwErV4tU
26
+oW6Vh2b9gtI/yvsLZhZNdjwfleOS/jw/137g1RrjzcDRaswTIz0=
27
+-----END RSA PRIVATE KEY-----

+ 43 - 0
app/stunnel/stunnel.pem

@@ -0,0 +1,43 @@
1
+-----BEGIN RSA PRIVATE KEY-----
2
+MIIEogIBAAKCAQEAtRzaYRi1lPyQNJAH28LmCuhM0d+2Mpdnox2GWYsIw5UgmgdX
3
+jev1po/SOaTR4R1sFdDDcGkM+yBM/Ei1fnG4rH84EQ8Q2Gqzna4mCyBov3uVn2Xw
4
+4m4gvkSVxjpSgeVPRl0LcJU1z0ksVjxDcdddsLc74MjP6dfX4P66ZBUyhf4E/nQG
5
+2EpoXUr3kHGHhyWLMjOQqA8CZfmLnxnqeDcCj5sCKDMcyEQM/5kT6Sg5cXfZTKWm
6
+BBMGc0/SUc+pUhS8GTUjY8Oub/6Ps6pwXiOcXVptNpEmnyXJzs082M8QGjHdA1Db
7
+pQoVF3qFbhjMmAKPzgl5U4qo65nTOWiWuav5vwIDAQABAoIBACSej90gBN757iJi
8
+mOQrVR4ReC7bP9ic2lyVxKtoPD5nca8TGvXcJtAltkjndXRB4a/LhSi+ZNyF3GsK
9
+PIAzeDaQhoKUfEB12plgM9r+E4/b6hXPo9P0lnRCI9JvymzvM4czmvOJh9bAodFR
10
+4AUtmYj4k4fQspFCjii0+HTyAEQtEBYHtUgOjfA8aZ39aXHtKmDuX3bRQ5LEhEYg
11
+NAmpgiqikjvamaQlceuXWk0ZTE15/dd4WaYa0dlNW/kEs0+hM5BVP2ndvbix/W2+
12
+mWrZedPk0OFAJHY0i+0sWUzwDU8jdegHGmKcbUfNyDbr4WsiUVjxuHQZrltm4xWl
13
+WOlCTAkCgYEA5E8GkyL1USjvWUOxEc8D4vT0RbT8ppc63NAfnpMRt8eF/xCXZClA
14
+SAppWTlGMtjvbGRTo7n6ZRXDR7dlRCil7N1S8PS8k1diZ6oElaDqUIymiAy2Cv15
15
+rr1QDlA+kshnRIj3WhvBtUOgygI8Usuit+pzCoQkcG4dDdeAFhQO4X0CgYEAyxRk
16
+6CZ4J77ynu34hBsI03RjqdKowHXkx5ohVm09VCEcR1bODsVSvtBDEeeqW+77KBVP
17
+sDZXxEXl7S6orJklfTGg8PpMLiflqfcP6gz51qfoQTzscWIXomfIcMsJCYQVfuPG
18
+XL0P4wU6u+QGggwiFuw9ycnGWDrtfV5/6pn/rOsCgYBdDHAri3Xb7AkQomwKTArT
19
+du4PcuH9q2kMEa6xXFM+SY0tFT/+TGmscsHY4WTg2FVMId+MvQF2LVZ3ZiFZlA97
20
+6AAjwDsS+exbP4m6yeh1h71feX7AH+p18yYrjzzRaefcoM3e5a0fCT8A1cRsIh5h
21
+QqY8RPrs75PbzlafqPEfqQKBgHsxJ/VcQM97mhqnKXaaH8SGel7ul8gIvHwJF+gh
22
+5G5Al7L/CYkUUpnGJKmb61BRrLIoG2s9zAgYjt5Oy6vIS2Gi1YrZi5UERuHQKitF
23
+K9n3iYDpwFUXuFagtosV36mSIqgS7KYdWqHQ7kxEi14glh1puiHK8TNcq+y9gsOC
24
+IAN5AoGACa3JWYinvJSMyKQfgywSr1Joi/0MwGXOoj3G7wxrxp220v9MxiSdedfH
25
+na3n1VoTrYro8Z8K9NqGqGScydLds/EtZb7HyZUU+07NYMSNDNbFQfozdwErV4tU
26
+oW6Vh2b9gtI/yvsLZhZNdjwfleOS/jw/137g1RrjzcDRaswTIz0=
27
+-----END RSA PRIVATE KEY-----
28
+-----BEGIN CERTIFICATE-----
29
+MIICljCCAX4CCQDaOf5rCaLNiTANBgkqhkiG9w0BAQUFADANMQswCQYDVQQGEwJU
30
+SDAeFw0yMTA3MjQwOTUzMzhaFw0yMjA3MjQwOTUzMzhaMA0xCzAJBgNVBAYTAlRI
31
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtRzaYRi1lPyQNJAH28Lm
32
+CuhM0d+2Mpdnox2GWYsIw5UgmgdXjev1po/SOaTR4R1sFdDDcGkM+yBM/Ei1fnG4
33
+rH84EQ8Q2Gqzna4mCyBov3uVn2Xw4m4gvkSVxjpSgeVPRl0LcJU1z0ksVjxDcddd
34
+sLc74MjP6dfX4P66ZBUyhf4E/nQG2EpoXUr3kHGHhyWLMjOQqA8CZfmLnxnqeDcC
35
+j5sCKDMcyEQM/5kT6Sg5cXfZTKWmBBMGc0/SUc+pUhS8GTUjY8Oub/6Ps6pwXiOc
36
+XVptNpEmnyXJzs082M8QGjHdA1DbpQoVF3qFbhjMmAKPzgl5U4qo65nTOWiWuav5
37
+vwIDAQABMA0GCSqGSIb3DQEBBQUAA4IBAQAuqUF+sV7/rrAtPAGvjlGwzNmK0ZWX
38
+cSP6poqxMOQesuq2DFr84EBEn5X19/8MNlYkDUCZJwaToXpJuobTlDiUaqccxa6a
39
+z+jweD3zr088Sfy5gyimZd9w2ZLmY2r5vNG/ccX2iAbCbMEbvX4/GApzbQEZZ0Xl
40
+SrCF1SIrmuJbDY80oP3bgS+Xi3y1ZVwAfuynHFXdefTvE0XD+XY5QsfapZ5l/+Wp
41
+iv/N2VZkb2KDFwsnlfIKGK7TcIYLCeMRT0RnHoS2SuI6dbj7TSnmv2oHl4bf03uQ
42
+RxH8PFexbaS7WgIteLGUFs3rCM7hFoO53oioiYjQYnCTqUsp/nN0h61Y
43
+-----END CERTIFICATE-----

+ 12 - 0
app/templates/admin/404.html

@@ -0,0 +1,12 @@
1
+{% extends "admin/base_site.html" %}
2
+{% load i18n %}
3
+
4
+{% block title %}{% translate 'Page not found' %}{% endblock %}
5
+
6
+{% block content %}
7
+
8
+<h2>{% translate 'Page not found' %}</h2>
9
+
10
+<p>{% translate 'We’re sorry, but the requested page could not be found.' %}</p>
11
+
12
+{% endblock %}

+ 17 - 0
app/templates/admin/500.html

@@ -0,0 +1,17 @@
1
+{% extends "admin/base_site.html" %}
2
+{% load i18n %}
3
+
4
+{% block breadcrumbs %}
5
+<div class="breadcrumbs">
6
+<a href="{% url 'admin:index' %}">{% translate 'Home' %}</a>
7
+&rsaquo; {% translate 'Server error' %}
8
+</div>
9
+{% endblock %}
10
+
11
+{% block title %}{% translate 'Server error (500)' %}{% endblock %}
12
+
13
+{% block content %}
14
+<h1>{% translate 'Server Error <em>(500)</em>' %}</h1>
15
+<p>{% translate 'There’s been an error. It’s been reported to the site administrators via email and should be fixed shortly. Thanks for your patience.' %}</p>
16
+
17
+{% endblock %}

+ 23 - 0
app/templates/admin/actions.html

@@ -0,0 +1,23 @@
1
+{% load i18n %}
2
+<div class="actions">
3
+  {% block actions %}
4
+    {% block actions-form %}
5
+    {% for field in action_form %}{% if field.label %}<label>{{ field.label }} {% endif %}{{ field }}{% if field.label %}</label>{% endif %}{% endfor %}
6
+    {% endblock %}
7
+    {% block actions-submit %}
8
+    <button type="submit" class="button" title="{% translate "Run the selected action" %}" name="index" value="{{ action_index|default:0 }}">{% translate "Go" %}</button>
9
+    {% endblock %}
10
+    {% block actions-counter %}
11
+    {% if actions_selection_counter %}
12
+        <span class="action-counter" data-actions-icnt="{{ cl.result_list|length }}">{{ selection_note }}</span>
13
+        {% if cl.result_count != cl.result_list|length %}
14
+        <span class="all hidden">{{ selection_note_all }}</span>
15
+        <span class="question hidden">
16
+            <a href="#" title="{% translate "Click here to select the objects across all pages" %}">{% blocktranslate with cl.result_count as total_count %}Select all {{ total_count }} {{ module_name }}{% endblocktranslate %}</a>
17
+        </span>
18
+        <span class="clear hidden"><a href="#">{% translate "Clear selection" %}</a></span>
19
+        {% endif %}
20
+    {% endif %}
21
+    {% endblock %}
22
+  {% endblock %}
23
+</div>

+ 18 - 0
app/templates/admin/app_index.html

@@ -0,0 +1,18 @@
1
+{% extends "admin/index.html" %}
2
+{% load i18n %}
3
+
4
+{% block bodyclass %}{{ block.super }} app-{{ app_label }}{% endblock %}
5
+
6
+{% if not is_popup %}
7
+{% block breadcrumbs %}
8
+<div class="breadcrumbs">
9
+<a href="{% url 'admin:index' %}">{% translate 'Home' %}</a>
10
+&rsaquo;
11
+{% for app in app_list %}
12
+{{ app.name }}
13
+{% endfor %}
14
+</div>
15
+{% endblock %}
16
+{% endif %}
17
+
18
+{% block sidebar %}{% endblock %}

+ 40 - 0
app/templates/admin/app_list.html

@@ -0,0 +1,40 @@
1
+{% load i18n %}
2
+
3
+{% if app_list %}
4
+  {% for app in app_list %}
5
+    <div class="app-{{ app.app_label }} module{% if app.app_url in request.path %} current-app{% endif %}">
6
+      <table>
7
+        <caption>
8
+          <a href="{{ app.app_url }}" class="section" title="{% blocktranslate with name=app.name %}Models in the {{ name }} application{% endblocktranslate %}">{{ app.name }}</a>
9
+        </caption>
10
+        {% for model in app.models %}
11
+          <tr class="model-{{ model.object_name|lower }}{% if model.admin_url in request.path %} current-model{% endif %}">
12
+            {% if model.admin_url %}
13
+              <th scope="row"><a href="{{ model.admin_url }}"{% if model.admin_url in request.path %} aria-current="page"{% endif %}>{{ model.name }}</a></th>
14
+            {% else %}
15
+              <th scope="row">{{ model.name }}</th>
16
+            {% endif %}
17
+
18
+            {% if model.add_url %}
19
+              <td><a href="{{ model.add_url }}" class="addlink">{% translate 'Add' %}</a></td>
20
+            {% else %}
21
+              <td></td>
22
+            {% endif %}
23
+
24
+            {% if model.admin_url and show_changelinks %}
25
+              {% if model.view_only %}
26
+                <td><a href="{{ model.admin_url }}" class="viewlink">{% translate 'View' %}</a></td>
27
+              {% else %}
28
+                <td><a href="{{ model.admin_url }}" class="changelink">{% translate 'Change' %}</a></td>
29
+              {% endif %}
30
+            {% elif show_changelinks %}
31
+              <td></td>
32
+            {% endif %}
33
+          </tr>
34
+        {% endfor %}
35
+      </table>
36
+    </div>
37
+  {% endfor %}
38
+{% else %}
39
+  <p>{% translate 'You don’t have permission to view or edit anything.' %}</p>
40
+{% endif %}

+ 10 - 0
app/templates/admin/auth/user/add_form.html

@@ -0,0 +1,10 @@
1
+{% extends "admin/change_form.html" %}
2
+{% load i18n %}
3
+
4
+{% block form_top %}
5
+  {% if not is_popup %}
6
+    <p>{% translate 'First, enter a username and password. Then, you’ll be able to edit more user options.' %}</p>
7
+  {% else %}
8
+    <p>{% translate "Enter a username and password." %}</p>
9
+  {% endif %}
10
+{% endblock %}

+ 57 - 0
app/templates/admin/auth/user/change_password.html

@@ -0,0 +1,57 @@
1
+{% extends "admin/base_site.html" %}
2
+{% load i18n static %}
3
+{% load admin_urls %}
4
+
5
+{% block extrastyle %}{{ block.super }}<link rel="stylesheet" type="text/css" href="{% static "admin/css/forms.css" %}">{% endblock %}
6
+{% block bodyclass %}{{ block.super }} {{ opts.app_label }}-{{ opts.model_name }} change-form{% endblock %}
7
+{% if not is_popup %}
8
+{% block breadcrumbs %}
9
+<div class="breadcrumbs">
10
+<a href="{% url 'admin:index' %}">{% translate 'Home' %}</a>
11
+&rsaquo; <a href="{% url 'admin:app_list' app_label=opts.app_label %}">{{ opts.app_config.verbose_name }}</a>
12
+&rsaquo; <a href="{% url opts|admin_urlname:'changelist' %}">{{ opts.verbose_name_plural|capfirst }}</a>
13
+&rsaquo; <a href="{% url opts|admin_urlname:'change' original.pk|admin_urlquote %}">{{ original|truncatewords:"18" }}</a>
14
+&rsaquo; {% translate 'Change password' %}
15
+</div>
16
+{% endblock %}
17
+{% endif %}
18
+{% block content %}<div id="content-main">
19
+<form{% if form_url %} action="{{ form_url }}"{% endif %} method="post" id="{{ opts.model_name }}_form">{% csrf_token %}{% block form_top %}{% endblock %}
20
+<input type="text" name="username" value="{{ original.get_username }}" class="hidden">
21
+<div>
22
+{% if is_popup %}<input type="hidden" name="_popup" value="1">{% endif %}
23
+{% if form.errors %}
24
+    <p class="errornote">
25
+    {% if form.errors.items|length == 1 %}{% translate "Please correct the error below." %}{% else %}{% translate "Please correct the errors below." %}{% endif %}
26
+    </p>
27
+{% endif %}
28
+
29
+<p>{% blocktranslate with username=original %}Enter a new password for the user <strong>{{ username }}</strong>.{% endblocktranslate %}</p>
30
+
31
+<fieldset class="module aligned">
32
+
33
+<div class="form-row">
34
+  {{ form.password1.errors }}
35
+  {{ form.password1.label_tag }} {{ form.password1 }}
36
+  {% if form.password1.help_text %}
37
+  <div class="help">{{ form.password1.help_text|safe }}</div>
38
+  {% endif %}
39
+</div>
40
+
41
+<div class="form-row">
42
+  {{ form.password2.errors }}
43
+  {{ form.password2.label_tag }} {{ form.password2 }}
44
+  {% if form.password2.help_text %}
45
+  <div class="help">{{ form.password2.help_text|safe }}</div>
46
+  {% endif %}
47
+</div>
48
+
49
+</fieldset>
50
+
51
+<div class="submit-row">
52
+<input type="submit" value="{% translate 'Change password' %}" class="default">
53
+</div>
54
+
55
+</div>
56
+</form></div>
57
+{% endblock %}

+ 104 - 0
app/templates/admin/base.html

@@ -0,0 +1,104 @@
1
+{% load i18n static %}<!DOCTYPE html>
2
+{% get_current_language as LANGUAGE_CODE %}{% get_current_language_bidi as LANGUAGE_BIDI %}
3
+<html lang="{{ LANGUAGE_CODE|default:"en-us" }}" dir="{{ LANGUAGE_BIDI|yesno:'rtl,ltr,auto' }}">
4
+<head>
5
+<title>{% block title %}{% endblock %}</title>
6
+<link rel="stylesheet" type="text/css" href="{% block stylesheet %}{% static "admin/css/base.css" %}{% endblock %}">
7
+{% if not is_popup and is_nav_sidebar_enabled %}
8
+  <link rel="stylesheet" type="text/css" href="{% static "admin/css/nav_sidebar.css" %}">
9
+  <script src="{% static 'admin/js/nav_sidebar.js' %}" defer></script>
10
+{% endif %}
11
+{% block extrastyle %}{% endblock %}
12
+{% if LANGUAGE_BIDI %}<link rel="stylesheet" type="text/css" href="{% block stylesheet_rtl %}{% static "admin/css/rtl.css" %}{% endblock %}">{% endif %}
13
+{% block extrahead %}{% endblock %}
14
+{% block responsive %}
15
+    <meta name="viewport" content="user-scalable=no, width=device-width, initial-scale=1.0, maximum-scale=1.0">
16
+    <link rel="stylesheet" type="text/css" href="{% static "admin/css/responsive.css" %}">
17
+    {% if LANGUAGE_BIDI %}<link rel="stylesheet" type="text/css" href="{% static "admin/css/responsive_rtl.css" %}">{% endif %}
18
+{% endblock %}
19
+{% block blockbots %}<meta name="robots" content="NONE,NOARCHIVE">{% endblock %}
20
+</head>
21
+{% load i18n %}
22
+
23
+<body class="{% if is_popup %}popup {% endif %}{% block bodyclass %}{% endblock %}"
24
+  data-admin-utc-offset="{% now "Z" %}">
25
+
26
+<!-- Container -->
27
+<div id="container">
28
+
29
+    {% if not is_popup %}
30
+    <!-- Header -->
31
+    <div id="header">
32
+        <div id="branding">
33
+        {% block branding %}{% endblock %}
34
+        </div>
35
+        {% block usertools %}
36
+        {% if has_permission %}
37
+        <div id="user-tools">
38
+            {% block welcome-msg %}
39
+                {% translate 'Welcome,' %}
40
+                <strong>{% firstof user.get_short_name user.get_username %}</strong>.
41
+            {% endblock %}
42
+            {% block userlinks %}
43
+                {% if site_url %}
44
+                    <a href="{{ site_url }}">{% translate 'View site' %}</a> /
45
+                {% endif %}
46
+                {% if user.is_active and user.is_staff %}
47
+                    {% url 'django-admindocs-docroot' as docsroot %}
48
+                    {% if docsroot %}
49
+                        <a href="{{ docsroot }}">{% translate 'Documentation' %}</a> /
50
+                    {% endif %}
51
+                {% endif %}
52
+                {% if user.has_usable_password %}
53
+                <a href="{% url 'admin:password_change' %}">{% translate 'Change password' %}</a> /
54
+                {% endif %}
55
+                <a href="{% url 'admin:logout' %}">{% translate 'Log out' %}</a>
56
+            {% endblock %}
57
+        </div>
58
+        {% endif %}
59
+        {% endblock %}
60
+        {% block nav-global %}{% endblock %}
61
+    </div>
62
+    <!-- END Header -->
63
+    {% block breadcrumbs %}
64
+    <div class="breadcrumbs">
65
+    <a href="{% url 'admin:index' %}">{% translate 'Home' %}</a>
66
+    {% if title %} &rsaquo; {{ title }}{% endif %}
67
+    </div>
68
+    {% endblock %}
69
+    {% endif %}
70
+
71
+    <div class="main shifted" id="main">
72
+      {% if not is_popup and is_nav_sidebar_enabled %}
73
+        {% block nav-sidebar %}
74
+          {% include "admin/nav_sidebar.html" %}
75
+        {% endblock %}
76
+      {% endif %}
77
+      <div class="content">
78
+        {% block messages %}
79
+          {% if messages %}
80
+            <ul class="messagelist">{% for message in messages %}
81
+              <li{% if message.tags %} class="{{ message.tags }}"{% endif %}>{{ message|capfirst }}</li>
82
+            {% endfor %}</ul>
83
+          {% endif %}
84
+        {% endblock messages %}
85
+        <!-- Content -->
86
+        <div id="content" class="{% block coltype %}colM{% endblock %}">
87
+          {% block pretitle %}{% endblock %}
88
+          {% block content_title %}{% if title %}<h1>{{ title }}</h1>{% endif %}{% endblock %}
89
+          {% block content_subtitle %}{% if subtitle %}<h2>{{ subtitle }}</h2>{% endif %}{% endblock %}
90
+          {% block content %}
91
+            {% block object-tools %}{% endblock %}
92
+            {{ content }}
93
+          {% endblock %}
94
+          {% block sidebar %}{% endblock %}
95
+          <br class="clear">
96
+        </div>
97
+        <!-- END Content -->
98
+        {% block footer %}<div id="footer"></div>{% endblock %}
99
+      </div>
100
+    </div>
101
+</div>
102
+<!-- END Container -->
103
+</body>
104
+</html>

+ 9 - 0
app/templates/admin/base_site.html

@@ -0,0 +1,9 @@
1
+{% extends "admin/base.html" %}
2
+
3
+{% block title %}{% if subtitle %}{{ subtitle }} | {% endif %}{{ title }} | {{ site_title|default:_('Django site admin') }}{% endblock %}
4
+
5
+{% block branding %}
6
+<h1 id="site-name"><a href="{% url 'admin:index' %}">ShaqFindBeds <!-- {{ site_header|default:_('ShaqFindBed administration') }} --></a></h1>
7
+{% endblock %}
8
+
9
+{% block nav-global %}{% endblock %}

+ 81 - 0
app/templates/admin/change_form.html

@@ -0,0 +1,81 @@
1
+{% extends "admin/base_site.html" %}
2
+{% load i18n admin_urls static admin_modify %}
3
+
4
+{% block extrahead %}{{ block.super }}
5
+<script src="{% url 'admin:jsi18n' %}"></script>
6
+{{ media }}
7
+{% endblock %}
8
+
9
+{% block extrastyle %}{{ block.super }}<link rel="stylesheet" type="text/css" href="{% static "admin/css/forms.css" %}">{% endblock %}
10
+
11
+{% block coltype %}colM{% endblock %}
12
+
13
+{% block bodyclass %}{{ block.super }} app-{{ opts.app_label }} model-{{ opts.model_name }} change-form{% endblock %}
14
+
15
+{% if not is_popup %}
16
+{% block breadcrumbs %}
17
+<div class="breadcrumbs">
18
+<a href="{% url 'admin:index' %}">{% translate 'Home' %}</a>
19
+&rsaquo; <a href="{% url 'admin:app_list' app_label=opts.app_label %}">{{ opts.app_config.verbose_name }}</a>
20
+&rsaquo; {% if has_view_permission %}<a href="{% url opts|admin_urlname:'changelist' %}">{{ opts.verbose_name_plural|capfirst }}</a>{% else %}{{ opts.verbose_name_plural|capfirst }}{% endif %}
21
+&rsaquo; {% if add %}{% blocktranslate with name=opts.verbose_name %}Add {{ name }}{% endblocktranslate %}{% else %}{{ original|truncatewords:"18" }}{% endif %}
22
+</div>
23
+{% endblock %}
24
+{% endif %}
25
+
26
+{% block content %}<div id="content-main">
27
+{% block object-tools %}
28
+{% if change %}{% if not is_popup %}
29
+  <ul class="object-tools">
30
+    {% block object-tools-items %}
31
+      {% change_form_object_tools %}
32
+    {% endblock %}
33
+  </ul>
34
+{% endif %}{% endif %}
35
+{% endblock %}
36
+<form {% if has_file_field %}enctype="multipart/form-data" {% endif %}{% if form_url %}action="{{ form_url }}" {% endif %}method="post" id="{{ opts.model_name }}_form" novalidate>{% csrf_token %}{% block form_top %}{% endblock %}
37
+<div>
38
+{% if is_popup %}<input type="hidden" name="{{ is_popup_var }}" value="1">{% endif %}
39
+{% if to_field %}<input type="hidden" name="{{ to_field_var }}" value="{{ to_field }}">{% endif %}
40
+{% if save_on_top %}{% block submit_buttons_top %}{% submit_row %}{% endblock %}{% endif %}
41
+{% if errors %}
42
+    <p class="errornote">
43
+    {% if errors|length == 1 %}{% translate "Please correct the error below." %}{% else %}{% translate "Please correct the errors below." %}{% endif %}
44
+    </p>
45
+    {{ adminform.form.non_field_errors }}
46
+{% endif %}
47
+
48
+{% block field_sets %}
49
+{% for fieldset in adminform %}
50
+  {% include "admin/includes/fieldset.html" %}
51
+{% endfor %}
52
+{% endblock %}
53
+
54
+{% block after_field_sets %}{% endblock %}
55
+
56
+{% block inline_field_sets %}
57
+{% for inline_admin_formset in inline_admin_formsets %}
58
+    {% include inline_admin_formset.opts.template %}
59
+{% endfor %}
60
+{% endblock %}
61
+
62
+{% block after_related_objects %}{% endblock %}
63
+
64
+{% block submit_buttons_bottom %}{% submit_row %}{% endblock %}
65
+
66
+{% block admin_change_form_document_ready %}
67
+    <script id="django-admin-form-add-constants"
68
+            src="{% static 'admin/js/change_form.js' %}"
69
+            {% if adminform and add %}
70
+                data-model-name="{{ opts.model_name }}"
71
+            {% endif %}
72
+            async>
73
+    </script>
74
+{% endblock %}
75
+
76
+{# JavaScript for prepopulated fields #}
77
+{% prepopulated_fields_js %}
78
+
79
+</div>
80
+</form></div>
81
+{% endblock %}

+ 8 - 0
app/templates/admin/change_form_object_tools.html

@@ -0,0 +1,8 @@
1
+{% load i18n admin_urls %}
2
+{% block object-tools-items %}
3
+<li>
4
+    {% url opts|admin_urlname:'history' original.pk|admin_urlquote as history_url %}
5
+    <a href="{% add_preserved_filters history_url %}" class="historylink">{% translate "History" %}</a>
6
+</li>
7
+{% if has_absolute_url %}<li><a href="{{ absolute_url }}" class="viewsitelink">{% translate "View on site" %}</a></li>{% endif %}
8
+{% endblock %}

+ 86 - 0
app/templates/admin/change_list.html

@@ -0,0 +1,86 @@
1
+{% extends "admin/base_site.html" %}
2
+{% load i18n admin_urls static admin_list %}
3
+
4
+{% block extrastyle %}
5
+  {{ block.super }}
6
+  <link rel="stylesheet" type="text/css" href="{% static "admin/css/changelists.css" %}">
7
+  {% if cl.formset %}
8
+    <link rel="stylesheet" type="text/css" href="{% static "admin/css/forms.css" %}">
9
+  {% endif %}
10
+  {% if cl.formset or action_form %}
11
+    <script src="{% url 'admin:jsi18n' %}"></script>
12
+  {% endif %}
13
+  {{ media.css }}
14
+  {% if not actions_on_top and not actions_on_bottom %}
15
+    <style>
16
+      #changelist table thead th:first-child {width: inherit}
17
+    </style>
18
+  {% endif %}
19
+{% endblock %}
20
+
21
+{% block extrahead %}
22
+{{ block.super }}
23
+{{ media.js }}
24
+{% endblock %}
25
+
26
+{% block bodyclass %}{{ block.super }} app-{{ opts.app_label }} model-{{ opts.model_name }} change-list{% endblock %}
27
+
28
+{% if not is_popup %}
29
+{% block breadcrumbs %}
30
+<div class="breadcrumbs">
31
+<a href="{% url 'admin:index' %}">{% translate 'Home' %}</a>
32
+&rsaquo; <a href="{% url 'admin:app_list' app_label=cl.opts.app_label %}">{{ cl.opts.app_config.verbose_name }}</a>
33
+&rsaquo; {{ cl.opts.verbose_name_plural|capfirst }}
34
+</div>
35
+{% endblock %}
36
+{% endif %}
37
+
38
+{% block coltype %}{% endblock %}
39
+
40
+{% block content %}
41
+  <div id="content-main">
42
+    {% block object-tools %}
43
+        <ul class="object-tools">
44
+          {% block object-tools-items %}
45
+            {% change_list_object_tools %}
46
+          {% endblock %}
47
+        </ul>
48
+    {% endblock %}
49
+    {% if cl.formset and cl.formset.errors %}
50
+        <p class="errornote">
51
+        {% if cl.formset.total_error_count == 1 %}{% translate "Please correct the error below." %}{% else %}{% translate "Please correct the errors below." %}{% endif %}
52
+        </p>
53
+        {{ cl.formset.non_form_errors }}
54
+    {% endif %}
55
+    <div class="module{% if cl.has_filters %} filtered{% endif %}" id="changelist">
56
+      <div class="changelist-form-container">
57
+        {% block search %}{% search_form cl %}{% endblock %}
58
+        {% block date_hierarchy %}{% if cl.date_hierarchy %}{% date_hierarchy cl %}{% endif %}{% endblock %}
59
+
60
+        <form id="changelist-form" method="post"{% if cl.formset and cl.formset.is_multipart %} enctype="multipart/form-data"{% endif %} novalidate>{% csrf_token %}
61
+        {% if cl.formset %}
62
+          <div>{{ cl.formset.management_form }}</div>
63
+        {% endif %}
64
+
65
+        {% block result_list %}
66
+          {% if action_form and actions_on_top and cl.show_admin_actions %}{% admin_actions %}{% endif %}
67
+          {% result_list cl %}
68
+          {% if action_form and actions_on_bottom and cl.show_admin_actions %}{% admin_actions %}{% endif %}
69
+        {% endblock %}
70
+        {% block pagination %}{% pagination cl %}{% endblock %}
71
+        </form>
72
+      </div>
73
+      {% block filters %}
74
+        {% if cl.has_filters %}
75
+          <div id="changelist-filter">
76
+            <h2>{% translate 'Filter' %}</h2>
77
+            {% if cl.has_active_filters %}<h3 id="changelist-filter-clear">
78
+              <a href="{{ cl.clear_all_filters_qs }}">&#10006; {% translate "Clear all filters" %}</a>
79
+            </h3>{% endif %}
80
+            {% for spec in cl.filter_specs %}{% admin_list_filter cl spec %}{% endfor %}
81
+          </div>
82
+        {% endif %}
83
+      {% endblock %}
84
+    </div>
85
+  </div>
86
+{% endblock %}

+ 12 - 0
app/templates/admin/change_list_object_tools.html

@@ -0,0 +1,12 @@
1
+{% load i18n admin_urls %}
2
+
3
+{% block object-tools-items %}
4
+  {% if has_add_permission %}
5
+  <li>
6
+    {% url cl.opts|admin_urlname:'add' as add_url %}
7
+    <a href="{% add_preserved_filters add_url is_popup to_field %}" class="addlink">
8
+      {% blocktranslate with cl.opts.verbose_name as name %}Add {{ name }}{% endblocktranslate %}
9
+    </a>
10
+  </li>
11
+  {% endif %}
12
+{% endblock %}

+ 38 - 0
app/templates/admin/change_list_results.html

@@ -0,0 +1,38 @@
1
+{% load i18n static %}
2
+{% if result_hidden_fields %}
3
+<div class="hiddenfields">{# DIV for HTML validation #}
4
+{% for item in result_hidden_fields %}{{ item }}{% endfor %}
5
+</div>
6
+{% endif %}
7
+{% if results %}
8
+<div class="results">
9
+<table id="result_list">
10
+<thead>
11
+<tr>
12
+{% for header in result_headers %}
13
+<th scope="col"{{ header.class_attrib }}>
14
+   {% if header.sortable %}
15
+     {% if header.sort_priority > 0 %}
16
+       <div class="sortoptions">
17
+         <a class="sortremove" href="{{ header.url_remove }}" title="{% translate "Remove from sorting" %}"></a>
18
+         {% if num_sorted_fields > 1 %}<span class="sortpriority" title="{% blocktranslate with priority_number=header.sort_priority %}Sorting priority: {{ priority_number }}{% endblocktranslate %}">{{ header.sort_priority }}</span>{% endif %}
19
+         <a href="{{ header.url_toggle }}" class="toggle {% if header.ascending %}ascending{% else %}descending{% endif %}" title="{% translate "Toggle sorting" %}"></a>
20
+       </div>
21
+     {% endif %}
22
+   {% endif %}
23
+   <div class="text">{% if header.sortable %}<a href="{{ header.url_primary }}">{{ header.text|capfirst }}</a>{% else %}<span>{{ header.text|capfirst }}</span>{% endif %}</div>
24
+   <div class="clear"></div>
25
+</th>{% endfor %}
26
+</tr>
27
+</thead>
28
+<tbody>
29
+{% for result in results %}
30
+{% if result.form and result.form.non_field_errors %}
31
+    <tr><td colspan="{{ result|length }}">{{ result.form.non_field_errors }}</td></tr>
32
+{% endif %}
33
+<tr>{% for item in result %}{{ item }}{% endfor %}</tr>
34
+{% endfor %}
35
+</tbody>
36
+</table>
37
+</div>
38
+{% endif %}

+ 16 - 0
app/templates/admin/date_hierarchy.html

@@ -0,0 +1,16 @@
1
+{% if show %}
2
+<div class="xfull">
3
+<ul class="toplinks">
4
+{% block date-hierarchy-toplinks %}
5
+{% block date-hierarchy-back %}
6
+{% if back %}<li class="date-back"><a href="{{ back.link }}">&lsaquo; {{ back.title }}</a></li>{% endif %}
7
+{% endblock %}
8
+{% block date-hierarchy-choices %}
9
+{% for choice in choices %}
10
+<li> {% if choice.link %}<a href="{{ choice.link }}">{% endif %}{{ choice.title }}{% if choice.link %}</a>{% endif %}</li>
11
+{% endfor %}
12
+{% endblock %}
13
+{% endblock %}
14
+</ul><br class="clear">
15
+</div>
16
+{% endif %}

+ 52 - 0
app/templates/admin/delete_confirmation.html

@@ -0,0 +1,52 @@
1
+{% extends "admin/base_site.html" %}
2
+{% load i18n admin_urls static %}
3
+
4
+{% block extrahead %}
5
+    {{ block.super }}
6
+    {{ media }}
7
+    <script src="{% static 'admin/js/cancel.js' %}" async></script>
8
+{% endblock %}
9
+
10
+{% block bodyclass %}{{ block.super }} app-{{ opts.app_label }} model-{{ opts.model_name }} delete-confirmation{% endblock %}
11
+
12
+{% block breadcrumbs %}
13
+<div class="breadcrumbs">
14
+<a href="{% url 'admin:index' %}">{% translate 'Home' %}</a>
15
+&rsaquo; <a href="{% url 'admin:app_list' app_label=opts.app_label %}">{{ opts.app_config.verbose_name }}</a>
16
+&rsaquo; <a href="{% url opts|admin_urlname:'changelist' %}">{{ opts.verbose_name_plural|capfirst }}</a>
17
+&rsaquo; <a href="{% url opts|admin_urlname:'change' object.pk|admin_urlquote %}">{{ object|truncatewords:"18" }}</a>
18
+&rsaquo; {% translate 'Delete' %}
19
+</div>
20
+{% endblock %}
21
+
22
+{% block content %}
23
+{% if perms_lacking %}
24
+    <p>{% blocktranslate with escaped_object=object %}Deleting the {{ object_name }} '{{ escaped_object }}' would result in deleting related objects, but your account doesn't have permission to delete the following types of objects:{% endblocktranslate %}</p>
25
+    <ul>
26
+    {% for obj in perms_lacking %}
27
+        <li>{{ obj }}</li>
28
+    {% endfor %}
29
+    </ul>
30
+{% elif protected %}
31
+    <p>{% blocktranslate with escaped_object=object %}Deleting the {{ object_name }} '{{ escaped_object }}' would require deleting the following protected related objects:{% endblocktranslate %}</p>
32
+    <ul>
33
+    {% for obj in protected %}
34
+        <li>{{ obj }}</li>
35
+    {% endfor %}
36
+    </ul>
37
+{% else %}
38
+    <p>{% blocktranslate with escaped_object=object %}Are you sure you want to delete the {{ object_name }} "{{ escaped_object }}"? All of the following related items will be deleted:{% endblocktranslate %}</p>
39
+    {% include "admin/includes/object_delete_summary.html" %}
40
+    <h2>{% translate "Objects" %}</h2>
41
+    <ul>{{ deleted_objects|unordered_list }}</ul>
42
+    <form method="post">{% csrf_token %}
43
+    <div>
44
+    <input type="hidden" name="post" value="yes">
45
+    {% if is_popup %}<input type="hidden" name="{{ is_popup_var }}" value="1">{% endif %}
46
+    {% if to_field %}<input type="hidden" name="{{ to_field_var }}" value="{{ to_field }}">{% endif %}
47
+    <input type="submit" value="{% translate 'Yes, I’m sure' %}">
48
+    <a href="#" class="button cancel-link">{% translate "No, take me back" %}</a>
49
+    </div>
50
+    </form>
51
+{% endif %}
52
+{% endblock %}

+ 55 - 0
app/templates/admin/delete_selected_confirmation.html

@@ -0,0 +1,55 @@
1
+{% extends "admin/base_site.html" %}
2
+{% load i18n l10n admin_urls static %}
3
+
4
+{% block extrahead %}
5
+    {{ block.super }}
6
+    {{ media }}
7
+    <script src="{% static 'admin/js/cancel.js' %}" async></script>
8
+{% endblock %}
9
+
10
+{% block bodyclass %}{{ block.super }} app-{{ opts.app_label }} model-{{ opts.model_name }} delete-confirmation delete-selected-confirmation{% endblock %}
11
+
12
+{% block breadcrumbs %}
13
+<div class="breadcrumbs">
14
+<a href="{% url 'admin:index' %}">{% translate 'Home' %}</a>
15
+&rsaquo; <a href="{% url 'admin:app_list' app_label=opts.app_label %}">{{ opts.app_config.verbose_name }}</a>
16
+&rsaquo; <a href="{% url opts|admin_urlname:'changelist' %}">{{ opts.verbose_name_plural|capfirst }}</a>
17
+&rsaquo; {% translate 'Delete multiple objects' %}
18
+</div>
19
+{% endblock %}
20
+
21
+{% block content %}
22
+{% if perms_lacking %}
23
+    <p>{% blocktranslate %}Deleting the selected {{ objects_name }} would result in deleting related objects, but your account doesn't have permission to delete the following types of objects:{% endblocktranslate %}</p>
24
+    <ul>
25
+    {% for obj in perms_lacking %}
26
+        <li>{{ obj }}</li>
27
+    {% endfor %}
28
+    </ul>
29
+{% elif protected %}
30
+    <p>{% blocktranslate %}Deleting the selected {{ objects_name }} would require deleting the following protected related objects:{% endblocktranslate %}</p>
31
+    <ul>
32
+    {% for obj in protected %}
33
+        <li>{{ obj }}</li>
34
+    {% endfor %}
35
+    </ul>
36
+{% else %}
37
+    <p>{% blocktranslate %}Are you sure you want to delete the selected {{ objects_name }}? All of the following objects and their related items will be deleted:{% endblocktranslate %}</p>
38
+    {% include "admin/includes/object_delete_summary.html" %}
39
+    <h2>{% translate "Objects" %}</h2>
40
+    {% for deletable_object in deletable_objects %}
41
+        <ul>{{ deletable_object|unordered_list }}</ul>
42
+    {% endfor %}
43
+    <form method="post">{% csrf_token %}
44
+    <div>
45
+    {% for obj in queryset %}
46
+    <input type="hidden" name="{{ action_checkbox_name }}" value="{{ obj.pk|unlocalize }}">
47
+    {% endfor %}
48
+    <input type="hidden" name="action" value="delete_selected">
49
+    <input type="hidden" name="post" value="yes">
50
+    <input type="submit" value="{% translate 'Yes, I’m sure' %}">
51
+    <a href="#" class="button cancel-link">{% translate "No, take me back" %}</a>
52
+    </div>
53
+    </form>
54
+{% endif %}
55
+{% endblock %}

File diff suppressed because it is too large
+ 29 - 0
app/templates/admin/edit_inline/stacked.html


+ 79 - 0
app/templates/admin/edit_inline/tabular.html

@@ -0,0 +1,79 @@
1
+{% load i18n admin_urls static admin_modify %}
2
+<div class="js-inline-admin-formset inline-group" id="{{ inline_admin_formset.formset.prefix }}-group"
3
+     data-inline-type="tabular"
4
+     data-inline-formset="{{ inline_admin_formset.inline_formset_data }}">
5
+  <div class="tabular inline-related {% if forloop.last %}last-related{% endif %}">
6
+{{ inline_admin_formset.formset.management_form }}
7
+<fieldset class="module {{ inline_admin_formset.classes }}">
8
+   {% if inline_admin_formset.formset.max_num == 1 %}
9
+     <h2>{{ inline_admin_formset.opts.verbose_name|capfirst }}</h2>
10
+   {% else %}
11
+     <h2>{{ inline_admin_formset.opts.verbose_name_plural|capfirst }}</h2>
12
+   {% endif %}
13
+   {{ inline_admin_formset.formset.non_form_errors }}
14
+   <table>
15
+     <thead><tr>
16
+       <th class="original"></th>
17
+     {% for field in inline_admin_formset.fields %}
18
+       {% if not field.widget.is_hidden %}
19
+         <th class="column-{{ field.name }}{% if field.required %} required{% endif %}">{{ field.label|capfirst }}
20
+         {% if field.help_text %}<img src="{% static "admin/img/icon-unknown.svg" %}" class="help help-tooltip" width="10" height="10" alt="({{ field.help_text|striptags }})" title="{{ field.help_text|striptags }}">{% endif %}
21
+         </th>
22
+       {% endif %}
23
+     {% endfor %}
24
+     {% if inline_admin_formset.formset.can_delete and inline_admin_formset.has_delete_permission %}<th>{% translate "Delete?" %}</th>{% endif %}
25
+     </tr></thead>
26
+
27
+     <tbody>
28
+     {% for inline_admin_form in inline_admin_formset %}
29
+        {% if inline_admin_form.form.non_field_errors %}
30
+        <tr class="row-form-errors"><td colspan="{{ inline_admin_form|cell_count }}">{{ inline_admin_form.form.non_field_errors }}</td></tr>
31
+        {% endif %}
32
+        <tr class="form-row {% if inline_admin_form.original or inline_admin_form.show_url %}has_original{% endif %}{% if forloop.last and inline_admin_formset.has_add_permission %} empty-form{% endif %}"
33
+             id="{{ inline_admin_formset.formset.prefix }}-{% if not forloop.last %}{{ forloop.counter0 }}{% else %}empty{% endif %}">
34
+        <td class="original">
35
+          {% if inline_admin_form.original or inline_admin_form.show_url %}<p>
36
+          {% if inline_admin_form.original %}
37
+          {{ inline_admin_form.original }}
38
+          {% if inline_admin_form.model_admin.show_change_link and inline_admin_form.model_admin.has_registered_model %}<a href="{% url inline_admin_form.model_admin.opts|admin_urlname:'change' inline_admin_form.original.pk|admin_urlquote %}" class="{% if inline_admin_formset.has_change_permission %}inlinechangelink{% else %}inlineviewlink{% endif %}">{% if inline_admin_formset.has_change_permission %}{% translate "Change" %}{% else %}{% translate "View" %}{% endif %}</a>{% endif %}
39
+          {% endif %}
40
+          {% if inline_admin_form.show_url %}<a href="{{ inline_admin_form.absolute_url }}">{% translate "View on site" %}</a>{% endif %}
41
+            </p>{% endif %}
42
+          {% if inline_admin_form.needs_explicit_pk_field %}{{ inline_admin_form.pk_field.field }}{% endif %}
43
+          {% if inline_admin_form.fk_field %}{{ inline_admin_form.fk_field.field }}{% endif %}
44
+          {% spaceless %}
45
+          {% for fieldset in inline_admin_form %}
46
+            {% for line in fieldset %}
47
+              {% for field in line %}
48
+                {% if not field.is_readonly and field.field.is_hidden %}{{ field.field }}{% endif %}
49
+              {% endfor %}
50
+            {% endfor %}
51
+          {% endfor %}
52
+          {% endspaceless %}
53
+        </td>
54
+        {% for fieldset in inline_admin_form %}
55
+          {% for line in fieldset %}
56
+            {% for field in line %}
57
+              {% if field.is_readonly or not field.field.is_hidden %}
58
+              <td{% if field.field.name %} class="field-{{ field.field.name }}"{% endif %}>
59
+              {% if field.is_readonly %}
60
+                  <p>{{ field.contents }}</p>
61
+              {% else %}
62
+                  {{ field.field.errors.as_ul }}
63
+                  {{ field.field }}
64
+              {% endif %}
65
+              </td>
66
+              {% endif %}
67
+            {% endfor %}
68
+          {% endfor %}
69
+        {% endfor %}
70
+        {% if inline_admin_formset.formset.can_delete and inline_admin_formset.has_delete_permission %}
71
+          <td class="delete">{% if inline_admin_form.original %}{{ inline_admin_form.deletion_field.field }}{% endif %}</td>
72
+        {% endif %}
73
+        </tr>
74
+     {% endfor %}
75
+     </tbody>
76
+   </table>
77
+</fieldset>
78
+  </div>
79
+</div>

+ 8 - 0
app/templates/admin/filter.html

@@ -0,0 +1,8 @@
1
+{% load i18n %}
2
+<h3>{% blocktranslate with filter_title=title %} By {{ filter_title }} {% endblocktranslate %}</h3>
3
+<ul>
4
+{% for choice in choices %}
5
+    <li{% if choice.selected %} class="selected"{% endif %}>
6
+    <a href="{{ choice.query_string|iriencode }}" title="{{ choice.display }}">{{ choice.display }}</a></li>
7
+{% endfor %}
8
+</ul>

+ 29 - 0
app/templates/admin/includes/fieldset.html

@@ -0,0 +1,29 @@
1
+<fieldset class="module aligned {{ fieldset.classes }}">
2
+    {% if fieldset.name %}<h2>{{ fieldset.name }}</h2>{% endif %}
3
+    {% if fieldset.description %}
4
+        <div class="description">{{ fieldset.description|safe }}</div>
5
+    {% endif %}
6
+    {% for line in fieldset %}
7
+        <div class="form-row{% if line.fields|length_is:'1' and line.errors %} errors{% endif %}{% if not line.has_visible_field %} hidden{% endif %}{% for field in line %}{% if field.field.name %} field-{{ field.field.name }}{% endif %}{% endfor %}">
8
+            {% if line.fields|length_is:'1' %}{{ line.errors }}{% endif %}
9
+            {% for field in line %}
10
+                <div{% if not line.fields|length_is:'1' %} class="fieldBox{% if field.field.name %} field-{{ field.field.name }}{% endif %}{% if not field.is_readonly and field.errors %} errors{% endif %}{% if field.field.is_hidden %} hidden{% endif %}"{% elif field.is_checkbox %} class="checkbox-row"{% endif %}>
11
+                    {% if not line.fields|length_is:'1' and not field.is_readonly %}{{ field.errors }}{% endif %}
12
+                    {% if field.is_checkbox %}
13
+                        {{ field.field }}{{ field.label_tag }}
14
+                    {% else %}
15
+                        {{ field.label_tag }}
16
+                        {% if field.is_readonly %}
17
+                            <div class="readonly">{{ field.contents }}</div>
18
+                        {% else %}
19
+                            {{ field.field }}
20
+                        {% endif %}
21
+                    {% endif %}
22
+                    {% if field.field.help_text %}
23
+                        <div class="help">{{ field.field.help_text|safe }}</div>
24
+                    {% endif %}
25
+                </div>
26
+            {% endfor %}
27
+        </div>
28
+    {% endfor %}
29
+</fieldset>

+ 7 - 0
app/templates/admin/includes/object_delete_summary.html

@@ -0,0 +1,7 @@
1
+{% load i18n %}
2
+<h2>{% translate "Summary" %}</h2>
3
+<ul>
4
+    {% for model_name, object_count in model_count %}
5
+    <li>{{ model_name|capfirst }}: {{ object_count }}</li>
6
+    {% endfor %}
7
+</ul>

+ 50 - 0
app/templates/admin/index.html

@@ -0,0 +1,50 @@
1
+{% extends "admin/base_site.html" %}
2
+{% load i18n static %}
3
+
4
+{% block extrastyle %}{{ block.super }}<link rel="stylesheet" type="text/css" href="{% static "admin/css/dashboard.css" %}">{% endblock %}
5
+
6
+{% block coltype %}colMS{% endblock %}
7
+
8
+{% block bodyclass %}{{ block.super }} dashboard{% endblock %}
9
+
10
+{% block breadcrumbs %}{% endblock %}
11
+
12
+{% block nav-sidebar %}{% endblock %}
13
+
14
+{% block content %}
15
+<div id="content-main">
16
+  {% include "admin/app_list.html" with app_list=app_list show_changelinks=True %}
17
+</div>
18
+{% endblock %}
19
+
20
+{% block sidebar %}
21
+<div id="content-related">
22
+    <div class="module" id="recent-actions-module">
23
+        <h2>{% translate 'Recent actions' %}</h2>
24
+        <h3>{% translate 'My actions' %}</h3>
25
+            {% load log %}
26
+            {% get_admin_log 10 as admin_log for_user user %}
27
+            {% if not admin_log %}
28
+            <p>{% translate 'None available' %}</p>
29
+            {% else %}
30
+            <ul class="actionlist">
31
+            {% for entry in admin_log %}
32
+            <li class="{% if entry.is_addition %}addlink{% endif %}{% if entry.is_change %}changelink{% endif %}{% if entry.is_deletion %}deletelink{% endif %}">
33
+                {% if entry.is_deletion or not entry.get_admin_url %}
34
+                    {{ entry.object_repr }}
35
+                {% else %}
36
+                    <a href="{{ entry.get_admin_url }}">{{ entry.object_repr }}</a>
37
+                {% endif %}
38
+                <br>
39
+                {% if entry.content_type %}
40
+                    <span class="mini quiet">{% filter capfirst %}{{ entry.content_type.name }}{% endfilter %}</span>
41
+                {% else %}
42
+                    <span class="mini quiet">{% translate 'Unknown content' %}</span>
43
+                {% endif %}
44
+            </li>
45
+            {% endfor %}
46
+            </ul>
47
+            {% endif %}
48
+    </div>
49
+</div>
50
+{% endblock %}

+ 13 - 0
app/templates/admin/invalid_setup.html

@@ -0,0 +1,13 @@
1
+{% extends "admin/base_site.html" %}
2
+{% load i18n %}
3
+
4
+{% block breadcrumbs %}
5
+<div class="breadcrumbs">
6
+<a href="{% url 'admin:index' %}">{% translate 'Home' %}</a>
7
+&rsaquo; {{ title }}
8
+</div>
9
+{% endblock %}
10
+
11
+{% block content %}
12
+<p>{% translate 'Something’s wrong with your database installation. Make sure the appropriate database tables have been created, and make sure the database is readable by the appropriate user.' %}</p>
13
+{% endblock %}

+ 68 - 0
app/templates/admin/login.html

@@ -0,0 +1,68 @@
1
+{% extends "admin/base_site.html" %}
2
+{% load i18n static %}
3
+
4
+{% block extrastyle %}{{ block.super }}<link rel="stylesheet" type="text/css" href="{% static "admin/css/login.css" %}">
5
+{{ form.media }}
6
+{% endblock %}
7
+
8
+{% block bodyclass %}{{ block.super }} login{% endblock %}
9
+
10
+{% block usertools %}{% endblock %}
11
+
12
+{% block nav-global %}{% endblock %}
13
+
14
+{% block nav-sidebar %}{% endblock %}
15
+
16
+{% block content_title %}{% endblock %}
17
+
18
+{% block breadcrumbs %}{% endblock %}
19
+
20
+{% block content %}
21
+{% if form.errors and not form.non_field_errors %}
22
+<p class="errornote">
23
+{% if form.errors.items|length == 1 %}{% translate "Please correct the error below." %}{% else %}{% translate "Please correct the errors below." %}{% endif %}
24
+</p>
25
+{% endif %}
26
+
27
+{% if form.non_field_errors %}
28
+{% for error in form.non_field_errors %}
29
+<p class="errornote">
30
+    {{ error }}
31
+</p>
32
+{% endfor %}
33
+{% endif %}
34
+
35
+<div id="content-main">
36
+
37
+{% if user.is_authenticated %}
38
+<p class="errornote">
39
+{% blocktranslate trimmed %}
40
+    You are authenticated as {{ username }}, but are not authorized to
41
+    access this page. Would you like to login to a different account?
42
+{% endblocktranslate %}
43
+</p>
44
+{% endif %}
45
+
46
+<form action="{{ app_path }}" method="post" id="login-form">{% csrf_token %}
47
+  <div class="form-row">
48
+    {{ form.username.errors }}
49
+    {{ form.username.label_tag }} {{ form.username }}
50
+  </div>
51
+  <div class="form-row">
52
+    {{ form.password.errors }}
53
+    {{ form.password.label_tag }} {{ form.password }}
54
+    <input type="hidden" name="next" value="{{ next }}">
55
+  </div>
56
+  {% url 'admin_password_reset' as password_reset_url %}
57
+  {% if password_reset_url %}
58
+  <div class="password-reset-link">
59
+    <a href="{{ password_reset_url }}">{% translate 'Forgotten your password or username?' %}</a>
60
+  </div>
61
+  {% endif %}
62
+  <div class="submit-row">
63
+    <input type="submit" value="{% translate 'Log in' %}">
64
+  </div>
65
+</form>
66
+
67
+</div>
68
+{% endblock %}

+ 5 - 0
app/templates/admin/nav_sidebar.html

@@ -0,0 +1,5 @@
1
+{% load i18n %}
2
+<button class="sticky toggle-nav-sidebar" id="toggle-nav-sidebar" aria-label="{% translate 'Toggle navigation' %}"></button>
3
+<nav class="sticky" id="nav-sidebar">
4
+  {% include 'admin/app_list.html' with app_list=available_apps show_changelinks=False %}
5
+</nav>

+ 42 - 0
app/templates/admin/object_history.html

@@ -0,0 +1,42 @@
1
+{% extends "admin/base_site.html" %}
2
+{% load i18n admin_urls %}
3
+
4
+{% block breadcrumbs %}
5
+<div class="breadcrumbs">
6
+<a href="{% url 'admin:index' %}">{% translate 'Home' %}</a>
7
+&rsaquo; <a href="{% url 'admin:app_list' app_label=opts.app_label %}">{{ opts.app_config.verbose_name }}</a>
8
+&rsaquo; <a href="{% url opts|admin_urlname:'changelist' %}">{{ module_name }}</a>
9
+&rsaquo; <a href="{% url opts|admin_urlname:'change' object.pk|admin_urlquote %}">{{ object|truncatewords:"18" }}</a>
10
+&rsaquo; {% translate 'History' %}
11
+</div>
12
+{% endblock %}
13
+
14
+{% block content %}
15
+<div id="content-main">
16
+<div class="module">
17
+
18
+{% if action_list %}
19
+    <table id="change-history">
20
+        <thead>
21
+        <tr>
22
+            <th scope="col">{% translate 'Date/time' %}</th>
23
+            <th scope="col">{% translate 'User' %}</th>
24
+            <th scope="col">{% translate 'Action' %}</th>
25
+        </tr>
26
+        </thead>
27
+        <tbody>
28
+        {% for action in action_list %}
29
+        <tr>
30
+            <th scope="row">{{ action.action_time|date:"DATETIME_FORMAT" }}</th>
31
+            <td>{{ action.user.get_username }}{% if action.user.get_full_name %} ({{ action.user.get_full_name }}){% endif %}</td>
32
+            <td>{{ action.get_change_message }}</td>
33
+        </tr>
34
+        {% endfor %}
35
+        </tbody>
36
+    </table>
37
+{% else %}
38
+    <p>{% translate 'This object doesn’t have a change history. It probably wasn’t added via this admin site.' %}</p>
39
+{% endif %}
40
+</div>
41
+</div>
42
+{% endblock %}

+ 12 - 0
app/templates/admin/pagination.html

@@ -0,0 +1,12 @@
1
+{% load admin_list %}
2
+{% load i18n %}
3
+<p class="paginator">
4
+{% if pagination_required %}
5
+{% for i in page_range %}
6
+    {% paginator_number cl i %}
7
+{% endfor %}
8
+{% endif %}
9
+{{ cl.result_count }} {% if cl.result_count == 1 %}{{ cl.opts.verbose_name }}{% else %}{{ cl.opts.verbose_name_plural }}{% endif %}
10
+{% if show_all_url %}<a href="{{ show_all_url }}" class="showall">{% translate 'Show all' %}</a>{% endif %}
11
+{% if cl.formset and cl.result_count %}<input type="submit" name="_save" class="default" value="{% translate 'Save' %}">{% endif %}
12
+</p>

+ 10 - 0
app/templates/admin/popup_response.html

@@ -0,0 +1,10 @@
1
+{% load i18n static %}<!DOCTYPE html>
2
+<html>
3
+  <head><title>{% translate 'Popup closing…' %}</title></head>
4
+  <body>
5
+    <script id="django-admin-popup-response-constants"
6
+            src="{% static "admin/js/popup_response.js" %}"
7
+            data-popup-response="{{ popup_response_data }}">
8
+    </script>
9
+  </body>
10
+</html>

+ 0 - 0
app/templates/admin/prepopulated_fields_js.html


Some files were not shown because too many files changed in this diff

tum/whitesports - Gogs: Simplico Git Service

暂无描述

class-wp-plugins-list-table.php 42KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238
  1. <?php
  2. /**
  3. * List Table API: WP_Plugins_List_Table class
  4. *
  5. * @package WordPress
  6. * @subpackage Administration
  7. * @since 3.1.0
  8. */
  9. /**
  10. * Core class used to implement displaying installed plugins in a list table.
  11. *
  12. * @since 3.1.0
  13. * @access private
  14. *
  15. * @see WP_List_Table
  16. */
  17. class WP_Plugins_List_Table extends WP_List_Table {
  18. /**
  19. * Whether to show the auto-updates UI.
  20. *
  21. * @since 5.5.0
  22. *
  23. * @var bool True if auto-updates UI is to be shown, false otherwise.
  24. */
  25. protected $show_autoupdates = true;
  26. /**
  27. * Constructor.
  28. *
  29. * @since 3.1.0
  30. *
  31. * @see WP_List_Table::__construct() for more information on default arguments.
  32. *
  33. * @global string $status
  34. * @global int $page
  35. *
  36. * @param array $args An associative array of arguments.
  37. */
  38. public function __construct( $args = array() ) {
  39. global $status, $page;
  40. parent::__construct(
  41. array(
  42. 'plural' => 'plugins',
  43. 'screen' => isset( $args['screen'] ) ? $args['screen'] : null,
  44. )
  45. );
  46. $allowed_statuses = array( 'active', 'inactive', 'recently_activated', 'upgrade', 'mustuse', 'dropins', 'search', 'paused', 'auto-update-enabled', 'auto-update-disabled' );
  47. $status = 'all';
  48. if ( isset( $_REQUEST['plugin_status'] ) && in_array( $_REQUEST['plugin_status'], $allowed_statuses, true ) ) {
  49. $status = $_REQUEST['plugin_status'];
  50. }
  51. if ( isset( $_REQUEST['s'] ) ) {
  52. $_SERVER['REQUEST_URI'] = add_query_arg( 's', wp_unslash( $_REQUEST['s'] ) );
  53. }
  54. $page = $this->get_pagenum();
  55. $this->show_autoupdates = wp_is_auto_update_enabled_for_type( 'plugin' )
  56. && current_user_can( 'update_plugins' )
  57. && ( ! is_multisite() || $this->screen->in_admin( 'network' ) )
  58. && ! in_array( $status, array( 'mustuse', 'dropins' ), true );
  59. }
  60. /**
  61. * @return array
  62. */
  63. protected function get_table_classes() {
  64. return array( 'widefat', $this->_args['plural'] );
  65. }
  66. /**
  67. * @return bool
  68. */
  69. public function ajax_user_can() {
  70. return current_user_can( 'activate_plugins' );
  71. }
  72. /**
  73. * @global string $status
  74. * @global array $plugins
  75. * @global array $totals
  76. * @global int $page
  77. * @global string $orderby
  78. * @global string $order
  79. * @global string $s
  80. */
  81. public function prepare_items() {
  82. global $status, $plugins, $totals, $page, $orderby, $order, $s;
  83. wp_reset_vars( array( 'orderby', 'order' ) );
  84. /**
  85. * Filters the full array of plugins to list in the Plugins list table.
  86. *
  87. * @since 3.0.0
  88. *
  89. * @see get_plugins()
  90. *
  91. * @param array $all_plugins An array of plugins to display in the list table.
  92. */
  93. $all_plugins = apply_filters( 'all_plugins', get_plugins() );
  94. $plugins = array(
  95. 'all' => $all_plugins,
  96. 'search' => array(),
  97. 'active' => array(),
  98. 'inactive' => array(),
  99. 'recently_activated' => array(),
  100. 'upgrade' => array(),
  101. 'mustuse' => array(),
  102. 'dropins' => array(),
  103. 'paused' => array(),
  104. );
  105. if ( $this->show_autoupdates ) {
  106. $auto_updates = (array) get_site_option( 'auto_update_plugins', array() );
  107. $plugins['auto-update-enabled'] = array();
  108. $plugins['auto-update-disabled'] = array();
  109. }
  110. $screen = $this->screen;
  111. if ( ! is_multisite() || ( $screen->in_admin( 'network' ) && current_user_can( 'manage_network_plugins' ) ) ) {
  112. /**
  113. * Filters whether to display the advanced plugins list table.
  114. *
  115. * There are two types of advanced plugins - must-use and drop-ins -
  116. * which can be used in a single site or Multisite network.
  117. *
  118. * The $type parameter allows you to differentiate between the type of advanced
  119. * plugins to filter the display of. Contexts include 'mustuse' and 'dropins'.
  120. *
  121. * @since 3.0.0
  122. *
  123. * @param bool $show Whether to show the advanced plugins for the specified
  124. * plugin type. Default true.
  125. * @param string $type The plugin type. Accepts 'mustuse', 'dropins'.
  126. */
  127. if ( apply_filters( 'show_advanced_plugins', true, 'mustuse' ) ) {
  128. $plugins['mustuse'] = get_mu_plugins();
  129. }
  130. /** This action is documented in wp-admin/includes/class-wp-plugins-list-table.php */
  131. if ( apply_filters( 'show_advanced_plugins', true, 'dropins' ) ) {
  132. $plugins['dropins'] = get_dropins();
  133. }
  134. if ( current_user_can( 'update_plugins' ) ) {
  135. $current = get_site_transient( 'update_plugins' );
  136. foreach ( (array) $plugins['all'] as $plugin_file => $plugin_data ) {
  137. if ( isset( $current->response[ $plugin_file ] ) ) {
  138. $plugins['all'][ $plugin_file ]['update'] = true;
  139. $plugins['upgrade'][ $plugin_file ] = $plugins['all'][ $plugin_file ];
  140. }
  141. }
  142. }
  143. }
  144. if ( ! $screen->in_admin( 'network' ) ) {
  145. $show = current_user_can( 'manage_network_plugins' );
  146. /**
  147. * Filters whether to display network-active plugins alongside plugins active for the current site.
  148. *
  149. * This also controls the display of inactive network-only plugins (plugins with
  150. * "Network: true" in the plugin header).
  151. *
  152. * Plugins cannot be network-activated or network-deactivated from this screen.
  153. *
  154. * @since 4.4.0
  155. *
  156. * @param bool $show Whether to show network-active plugins. Default is whether the current
  157. * user can manage network plugins (ie. a Super Admin).
  158. */
  159. $show_network_active = apply_filters( 'show_network_active_plugins', $show );
  160. }
  161. if ( $screen->in_admin( 'network' ) ) {
  162. $recently_activated = get_site_option( 'recently_activated', array() );
  163. } else {
  164. $recently_activated = get_option( 'recently_activated', array() );
  165. }
  166. foreach ( $recently_activated as $key => $time ) {
  167. if ( $time + WEEK_IN_SECONDS < time() ) {
  168. unset( $recently_activated[ $key ] );
  169. }
  170. }
  171. if ( $screen->in_admin( 'network' ) ) {
  172. update_site_option( 'recently_activated', $recently_activated );
  173. } else {
  174. update_option( 'recently_activated', $recently_activated );
  175. }
  176. $plugin_info = get_site_transient( 'update_plugins' );
  177. foreach ( (array) $plugins['all'] as $plugin_file => $plugin_data ) {
  178. // Extra info if known. array_merge() ensures $plugin_data has precedence if keys collide.
  179. if ( isset( $plugin_info->response[ $plugin_file ] ) ) {
  180. $plugin_data = array_merge( (array) $plugin_info->response[ $plugin_file ], array( 'update-supported' => true ), $plugin_data );
  181. } elseif ( isset( $plugin_info->no_update[ $plugin_file ] ) ) {
  182. $plugin_data = array_merge( (array) $plugin_info->no_update[ $plugin_file ], array( 'update-supported' => true ), $plugin_data );
  183. } elseif ( empty( $plugin_data['update-supported'] ) ) {
  184. $plugin_data['update-supported'] = false;
  185. }
  186. /*
  187. * Create the payload that's used for the auto_update_plugin filter.
  188. * This is the same data contained within $plugin_info->(response|no_update) however
  189. * not all plugins will be contained in those keys, this avoids unexpected warnings.
  190. */
  191. $filter_payload = array(
  192. 'id' => $plugin_file,
  193. 'slug' => '',
  194. 'plugin' => $plugin_file,
  195. 'new_version' => '',
  196. 'url' => '',
  197. 'package' => '',
  198. 'icons' => array(),
  199. 'banners' => array(),
  200. 'banners_rtl' => array(),
  201. 'tested' => '',
  202. 'requires_php' => '',
  203. 'compatibility' => new stdClass(),
  204. );
  205. $filter_payload = (object) wp_parse_args( $plugin_data, $filter_payload );
  206. $auto_update_forced = wp_is_auto_update_forced_for_item( 'plugin', null, $filter_payload );
  207. if ( ! is_null( $auto_update_forced ) ) {
  208. $plugin_data['auto-update-forced'] = $auto_update_forced;
  209. }
  210. $plugins['all'][ $plugin_file ] = $plugin_data;
  211. // Make sure that $plugins['upgrade'] also receives the extra info since it is used on ?plugin_status=upgrade.
  212. if ( isset( $plugins['upgrade'][ $plugin_file ] ) ) {
  213. $plugins['upgrade'][ $plugin_file ] = $plugin_data;
  214. }
  215. // Filter into individual sections.
  216. if ( is_multisite() && ! $screen->in_admin( 'network' ) && is_network_only_plugin( $plugin_file ) && ! is_plugin_active( $plugin_file ) ) {
  217. if ( $show_network_active ) {
  218. // On the non-network screen, show inactive network-only plugins if allowed.
  219. $plugins['inactive'][ $plugin_file ] = $plugin_data;
  220. } else {
  221. // On the non-network screen, filter out network-only plugins as long as they're not individually active.
  222. unset( $plugins['all'][ $plugin_file ] );
  223. }
  224. } elseif ( ! $screen->in_admin( 'network' ) && is_plugin_active_for_network( $plugin_file ) ) {
  225. if ( $show_network_active ) {
  226. // On the non-network screen, show network-active plugins if allowed.
  227. $plugins['active'][ $plugin_file ] = $plugin_data;
  228. } else {
  229. // On the non-network screen, filter out network-active plugins.
  230. unset( $plugins['all'][ $plugin_file ] );
  231. }
  232. } elseif ( ( ! $screen->in_admin( 'network' ) && is_plugin_active( $plugin_file ) )
  233. || ( $screen->in_admin( 'network' ) && is_plugin_active_for_network( $plugin_file ) ) ) {
  234. // On the non-network screen, populate the active list with plugins that are individually activated.
  235. // On the network admin screen, populate the active list with plugins that are network-activated.
  236. $plugins['active'][ $plugin_file ] = $plugin_data;
  237. if ( ! $screen->in_admin( 'network' ) && is_plugin_paused( $plugin_file ) ) {
  238. $plugins['paused'][ $plugin_file ] = $plugin_data;
  239. }
  240. } else {
  241. if ( isset( $recently_activated[ $plugin_file ] ) ) {
  242. // Populate the recently activated list with plugins that have been recently activated.
  243. $plugins['recently_activated'][ $plugin_file ] = $plugin_data;
  244. }
  245. // Populate the inactive list with plugins that aren't activated.
  246. $plugins['inactive'][ $plugin_file ] = $plugin_data;
  247. }
  248. if ( $this->show_autoupdates ) {
  249. $enabled = in_array( $plugin_file, $auto_updates, true ) && $plugin_data['update-supported'];
  250. if ( isset( $plugin_data['auto-update-forced'] ) ) {
  251. $enabled = (bool) $plugin_data['auto-update-forced'];
  252. }
  253. if ( $enabled ) {
  254. $plugins['auto-update-enabled'][ $plugin_file ] = $plugin_data;
  255. } else {
  256. $plugins['auto-update-disabled'][ $plugin_file ] = $plugin_data;
  257. }
  258. }
  259. }
  260. if ( strlen( $s ) ) {
  261. $status = 'search';
  262. $plugins['search'] = array_filter( $plugins['all'], array( $this, '_search_callback' ) );
  263. }
  264. $totals = array();
  265. foreach ( $plugins as $type => $list ) {
  266. $totals[ $type ] = count( $list );
  267. }
  268. if ( empty( $plugins[ $status ] ) && ! in_array( $status, array( 'all', 'search' ), true ) ) {
  269. $status = 'all';
  270. }
  271. $this->items = array();
  272. foreach ( $plugins[ $status ] as $plugin_file => $plugin_data ) {
  273. // Translate, don't apply markup, sanitize HTML.
  274. $this->items[ $plugin_file ] = _get_plugin_data_markup_translate( $plugin_file, $plugin_data, false, true );
  275. }
  276. $total_this_page = $totals[ $status ];
  277. $js_plugins = array();
  278. foreach ( $plugins as $key => $list ) {
  279. $js_plugins[ $key ] = array_keys( $list );
  280. }
  281. wp_localize_script(
  282. 'updates',
  283. '_wpUpdatesItemCounts',
  284. array(
  285. 'plugins' => $js_plugins,
  286. 'totals' => wp_get_update_data(),
  287. )
  288. );
  289. if ( ! $orderby ) {
  290. $orderby = 'Name';
  291. } else {
  292. $orderby = ucfirst( $orderby );
  293. }
  294. $order = strtoupper( $order );
  295. uasort( $this->items, array( $this, '_order_callback' ) );
  296. $plugins_per_page = $this->get_items_per_page( str_replace( '-', '_', $screen->id . '_per_page' ), 999 );
  297. $start = ( $page - 1 ) * $plugins_per_page;
  298. if ( $total_this_page > $plugins_per_page ) {
  299. $this->items = array_slice( $this->items, $start, $plugins_per_page );
  300. }
  301. $this->set_pagination_args(
  302. array(
  303. 'total_items' => $total_this_page,
  304. 'per_page' => $plugins_per_page,
  305. )
  306. );
  307. }
  308. /**
  309. * @global string $s URL encoded search term.
  310. *
  311. * @param array $plugin
  312. * @return bool
  313. */
  314. public function _search_callback( $plugin ) {
  315. global $s;
  316. foreach ( $plugin as $value ) {
  317. if ( is_string( $value ) && false !== stripos( strip_tags( $value ), urldecode( $s ) ) ) {
  318. return true;
  319. }
  320. }
  321. return false;
  322. }
  323. /**
  324. * @global string $orderby
  325. * @global string $order
  326. * @param array $plugin_a
  327. * @param array $plugin_b
  328. * @return int
  329. */
  330. public function _order_callback( $plugin_a, $plugin_b ) {
  331. global $orderby, $order;
  332. $a = $plugin_a[ $orderby ];
  333. $b = $plugin_b[ $orderby ];
  334. if ( $a === $b ) {
  335. return 0;
  336. }
  337. if ( 'DESC' === $order ) {
  338. return strcasecmp( $b, $a );
  339. } else {
  340. return strcasecmp( $a, $b );
  341. }
  342. }
  343. /**
  344. * @global array $plugins
  345. */
  346. public function no_items() {
  347. global $plugins;
  348. if ( ! empty( $_REQUEST['s'] ) ) {
  349. $s = esc_html( wp_unslash( $_REQUEST['s'] ) );
  350. /* translators: %s: Plugin search term. */
  351. printf( __( 'No plugins found for: %s.' ), '<strong>' . $s . '</strong>' );
  352. // We assume that somebody who can install plugins in multisite is experienced enough to not need this helper link.
  353. if ( ! is_multisite() && current_user_can( 'install_plugins' ) ) {
  354. echo ' <a href="' . esc_url( admin_url( 'plugin-install.php?tab=search&s=' . urlencode( $s ) ) ) . '">' . __( 'Search for plugins in the WordPress Plugin Directory.' ) . '</a>';
  355. }
  356. } elseif ( ! empty( $plugins['all'] ) ) {
  357. _e( 'No plugins found.' );
  358. } else {
  359. _e( 'No plugins are currently available.' );
  360. }
  361. }
  362. /**
  363. * Displays the search box.
  364. *
  365. * @since 4.6.0
  366. *
  367. * @param string $text The 'submit' button label.
  368. * @param string $input_id ID attribute value for the search input field.
  369. */
  370. public function search_box( $text, $input_id ) {
  371. if ( empty( $_REQUEST['s'] ) && ! $this->has_items() ) {
  372. return;
  373. }
  374. $input_id = $input_id . '-search-input';
  375. if ( ! empty( $_REQUEST['orderby'] ) ) {
  376. echo '<input type="hidden" name="orderby" value="' . esc_attr( $_REQUEST['orderby'] ) . '" />';
  377. }
  378. if ( ! empty( $_REQUEST['order'] ) ) {
  379. echo '<input type="hidden" name="order" value="' . esc_attr( $_REQUEST['order'] ) . '" />';
  380. }
  381. ?>
  382. <p class="search-box">
  383. <label class="screen-reader-text" for="<?php echo esc_attr( $input_id ); ?>"><?php echo $text; ?>:</label>
  384. <input type="search" id="<?php echo esc_attr( $input_id ); ?>" class="wp-filter-search" name="s" value="<?php _admin_search_query(); ?>" placeholder="<?php esc_attr_e( 'Search installed plugins...' ); ?>" />
  385. <?php submit_button( $text, 'hide-if-js', '', false, array( 'id' => 'search-submit' ) ); ?>
  386. </p>
  387. <?php
  388. }
  389. /**
  390. * @global string $status
  391. * @return array
  392. */
  393. public function get_columns() {
  394. global $status;
  395. $columns = array(
  396. 'cb' => ! in_array( $status, array( 'mustuse', 'dropins' ), true ) ? '<input type="checkbox" />' : '',
  397. 'name' => __( 'Plugin' ),
  398. 'description' => __( 'Description' ),
  399. );
  400. if ( $this->show_autoupdates ) {
  401. $columns['auto-updates'] = __( 'Automatic Updates' );
  402. }
  403. return $columns;
  404. }
  405. /**
  406. * @return array
  407. */
  408. protected function get_sortable_columns() {
  409. return array();
  410. }
  411. /**
  412. * @global array $totals
  413. * @global string $status
  414. * @return array
  415. */
  416. protected function get_views() {
  417. global $totals, $status;
  418. $status_links = array();
  419. foreach ( $totals as $type => $count ) {
  420. if ( ! $count ) {
  421. continue;
  422. }
  423. switch ( $type ) {
  424. case 'all':
  425. /* translators: %s: Number of plugins. */
  426. $text = _nx(
  427. 'All <span class="count">(%s)</span>',
  428. 'All <span class="count">(%s)</span>',
  429. $count,
  430. 'plugins'
  431. );
  432. break;
  433. case 'active':
  434. /* translators: %s: Number of plugins. */
  435. $text = _n(
  436. 'Active <span class="count">(%s)</span>',
  437. 'Active <span class="count">(%s)</span>',
  438. $count
  439. );
  440. break;
  441. case 'recently_activated':
  442. /* translators: %s: Number of plugins. */
  443. $text = _n(
  444. 'Recently Active <span class="count">(%s)</span>',
  445. 'Recently Active <span class="count">(%s)</span>',
  446. $count
  447. );
  448. break;
  449. case 'inactive':
  450. /* translators: %s: Number of plugins. */
  451. $text = _n(
  452. 'Inactive <span class="count">(%s)</span>',
  453. 'Inactive <span class="count">(%s)</span>',
  454. $count
  455. );
  456. break;
  457. case 'mustuse':
  458. /* translators: %s: Number of plugins. */
  459. $text = _n(
  460. 'Must-Use <span class="count">(%s)</span>',
  461. 'Must-Use <span class="count">(%s)</span>',
  462. $count
  463. );
  464. break;
  465. case 'dropins':
  466. /* translators: %s: Number of plugins. */
  467. $text = _n(
  468. 'Drop-in <span class="count">(%s)</span>',
  469. 'Drop-ins <span class="count">(%s)</span>',
  470. $count
  471. );
  472. break;
  473. case 'paused':
  474. /* translators: %s: Number of plugins. */
  475. $text = _n(
  476. 'Paused <span class="count">(%s)</span>',
  477. 'Paused <span class="count">(%s)</span>',
  478. $count
  479. );
  480. break;
  481. case 'upgrade':
  482. /* translators: %s: Number of plugins. */
  483. $text = _n(
  484. 'Update Available <span class="count">(%s)</span>',
  485. 'Update Available <span class="count">(%s)</span>',
  486. $count
  487. );
  488. break;
  489. case 'auto-update-enabled':
  490. /* translators: %s: Number of plugins. */
  491. $text = _n(
  492. 'Auto-updates Enabled <span class="count">(%s)</span>',
  493. 'Auto-updates Enabled <span class="count">(%s)</span>',
  494. $count
  495. );
  496. break;
  497. case 'auto-update-disabled':
  498. /* translators: %s: Number of plugins. */
  499. $text = _n(
  500. 'Auto-updates Disabled <span class="count">(%s)</span>',
  501. 'Auto-updates Disabled <span class="count">(%s)</span>',
  502. $count
  503. );
  504. break;
  505. }
  506. if ( 'search' !== $type ) {
  507. $status_links[ $type ] = sprintf(
  508. "<a href='%s'%s>%s</a>",
  509. add_query_arg( 'plugin_status', $type, 'plugins.php' ),
  510. ( $type === $status ) ? ' class="current" aria-current="page"' : '',
  511. sprintf( $text, number_format_i18n( $count ) )
  512. );
  513. }
  514. }
  515. return $status_links;
  516. }
  517. /**
  518. * @global string $status
  519. * @return array
  520. */
  521. protected function get_bulk_actions() {
  522. global $status;
  523. $actions = array();
  524. if ( 'active' !== $status ) {
  525. $actions['activate-selected'] = $this->screen->in_admin( 'network' ) ? __( 'Network Activate' ) : __( 'Activate' );
  526. }
  527. if ( 'inactive' !== $status && 'recent' !== $status ) {
  528. $actions['deactivate-selected'] = $this->screen->in_admin( 'network' ) ? __( 'Network Deactivate' ) : __( 'Deactivate' );
  529. }
  530. if ( ! is_multisite() || $this->screen->in_admin( 'network' ) ) {
  531. if ( current_user_can( 'update_plugins' ) ) {
  532. $actions['update-selected'] = __( 'Update' );
  533. }
  534. if ( current_user_can( 'delete_plugins' ) && ( 'active' !== $status ) ) {
  535. $actions['delete-selected'] = __( 'Delete' );
  536. }
  537. if ( $this->show_autoupdates ) {
  538. if ( 'auto-update-enabled' !== $status ) {
  539. $actions['enable-auto-update-selected'] = __( 'Enable Auto-updates' );
  540. }
  541. if ( 'auto-update-disabled' !== $status ) {
  542. $actions['disable-auto-update-selected'] = __( 'Disable Auto-updates' );
  543. }
  544. }
  545. }
  546. return $actions;
  547. }
  548. /**
  549. * @global string $status
  550. * @param string $which
  551. */
  552. public function bulk_actions( $which = '' ) {
  553. global $status;
  554. if ( in_array( $status, array( 'mustuse', 'dropins' ), true ) ) {
  555. return;
  556. }
  557. parent::bulk_actions( $which );
  558. }
  559. /**
  560. * @global string $status
  561. * @param string $which
  562. */
  563. protected function extra_tablenav( $which ) {
  564. global $status;
  565. if ( ! in_array( $status, array( 'recently_activated', 'mustuse', 'dropins' ), true ) ) {
  566. return;
  567. }
  568. echo '<div class="alignleft actions">';
  569. if ( 'recently_activated' === $status ) {
  570. submit_button( __( 'Clear List' ), '', 'clear-recent-list', false );
  571. } elseif ( 'top' === $which && 'mustuse' === $status ) {
  572. echo '<p>' . sprintf(
  573. /* translators: %s: mu-plugins directory name. */
  574. __( 'Files in the %s directory are executed automatically.' ),
  575. '<code>' . str_replace( ABSPATH, '/', WPMU_PLUGIN_DIR ) . '</code>'
  576. ) . '</p>';
  577. } elseif ( 'top' === $which && 'dropins' === $status ) {
  578. echo '<p>' . sprintf(
  579. /* translators: %s: wp-content directory name. */
  580. __( 'Drop-ins are single files, found in the %s directory, that replace or enhance WordPress features in ways that are not possible for traditional plugins.' ),
  581. '<code>' . str_replace( ABSPATH, '', WP_CONTENT_DIR ) . '</code>'
  582. ) . '</p>';
  583. }
  584. echo '</div>';
  585. }
  586. /**
  587. * @return string
  588. */
  589. public function current_action() {
  590. if ( isset( $_POST['clear-recent-list'] ) ) {
  591. return 'clear-recent-list';
  592. }
  593. return parent::current_action();
  594. }
  595. /**
  596. * @global string $status
  597. */
  598. public function display_rows() {
  599. global $status;
  600. if ( is_multisite() && ! $this->screen->in_admin( 'network' ) && in_array( $status, array( 'mustuse', 'dropins' ), true ) ) {
  601. return;
  602. }
  603. foreach ( $this->items as $plugin_file => $plugin_data ) {
  604. $this->single_row( array( $plugin_file, $plugin_data ) );
  605. }
  606. }
  607. /**
  608. * @global string $status
  609. * @global int $page
  610. * @global string $s
  611. * @global array $totals
  612. *
  613. * @param array $item
  614. */
  615. public function single_row( $item ) {
  616. global $status, $page, $s, $totals;
  617. static $plugin_id_attrs = array();
  618. list( $plugin_file, $plugin_data ) = $item;
  619. $plugin_slug = isset( $plugin_data['slug'] ) ? $plugin_data['slug'] : sanitize_title( $plugin_data['Name'] );
  620. $plugin_id_attr = $plugin_slug;
  621. // Ensure the ID attribute is unique.
  622. $suffix = 2;
  623. while ( in_array( $plugin_id_attr, $plugin_id_attrs, true ) ) {
  624. $plugin_id_attr = "$plugin_slug-$suffix";
  625. $suffix++;
  626. }
  627. $plugin_id_attrs[] = $plugin_id_attr;
  628. $context = $status;
  629. $screen = $this->screen;
  630. // Pre-order.
  631. $actions = array(
  632. 'deactivate' => '',
  633. 'activate' => '',
  634. 'details' => '',
  635. 'delete' => '',
  636. );
  637. // Do not restrict by default.
  638. $restrict_network_active = false;
  639. $restrict_network_only = false;
  640. if ( 'mustuse' === $context ) {
  641. $is_active = true;
  642. } elseif ( 'dropins' === $context ) {
  643. $dropins = _get_dropins();
  644. $plugin_name = $plugin_file;
  645. if ( $plugin_file !== $plugin_data['Name'] ) {
  646. $plugin_name .= '<br/>' . $plugin_data['Name'];
  647. }
  648. if ( true === ( $dropins[ $plugin_file ][1] ) ) { // Doesn't require a constant.
  649. $is_active = true;
  650. $description = '<p><strong>' . $dropins[ $plugin_file ][0] . '</strong></p>';
  651. } elseif ( defined( $dropins[ $plugin_file ][1] ) && constant( $dropins[ $plugin_file ][1] ) ) { // Constant is true.
  652. $is_active = true;
  653. $description = '<p><strong>' . $dropins[ $plugin_file ][0] . '</strong></p>';
  654. } else {
  655. $is_active = false;
  656. $description = '<p><strong>' . $dropins[ $plugin_file ][0] . ' <span class="error-message">' . __( 'Inactive:' ) . '</span></strong> ' .
  657. sprintf(
  658. /* translators: 1: Drop-in constant name, 2: wp-config.php */
  659. __( 'Requires %1$s in %2$s file.' ),
  660. "<code>define('" . $dropins[ $plugin_file ][1] . "', true);</code>",
  661. '<code>wp-config.php</code>'
  662. ) . '</p>';
  663. }
  664. if ( $plugin_data['Description'] ) {
  665. $description .= '<p>' . $plugin_data['Description'] . '</p>';
  666. }
  667. } else {
  668. if ( $screen->in_admin( 'network' ) ) {
  669. $is_active = is_plugin_active_for_network( $plugin_file );
  670. } else {
  671. $is_active = is_plugin_active( $plugin_file );
  672. $restrict_network_active = ( is_multisite() && is_plugin_active_for_network( $plugin_file ) );
  673. $restrict_network_only = ( is_multisite() && is_network_only_plugin( $plugin_file ) && ! $is_active );
  674. }
  675. if ( $screen->in_admin( 'network' ) ) {
  676. if ( $is_active ) {
  677. if ( current_user_can( 'manage_network_plugins' ) ) {
  678. $actions['deactivate'] = sprintf(
  679. '<a href="%s" id="deactivate-%s" aria-label="%s">%s</a>',
  680. wp_nonce_url( 'plugins.php?action=deactivate&amp;plugin=' . urlencode( $plugin_file ) . '&amp;plugin_status=' . $context . '&amp;paged=' . $page . '&amp;s=' . $s, 'deactivate-plugin_' . $plugin_file ),
  681. esc_attr( $plugin_id_attr ),
  682. /* translators: %s: Plugin name. */
  683. esc_attr( sprintf( _x( 'Network Deactivate %s', 'plugin' ), $plugin_data['Name'] ) ),
  684. __( 'Network Deactivate' )
  685. );
  686. }
  687. } else {
  688. if ( current_user_can( 'manage_network_plugins' ) ) {
  689. $actions['activate'] = sprintf(
  690. '<a href="%s" id="activate-%s" class="edit" aria-label="%s">%s</a>',
  691. wp_nonce_url( 'plugins.php?action=activate&amp;plugin=' . urlencode( $plugin_file ) . '&amp;plugin_status=' . $context . '&amp;paged=' . $page . '&amp;s=' . $s, 'activate-plugin_' . $plugin_file ),
  692. esc_attr( $plugin_id_attr ),
  693. /* translators: %s: Plugin name. */
  694. esc_attr( sprintf( _x( 'Network Activate %s', 'plugin' ), $plugin_data['Name'] ) ),
  695. __( 'Network Activate' )
  696. );
  697. }
  698. if ( current_user_can( 'delete_plugins' ) && ! is_plugin_active( $plugin_file ) ) {
  699. $actions['delete'] = sprintf(
  700. '<a href="%s" id="delete-%s" class="delete" aria-label="%s">%s</a>',
  701. wp_nonce_url( 'plugins.php?action=delete-selected&amp;checked[]=' . urlencode( $plugin_file ) . '&amp;plugin_status=' . $context . '&amp;paged=' . $page . '&amp;s=' . $s, 'bulk-plugins' ),
  702. esc_attr( $plugin_id_attr ),
  703. /* translators: %s: Plugin name. */
  704. esc_attr( sprintf( _x( 'Delete %s', 'plugin' ), $plugin_data['Name'] ) ),
  705. __( 'Delete' )
  706. );
  707. }
  708. }
  709. } else {
  710. if ( $restrict_network_active ) {
  711. $actions = array(
  712. 'network_active' => __( 'Network Active' ),
  713. );
  714. } elseif ( $restrict_network_only ) {
  715. $actions = array(
  716. 'network_only' => __( 'Network Only' ),
  717. );
  718. } elseif ( $is_active ) {
  719. if ( current_user_can( 'deactivate_plugin', $plugin_file ) ) {
  720. $actions['deactivate'] = sprintf(
  721. '<a href="%s" id="deactivate-%s" aria-label="%s">%s</a>',
  722. wp_nonce_url( 'plugins.php?action=deactivate&amp;plugin=' . urlencode( $plugin_file ) . '&amp;plugin_status=' . $context . '&amp;paged=' . $page . '&amp;s=' . $s, 'deactivate-plugin_' . $plugin_file ),
  723. esc_attr( $plugin_id_attr ),
  724. /* translators: %s: Plugin name. */
  725. esc_attr( sprintf( _x( 'Deactivate %s', 'plugin' ), $plugin_data['Name'] ) ),
  726. __( 'Deactivate' )
  727. );
  728. }
  729. if ( current_user_can( 'resume_plugin', $plugin_file ) && is_plugin_paused( $plugin_file ) ) {
  730. $actions['resume'] = sprintf(
  731. '<a href="%s" id="resume-%s" class="resume-link" aria-label="%s">%s</a>',
  732. wp_nonce_url( 'plugins.php?action=resume&amp;plugin=' . urlencode( $plugin_file ) . '&amp;plugin_status=' . $context . '&amp;paged=' . $page . '&amp;s=' . $s, 'resume-plugin_' . $plugin_file ),
  733. esc_attr( $plugin_id_attr ),
  734. /* translators: %s: Plugin name. */
  735. esc_attr( sprintf( _x( 'Resume %s', 'plugin' ), $plugin_data['Name'] ) ),
  736. __( 'Resume' )
  737. );
  738. }
  739. } else {
  740. if ( current_user_can( 'activate_plugin', $plugin_file ) ) {
  741. $actions['activate'] = sprintf(
  742. '<a href="%s" id="activate-%s" class="edit" aria-label="%s">%s</a>',
  743. wp_nonce_url( 'plugins.php?action=activate&amp;plugin=' . urlencode( $plugin_file ) . '&amp;plugin_status=' . $context . '&amp;paged=' . $page . '&amp;s=' . $s, 'activate-plugin_' . $plugin_file ),
  744. esc_attr( $plugin_id_attr ),
  745. /* translators: %s: Plugin name. */
  746. esc_attr( sprintf( _x( 'Activate %s', 'plugin' ), $plugin_data['Name'] ) ),
  747. __( 'Activate' )
  748. );
  749. }
  750. if ( ! is_multisite() && current_user_can( 'delete_plugins' ) ) {
  751. $actions['delete'] = sprintf(
  752. '<a href="%s" id="delete-%s" class="delete" aria-label="%s">%s</a>',
  753. wp_nonce_url( 'plugins.php?action=delete-selected&amp;checked[]=' . urlencode( $plugin_file ) . '&amp;plugin_status=' . $context . '&amp;paged=' . $page . '&amp;s=' . $s, 'bulk-plugins' ),
  754. esc_attr( $plugin_id_attr ),
  755. /* translators: %s: Plugin name. */
  756. esc_attr( sprintf( _x( 'Delete %s', 'plugin' ), $plugin_data['Name'] ) ),
  757. __( 'Delete' )
  758. );
  759. }
  760. } // End if $is_active.
  761. } // End if $screen->in_admin( 'network' ).
  762. } // End if $context.
  763. $actions = array_filter( $actions );
  764. if ( $screen->in_admin( 'network' ) ) {
  765. /**
  766. * Filters the action links displayed for each plugin in the Network Admin Plugins list table.
  767. *
  768. * @since 3.1.0
  769. *
  770. * @param string[] $actions An array of plugin action links. By default this can include 'activate',
  771. * 'deactivate', and 'delete'.
  772. * @param string $plugin_file Path to the plugin file relative to the plugins directory.
  773. * @param array $plugin_data An array of plugin data. See `get_plugin_data()`.
  774. * @param string $context The plugin context. By default this can include 'all', 'active', 'inactive',
  775. * 'recently_activated', 'upgrade', 'mustuse', 'dropins', and 'search'.
  776. */
  777. $actions = apply_filters( 'network_admin_plugin_action_links', $actions, $plugin_file, $plugin_data, $context );
  778. /**
  779. * Filters the list of action links displayed for a specific plugin in the Network Admin Plugins list table.
  780. *
  781. * The dynamic portion of the hook name, `$plugin_file`, refers to the path
  782. * to the plugin file, relative to the plugins directory.
  783. *
  784. * @since 3.1.0
  785. *
  786. * @param string[] $actions An array of plugin action links. By default this can include 'activate',
  787. * 'deactivate', and 'delete'.
  788. * @param string $plugin_file Path to the plugin file relative to the plugins directory.
  789. * @param array $plugin_data An array of plugin data. See `get_plugin_data()`.
  790. * @param string $context The plugin context. By default this can include 'all', 'active', 'inactive',
  791. * 'recently_activated', 'upgrade', 'mustuse', 'dropins', and 'search'.
  792. */
  793. $actions = apply_filters( "network_admin_plugin_action_links_{$plugin_file}", $actions, $plugin_file, $plugin_data, $context );
  794. } else {
  795. /**
  796. * Filters the action links displayed for each plugin in the Plugins list table.
  797. *
  798. * @since 2.5.0
  799. * @since 2.6.0 The `$context` parameter was added.
  800. * @since 4.9.0 The 'Edit' link was removed from the list of action links.
  801. *
  802. * @param string[] $actions An array of plugin action links. By default this can include 'activate',
  803. * 'deactivate', and 'delete'. With Multisite active this can also include
  804. * 'network_active' and 'network_only' items.
  805. * @param string $plugin_file Path to the plugin file relative to the plugins directory.
  806. * @param array $plugin_data An array of plugin data. See `get_plugin_data()`.
  807. * @param string $context The plugin context. By default this can include 'all', 'active', 'inactive',
  808. * 'recently_activated', 'upgrade', 'mustuse', 'dropins', and 'search'.
  809. */
  810. $actions = apply_filters( 'plugin_action_links', $actions, $plugin_file, $plugin_data, $context );
  811. /**
  812. * Filters the list of action links displayed for a specific plugin in the Plugins list table.
  813. *
  814. * The dynamic portion of the hook name, `$plugin_file`, refers to the path
  815. * to the plugin file, relative to the plugins directory.
  816. *
  817. * @since 2.7.0
  818. * @since 4.9.0 The 'Edit' link was removed from the list of action links.
  819. *
  820. * @param string[] $actions An array of plugin action links. By default this can include 'activate',
  821. * 'deactivate', and 'delete'. With Multisite active this can also include
  822. * 'network_active' and 'network_only' items.
  823. * @param string $plugin_file Path to the plugin file relative to the plugins directory.
  824. * @param array $plugin_data An array of plugin data. See `get_plugin_data()`.
  825. * @param string $context The plugin context. By default this can include 'all', 'active', 'inactive',
  826. * 'recently_activated', 'upgrade', 'mustuse', 'dropins', and 'search'.
  827. */
  828. $actions = apply_filters( "plugin_action_links_{$plugin_file}", $actions, $plugin_file, $plugin_data, $context );
  829. }
  830. $requires_php = isset( $plugin_data['requires_php'] ) ? $plugin_data['requires_php'] : null;
  831. $compatible_php = is_php_version_compatible( $requires_php );
  832. $class = $is_active ? 'active' : 'inactive';
  833. $checkbox_id = 'checkbox_' . md5( $plugin_file );
  834. if ( $restrict_network_active || $restrict_network_only || in_array( $status, array( 'mustuse', 'dropins' ), true ) || ! $compatible_php ) {
  835. $checkbox = '';
  836. } else {
  837. $checkbox = sprintf(
  838. '<label class="screen-reader-text" for="%1$s">%2$s</label>' .
  839. '<input type="checkbox" name="checked[]" value="%3$s" id="%1$s" />',
  840. $checkbox_id,
  841. /* translators: %s: Plugin name. */
  842. sprintf( __( 'Select %s' ), $plugin_data['Name'] ),
  843. esc_attr( $plugin_file )
  844. );
  845. }
  846. if ( 'dropins' !== $context ) {
  847. $description = '<p>' . ( $plugin_data['Description'] ? $plugin_data['Description'] : '&nbsp;' ) . '</p>';
  848. $plugin_name = $plugin_data['Name'];
  849. }
  850. if ( ! empty( $totals['upgrade'] ) && ! empty( $plugin_data['update'] ) ) {
  851. $class .= ' update';
  852. }
  853. $paused = ! $screen->in_admin( 'network' ) && is_plugin_paused( $plugin_file );
  854. if ( $paused ) {
  855. $class .= ' paused';
  856. }
  857. if ( is_uninstallable_plugin( $plugin_file ) ) {
  858. $class .= ' is-uninstallable';
  859. }
  860. printf(
  861. '<tr class="%s" data-slug="%s" data-plugin="%s">',
  862. esc_attr( $class ),
  863. esc_attr( $plugin_slug ),
  864. esc_attr( $plugin_file )
  865. );
  866. list( $columns, $hidden, $sortable, $primary ) = $this->get_column_info();
  867. $auto_updates = (array) get_site_option( 'auto_update_plugins', array() );
  868. $available_updates = get_site_transient( 'update_plugins' );
  869. foreach ( $columns as $column_name => $column_display_name ) {
  870. $extra_classes = '';
  871. if ( in_array( $column_name, $hidden, true ) ) {
  872. $extra_classes = ' hidden';
  873. }
  874. switch ( $column_name ) {
  875. case 'cb':
  876. echo "<th scope='row' class='check-column'>$checkbox</th>";
  877. break;
  878. case 'name':
  879. echo "<td class='plugin-title column-primary'><strong>$plugin_name</strong>";
  880. echo $this->row_actions( $actions, true );
  881. echo '</td>';
  882. break;
  883. case 'description':
  884. $classes = 'column-description desc';
  885. echo "<td class='$classes{$extra_classes}'>
  886. <div class='plugin-description'>$description</div>
  887. <div class='$class second plugin-version-author-uri'>";
  888. $plugin_meta = array();
  889. if ( ! empty( $plugin_data['Version'] ) ) {
  890. /* translators: %s: Plugin version number. */
  891. $plugin_meta[] = sprintf( __( 'Version %s' ), $plugin_data['Version'] );
  892. }
  893. if ( ! empty( $plugin_data['Author'] ) ) {
  894. $author = $plugin_data['Author'];
  895. if ( ! empty( $plugin_data['AuthorURI'] ) ) {
  896. $author = '<a href="' . $plugin_data['AuthorURI'] . '">' . $plugin_data['Author'] . '</a>';
  897. }
  898. /* translators: %s: Plugin author name. */
  899. $plugin_meta[] = sprintf( __( 'By %s' ), $author );
  900. }
  901. // Details link using API info, if available.
  902. if ( isset( $plugin_data['slug'] ) && current_user_can( 'install_plugins' ) ) {
  903. $plugin_meta[] = sprintf(
  904. '<a href="%s" class="thickbox open-plugin-details-modal" aria-label="%s" data-title="%s">%s</a>',
  905. esc_url(
  906. network_admin_url(
  907. 'plugin-install.php?tab=plugin-information&plugin=' . $plugin_data['slug'] .
  908. '&TB_iframe=true&width=600&height=550'
  909. )
  910. ),
  911. /* translators: %s: Plugin name. */
  912. esc_attr( sprintf( __( 'More information about %s' ), $plugin_name ) ),
  913. esc_attr( $plugin_name ),
  914. __( 'View details' )
  915. );
  916. } elseif ( ! empty( $plugin_data['PluginURI'] ) ) {
  917. $plugin_meta[] = sprintf(
  918. '<a href="%s">%s</a>',
  919. esc_url( $plugin_data['PluginURI'] ),
  920. __( 'Visit plugin site' )
  921. );
  922. }
  923. /**
  924. * Filters the array of row meta for each plugin in the Plugins list table.
  925. *
  926. * @since 2.8.0
  927. *
  928. * @param string[] $plugin_meta An array of the plugin's metadata, including
  929. * the version, author, author URI, and plugin URI.
  930. * @param string $plugin_file Path to the plugin file relative to the plugins directory.
  931. * @param array $plugin_data An array of plugin data.
  932. * @param string $status Status filter currently applied to the plugin list. Possible
  933. * values are: 'all', 'active', 'inactive', 'recently_activated',
  934. * 'upgrade', 'mustuse', 'dropins', 'search', 'paused',
  935. * 'auto-update-enabled', 'auto-update-disabled'.
  936. */
  937. $plugin_meta = apply_filters( 'plugin_row_meta', $plugin_meta, $plugin_file, $plugin_data, $status );
  938. echo implode( ' | ', $plugin_meta );
  939. echo '</div>';
  940. if ( $paused ) {
  941. $notice_text = __( 'This plugin failed to load properly and is paused during recovery mode.' );
  942. printf( '<p><span class="dashicons dashicons-warning"></span> <strong>%s</strong></p>', $notice_text );
  943. $error = wp_get_plugin_error( $plugin_file );
  944. if ( false !== $error ) {
  945. printf( '<div class="error-display"><p>%s</p></div>', wp_get_extension_error_description( $error ) );
  946. }
  947. }
  948. echo '</td>';
  949. break;
  950. case 'auto-updates':
  951. if ( ! $this->show_autoupdates ) {
  952. break;
  953. }
  954. echo "<td class='column-auto-updates{$extra_classes}'>";
  955. $html = array();
  956. if ( isset( $plugin_data['auto-update-forced'] ) ) {
  957. if ( $plugin_data['auto-update-forced'] ) {
  958. // Forced on.
  959. $text = __( 'Auto-updates enabled' );
  960. } else {
  961. $text = __( 'Auto-updates disabled' );
  962. }
  963. $action = 'unavailable';
  964. $time_class = ' hidden';
  965. } elseif ( empty( $plugin_data['update-supported'] ) ) {
  966. $text = '';
  967. $action = 'unavailable';
  968. $time_class = ' hidden';
  969. } elseif ( in_array( $plugin_file, $auto_updates, true ) ) {
  970. $text = __( 'Disable auto-updates' );
  971. $action = 'disable';
  972. $time_class = '';
  973. } else {
  974. $text = __( 'Enable auto-updates' );
  975. $action = 'enable';
  976. $time_class = ' hidden';
  977. }
  978. $query_args = array(
  979. 'action' => "{$action}-auto-update",
  980. 'plugin' => $plugin_file,
  981. 'paged' => $page,
  982. 'plugin_status' => $status,
  983. );
  984. $url = add_query_arg( $query_args, 'plugins.php' );
  985. if ( 'unavailable' === $action ) {
  986. $html[] = '<span class="label">' . $text . '</span>';
  987. } else {
  988. $html[] = sprintf(
  989. '<a href="%s" class="toggle-auto-update aria-button-if-js" data-wp-action="%s">',
  990. wp_nonce_url( $url, 'updates' ),
  991. $action
  992. );
  993. $html[] = '<span class="dashicons dashicons-update spin hidden" aria-hidden="true"></span>';
  994. $html[] = '<span class="label">' . $text . '</span>';
  995. $html[] = '</a>';
  996. }
  997. if ( ! empty( $plugin_data['update'] ) ) {
  998. $html[] = sprintf(
  999. '<div class="auto-update-time%s">%s</div>',
  1000. $time_class,
  1001. wp_get_auto_update_message()
  1002. );
  1003. }
  1004. $html = implode( '', $html );
  1005. /**
  1006. * Filters the HTML of the auto-updates setting for each plugin in the Plugins list table.
  1007. *
  1008. * @since 5.5.0
  1009. *
  1010. * @param string $html The HTML of the plugin's auto-update column content, including
  1011. * toggle auto-update action links and time to next update.
  1012. * @param string $plugin_file Path to the plugin file relative to the plugins directory.
  1013. * @param array $plugin_data An array of plugin data.
  1014. */
  1015. echo apply_filters( 'plugin_auto_update_setting_html', $html, $plugin_file, $plugin_data );
  1016. echo '<div class="notice notice-error notice-alt inline hidden"><p></p></div>';
  1017. echo '</td>';
  1018. break;
  1019. default:
  1020. $classes = "$column_name column-$column_name $class";
  1021. echo "<td class='$classes{$extra_classes}'>";
  1022. /**
  1023. * Fires inside each custom column of the Plugins list table.
  1024. *
  1025. * @since 3.1.0
  1026. *
  1027. * @param string $column_name Name of the column.
  1028. * @param string $plugin_file Path to the plugin file relative to the plugins directory.
  1029. * @param array $plugin_data An array of plugin data.
  1030. */
  1031. do_action( 'manage_plugins_custom_column', $column_name, $plugin_file, $plugin_data );
  1032. echo '</td>';
  1033. }
  1034. }
  1035. echo '</tr>';
  1036. /**
  1037. * Fires after each row in the Plugins list table.
  1038. *
  1039. * @since 2.3.0
  1040. * @since 5.5.0 Added 'auto-update-enabled' and 'auto-update-disabled' to possible values for `$status`.
  1041. *
  1042. * @param string $plugin_file Path to the plugin file relative to the plugins directory.
  1043. * @param array $plugin_data An array of plugin data.
  1044. * @param string $status Status filter currently applied to the plugin list. Possible
  1045. * values are: 'all', 'active', 'inactive', 'recently_activated',
  1046. * 'upgrade', 'mustuse', 'dropins', 'search', 'paused',
  1047. * 'auto-update-enabled', 'auto-update-disabled'.
  1048. */
  1049. do_action( 'after_plugin_row', $plugin_file, $plugin_data, $status );
  1050. /**
  1051. * Fires after each specific row in the Plugins list table.
  1052. *
  1053. * The dynamic portion of the hook name, `$plugin_file`, refers to the path
  1054. * to the plugin file, relative to the plugins directory.
  1055. *
  1056. * @since 2.7.0
  1057. * @since 5.5.0 Added 'auto-update-enabled' and 'auto-update-disabled' to possible values for `$status`.
  1058. *
  1059. * @param string $plugin_file Path to the plugin file relative to the plugins directory.
  1060. * @param array $plugin_data An array of plugin data.
  1061. * @param string $status Status filter currently applied to the plugin list. Possible
  1062. * values are: 'all', 'active', 'inactive', 'recently_activated',
  1063. * 'upgrade', 'mustuse', 'dropins', 'search', 'paused',
  1064. * 'auto-update-enabled', 'auto-update-disabled'.
  1065. */
  1066. do_action( "after_plugin_row_{$plugin_file}", $plugin_file, $plugin_data, $status );
  1067. }
  1068. /**
  1069. * Gets the name of the primary column for this specific list table.
  1070. *
  1071. * @since 4.3.0
  1072. *
  1073. * @return string Unalterable name for the primary column, in this case, 'name'.
  1074. */
  1075. protected function get_primary_column_name() {
  1076. return 'name';
  1077. }
  1078. }