Initial revision.
This commit is contained in:
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
*.py[cod]
|
||||
.idea/
|
||||
*.orig
|
||||
5
CHANGELOG.md
Normal file
5
CHANGELOG.md
Normal file
@@ -0,0 +1,5 @@
|
||||
# Changelog
|
||||
|
||||
## 0.1.0
|
||||
|
||||
- Initial release
|
||||
21
LICENSE.md
Normal file
21
LICENSE.md
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License (MIT)
|
||||
|
||||
Copyright (c) 2014 Scott Vitale
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
2
MANIFEST.in
Normal file
2
MANIFEST.in
Normal file
@@ -0,0 +1,2 @@
|
||||
include *.md
|
||||
recursive-include pwa/templates *
|
||||
91
README.md
Normal file
91
README.md
Normal file
@@ -0,0 +1,91 @@
|
||||
django-progressive-web-app
|
||||
=====
|
||||
This Django app turns your project into a [progressive web app](https://developers.google.com/web/progressive-web-apps/). Navigating to your site on an Android phone will prompt you to add the app to your home screen.
|
||||
|
||||

|
||||
|
||||
Launching the app from your home screen will display your app [without browser chrome](images/screenshot2.png). As such, it's critical that your application provides all navigation within the HTML (no reliance on the browser back or forward button).
|
||||
|
||||
Requirements
|
||||
=====
|
||||
Progressive Web Apps require HTTPS unless being served from localhost. If you're not already using HTTPS on your site, check out [Let's Encrypt](https://letsencrypt.org/) and [ZeroSSL](https://zerossl.com/).
|
||||
|
||||
Installation
|
||||
=====
|
||||
Install from PyPI:
|
||||
|
||||
```
|
||||
pip install django-progressive-web-app
|
||||
```
|
||||
|
||||
Configuration
|
||||
=====
|
||||
Add `pwa` to your list of `INSTALLED_APPS` in settings.py:
|
||||
|
||||
```python
|
||||
INSTALLED_APPS = [
|
||||
...
|
||||
'pwa',
|
||||
...
|
||||
]
|
||||
```
|
||||
|
||||
Configure your app name, description, and icons in settings.py:
|
||||
```python
|
||||
PWA_APP_NAME = 'My Kickass App'
|
||||
PWA_APP_DESCRIPTION = "Do kickass things all day long without that pesky browser chrome"
|
||||
PWA_APP_THEME_COLOR = '#0A0302'
|
||||
PWA_APP_ICONS = [
|
||||
{
|
||||
'src': '/static/images/my_app_icon.png',
|
||||
'sizes': '160x160'
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
Add the progressive web app URLs to urls.py:
|
||||
```python
|
||||
from django.conf.urls import url, include
|
||||
|
||||
urlpatterns = [
|
||||
...
|
||||
url('', include('pwa.urls')), # You MUST use an empty string as the URL prefix
|
||||
...
|
||||
]
|
||||
```
|
||||
|
||||
Inject the required meta tags in your base.html (or wherever your HTML <head> is defined):
|
||||
```html
|
||||
{% load pwa %}
|
||||
|
||||
<head>
|
||||
...
|
||||
{% progressive_web_app_meta %}
|
||||
...
|
||||
</head>
|
||||
```
|
||||
|
||||
Troubleshooting
|
||||
=====
|
||||
While running the Django test server:
|
||||
1. Verify that `/manifest.json` is being served
|
||||
1. Verify that `/serviceworker.js` is being served
|
||||
1. Use the Application tab in the Chrome Developer Tools to verify the progressive web app is configured correctly.
|
||||
1. Use the "Add to homescreen" link on the Application Tab to verify you can add the app successfully.
|
||||
|
||||
Adding Your Own Service Worker
|
||||
=====
|
||||
By default, the service worker implemented by this app is empty. To add service worker functionality, you'll want to create a `serviceworker.js` or similarly named file, and then point at it using the PWA_SERVICE_WORKER_PATH variable.
|
||||
|
||||
```python
|
||||
PWA_SERVICE_WORKER_PATH = os.path.join(BASE_DIR, 'my_app', 'serviceworker.js')
|
||||
|
||||
```
|
||||
|
||||
Feedback
|
||||
=====
|
||||
I welcome your feedback and pull requests. Enjoy!
|
||||
|
||||
License
|
||||
=====
|
||||
All files in this repository are distributed under the MIT license.
|
||||
BIN
images/screenshot1.png
Normal file
BIN
images/screenshot1.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 43 KiB |
BIN
images/screenshot2.png
Normal file
BIN
images/screenshot2.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 47 KiB |
0
pwa/__init__.py
Normal file
0
pwa/__init__.py
Normal file
21
pwa/app_settings.py
Normal file
21
pwa/app_settings.py
Normal file
@@ -0,0 +1,21 @@
|
||||
""" Settings required by django-progressive-web-app. """
|
||||
from django.conf import settings
|
||||
import os
|
||||
|
||||
# Path to the service worker implementation. Default implementation is empty.
|
||||
PWA_SERVICE_WORKER_PATH = getattr(settings, 'PWA_SERVICE_WORKER_PATH',
|
||||
os.path.join(os.path.abspath(os.path.dirname(__file__)), 'templates', 'serviceworker.js'))
|
||||
|
||||
# App parameters to include in manifest.json and appropriate meta tags
|
||||
PWA_APP_NAME = getattr(settings, 'PWA_APP_NAME', 'MyApp')
|
||||
PWA_APP_DESCRIPTION = getattr(settings, 'PWA_APP_DESCRIPTION', 'My Progressive Web App')
|
||||
PWA_APP_ROOT_URL = getattr(settings, 'PWA_APP_ROOT_URL', '/')
|
||||
PWA_APP_THEME_COLOR = getattr(settings, 'PWA_APP_THEME_COLOR', '#000')
|
||||
PWA_APP_ICONS = getattr(settings, 'PWA_APP_ICONS', [
|
||||
{
|
||||
'src': '/',
|
||||
'sizes': '160x160'
|
||||
}
|
||||
])
|
||||
|
||||
|
||||
5
pwa/apps.py
Normal file
5
pwa/apps.py
Normal file
@@ -0,0 +1,5 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class PwaConfig(AppConfig):
|
||||
name = 'pwa'
|
||||
11
pwa/templates/manifest.json
Normal file
11
pwa/templates/manifest.json
Normal file
@@ -0,0 +1,11 @@
|
||||
{% load pwa %}
|
||||
{
|
||||
"name": {{ PWA_APP_NAME|js }},
|
||||
"short_name": {{ PWA_APP_NAME|js }},
|
||||
"description": {{ PWA_APP_DESCRIPTION|js }},
|
||||
"start_url": "/",
|
||||
"display": "standalone",
|
||||
"background_color": "#fff",
|
||||
"theme_color": {{ PWA_APP_THEME_COLOR|js }},
|
||||
"icons": {{ PWA_APP_ICONS|js }}
|
||||
}
|
||||
27
pwa/templates/pwa.html
Normal file
27
pwa/templates/pwa.html
Normal file
@@ -0,0 +1,27 @@
|
||||
<!-- Path to manifest.json -->
|
||||
<link rel="manifest" href="/manifest.json">
|
||||
|
||||
<!-- Icons for Apple Devices -->
|
||||
{% for icon in PWA_APP_ICONS %}
|
||||
<link rel="apple-touch-icon" href="{{ icon.src }}" sizes="{{ icon.sizes }}">
|
||||
{% endfor %}
|
||||
|
||||
<meta name="theme-color" content="{{ PWA_APP_THEME_COLOR }}">
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
<meta name="apple-mobile-web-app-title" content="{{ PWA_APP_NAME }}">
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="default">
|
||||
|
||||
<script type="text/javascript">
|
||||
// Initialize the service worker
|
||||
if ('serviceWorker' in navigator) {
|
||||
window.addEventListener('load', function () {
|
||||
navigator.serviceWorker.register('/serviceworker.js').then(function (registration) {
|
||||
// Registration was successful
|
||||
console.log('django-progressive-web-app: ServiceWorker registration successful with scope: ', registration.scope);
|
||||
}).catch(function (err) {
|
||||
// registration failed :(
|
||||
console.log('django-progressive-web-app: ServiceWorker registration failed: ', err);
|
||||
});
|
||||
});
|
||||
}
|
||||
</script>
|
||||
2
pwa/templates/serviceworker.js
Normal file
2
pwa/templates/serviceworker.js
Normal file
@@ -0,0 +1,2 @@
|
||||
// Empty Service Worker implementation. To use your own Service Worker, set the PWA_SERVICE_WORKER_PATH variable in
|
||||
// settings.py
|
||||
0
pwa/templatetags/__init__.py
Normal file
0
pwa/templatetags/__init__.py
Normal file
25
pwa/templatetags/pwa.py
Normal file
25
pwa/templatetags/pwa.py
Normal file
@@ -0,0 +1,25 @@
|
||||
import json
|
||||
|
||||
from django import template
|
||||
from django.core.serializers.json import DjangoJSONEncoder
|
||||
from django.utils.safestring import mark_safe
|
||||
|
||||
from .. import app_settings
|
||||
|
||||
register = template.Library()
|
||||
|
||||
|
||||
@register.filter(is_safe=True)
|
||||
def js(obj):
|
||||
""" Transform a python object so it can be safely used in javascript/JSON. """
|
||||
return mark_safe(json.dumps(obj, cls=DjangoJSONEncoder))
|
||||
|
||||
|
||||
@register.inclusion_tag('pwa.html', takes_context=True)
|
||||
def progressive_web_app_meta(context):
|
||||
# Pass all PWA_* settings into the template
|
||||
return {
|
||||
setting_name: getattr(app_settings, setting_name)
|
||||
for setting_name in dir(app_settings)
|
||||
if setting_name.startswith('PWA_')
|
||||
}
|
||||
8
pwa/urls.py
Normal file
8
pwa/urls.py
Normal file
@@ -0,0 +1,8 @@
|
||||
from django.conf.urls import url
|
||||
from .views import manifest, service_worker
|
||||
|
||||
# Serve up serviceworker.js and manifest.json at the root
|
||||
urlpatterns = [
|
||||
url('^serviceworker.js$', service_worker),
|
||||
url('^manifest.json$', manifest)
|
||||
]
|
||||
17
pwa/views.py
Normal file
17
pwa/views.py
Normal file
@@ -0,0 +1,17 @@
|
||||
from django.http import HttpResponse
|
||||
from django.shortcuts import render
|
||||
|
||||
from . import app_settings
|
||||
|
||||
|
||||
def service_worker(request):
|
||||
response = HttpResponse(open(app_settings.PWA_SERVICE_WORKER_PATH).read(), content_type='application/javascript')
|
||||
return response
|
||||
|
||||
|
||||
def manifest(request):
|
||||
return render(request, 'manifest.json', {
|
||||
setting_name: getattr(app_settings, setting_name)
|
||||
for setting_name in dir(app_settings)
|
||||
if setting_name.startswith('PWA_')
|
||||
})
|
||||
1
requirements-dev.txt
Normal file
1
requirements-dev.txt
Normal file
@@ -0,0 +1 @@
|
||||
pypandoc==1.3.3
|
||||
45
setup.py
Normal file
45
setup.py
Normal file
@@ -0,0 +1,45 @@
|
||||
import os
|
||||
from setuptools import find_packages, setup
|
||||
|
||||
short_description = 'A Django app to include a manifest.json and Service Worker instance to enable progressive web ' \
|
||||
'app behavior '
|
||||
|
||||
# noinspection PyBroadException
|
||||
try:
|
||||
# noinspection PyPackageRequirements
|
||||
import pypandoc
|
||||
|
||||
long_description = pypandoc.convert('README.md', 'rst')
|
||||
except:
|
||||
long_description = short_description
|
||||
|
||||
# allow setup.py to be run from any path
|
||||
os.chdir(os.path.normpath(os.path.join(os.path.abspath(__file__), os.pardir)))
|
||||
|
||||
setup(
|
||||
name='django-progressive-web-app',
|
||||
version='0.1',
|
||||
packages=find_packages(),
|
||||
include_package_data=True,
|
||||
license='MIT License',
|
||||
description=short_description,
|
||||
long_description=long_description,
|
||||
url='http://github.com/svvitale/django-progressive-web-app',
|
||||
author='Scott Vitale',
|
||||
author_email='svvitale@gmail.com',
|
||||
classifiers=[
|
||||
'Environment :: Web Environment',
|
||||
'Framework :: Django',
|
||||
'Framework :: Django :: 1.10',
|
||||
'Intended Audience :: Developers',
|
||||
'License :: OSI Approved :: MIT License',
|
||||
'Operating System :: OS Independent',
|
||||
'Programming Language :: Python',
|
||||
'Programming Language :: Python :: 3',
|
||||
'Programming Language :: Python :: 3.4',
|
||||
'Programming Language :: Python :: 3.5',
|
||||
'Programming Language :: Python :: 3.6',
|
||||
'Topic :: Internet :: WWW/HTTP',
|
||||
'Topic :: Internet :: WWW/HTTP :: Dynamic Content',
|
||||
],
|
||||
)
|
||||
Reference in New Issue
Block a user