Compare commits
39 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
667a5fdb46 | ||
|
|
86e3e185f7 | ||
|
|
6da81f2115 | ||
|
|
d12b1e80ac | ||
|
|
880033d3a5 | ||
|
|
c15074873b | ||
|
|
8e912f9738 | ||
|
|
70b4f2e9b6 | ||
|
|
124fe21d06 | ||
|
|
738eb869fc | ||
|
|
32566873a4 | ||
|
|
bbde0d7005 | ||
|
|
11ae827643 | ||
|
|
337a8f9a08 | ||
|
|
51005db0ca | ||
|
|
02e78b1a6c | ||
|
|
b29c5964af | ||
|
|
5c7f1d7037 | ||
|
|
7666e781e5 | ||
|
|
27eeca4f8f | ||
|
|
a8bf966933 | ||
|
|
202df097b0 | ||
|
|
0de152f292 | ||
|
|
1fd41a622a | ||
|
|
b87ba745e7 | ||
|
|
69e81bd808 | ||
|
|
91b0e4d400 | ||
|
|
a86cc67520 | ||
|
|
271b4239a2 | ||
|
|
ca5ba2d0f8 | ||
|
|
95860405f3 | ||
|
|
ff2494a33e | ||
|
|
9c8ad2087e | ||
|
|
a8ef50d8d0 | ||
|
|
5c17f753e7 | ||
|
|
ed42468767 | ||
|
|
a19d0f9545 | ||
|
|
c4bb0b8fd1 | ||
|
|
86791e12f3 |
2
.coveragerc
Normal file
2
.coveragerc
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
[run]
|
||||||
|
source = jsignature
|
||||||
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
*.pyc
|
||||||
|
.coverage
|
||||||
|
htmlcov
|
||||||
32
.travis.yml
Normal file
32
.travis.yml
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
language: python
|
||||||
|
|
||||||
|
python:
|
||||||
|
- 2.7
|
||||||
|
- 3.3
|
||||||
|
- 3.4
|
||||||
|
- 3.5
|
||||||
|
|
||||||
|
env:
|
||||||
|
- DJANGO_VERSION=1.7 MODULE=jsignature.tests
|
||||||
|
- DJANGO_VERSION=1.8 MODULE=jsignature.tests
|
||||||
|
- DJANGO_VERSION=1.9 MODULE=jsignature.tests
|
||||||
|
- DJANGO_VERSION=1.10 MODULE=jsignature.tests
|
||||||
|
|
||||||
|
install:
|
||||||
|
- pip install -r requirements.txt
|
||||||
|
- pip install -q Django==$DJANGO_VERSION
|
||||||
|
- pip install coverage
|
||||||
|
|
||||||
|
script: coverage run quicktest.py $MODULE
|
||||||
|
|
||||||
|
after_success:
|
||||||
|
- pip install coveralls
|
||||||
|
- coveralls
|
||||||
|
|
||||||
|
# We need to exclude old versions of Python for tests with Django >= 1.9.
|
||||||
|
matrix:
|
||||||
|
exclude:
|
||||||
|
- python: 3.3
|
||||||
|
env: DJANGO_VERSION=1.9 MODULE=jsignature.tests
|
||||||
|
- python: 3.3
|
||||||
|
env: DJANGO_VERSION=1.10 MODULE=jsignature.tests
|
||||||
24
CHANGES
Normal file
24
CHANGES
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
=========
|
||||||
|
CHANGELOG
|
||||||
|
=========
|
||||||
|
|
||||||
|
0.8 (2014-12-04)
|
||||||
|
==================
|
||||||
|
|
||||||
|
** New **
|
||||||
|
|
||||||
|
- Add support for Python 3 (@Gagaro)
|
||||||
|
- Add support for Django 1.7 (@Gagaro)
|
||||||
|
|
||||||
|
|
||||||
|
0.7.6 (2014-11-26)
|
||||||
|
==================
|
||||||
|
|
||||||
|
** New features **
|
||||||
|
|
||||||
|
- A setting to display (or not) the reset button has been added (@jsayles)
|
||||||
|
|
||||||
|
** Internal changes **
|
||||||
|
|
||||||
|
- Rendering is now based on a template (@andybak)
|
||||||
|
- Javascript is properly initialized (@andybak)
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
include README.rst LICENSE
|
include README.rst LICENSE CHANGES
|
||||||
recursive-include jsignature/templates *.html
|
|
||||||
recursive-include jsignature/static *.js
|
recursive-include jsignature/static *.js
|
||||||
recursive-include jsignature/static *.css
|
recursive-include jsignature/locale *.mo
|
||||||
|
recursive-include jsignature/templates *.html
|
||||||
|
|||||||
@@ -1,4 +0,0 @@
|
|||||||
django-jsignature
|
|
||||||
=================
|
|
||||||
|
|
||||||
Use jSignature jQuery plugin in your django projects
|
|
||||||
143
README.rst
143
README.rst
@@ -1,12 +1,145 @@
|
|||||||
==================
|
|
||||||
django-jsignature
|
|
||||||
==================
|
|
||||||
|
|
||||||
A simple way to use `jSignature jQuery plugin <https://github.com/brinley/jSignature/blob/master/README.md>`_ in your `Django <https://www.djangoproject.com>`_ projects.
|
A simple way to use `jSignature jQuery plugin <https://github.com/brinley/jSignature/blob/master/README.md>`_ in your `Django <https://www.djangoproject.com>`_ projects.
|
||||||
|
|
||||||
It provides:
|
It provides:
|
||||||
|
|
||||||
* A form field and a form widget to handle jquery plugin through a Django form;
|
* A form field and a form widget to handle jquery plugin through a Django form;
|
||||||
* A model field to store a captured signature;
|
* A model field to store a captured signature;
|
||||||
* A mixin adding two fields (signature / signature_date) in any of your Django models;
|
* A mixin adding two fields (signature / signature_date) in any of your Django models.
|
||||||
|
|
||||||
|
.. image:: https://travis-ci.org/fle/django-jsignature.png?branch=master
|
||||||
|
:target: https://travis-ci.org/fle/django-jsignature
|
||||||
|
|
||||||
|
.. image:: https://coveralls.io/repos/fle/django-jsignature/badge.png
|
||||||
|
:target: https://coveralls.io/r/fle/django-jsignature
|
||||||
|
|
||||||
|
|
||||||
|
.. image:: https://github.com/fle/django-jsignature/blob/master/screen.png
|
||||||
|
|
||||||
|
==================
|
||||||
|
INSTALL
|
||||||
|
==================
|
||||||
|
|
||||||
|
For now:
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
pip install django-jsignature
|
||||||
|
|
||||||
|
==================
|
||||||
|
USAGE
|
||||||
|
==================
|
||||||
|
|
||||||
|
* Add ``jsignature`` to your ``INSTALLED_APPS``:
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
# settings.py
|
||||||
|
INSTALLED_APPS = (
|
||||||
|
...
|
||||||
|
'jsignature',
|
||||||
|
)
|
||||||
|
|
||||||
|
* Use provided form field and widget:
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
# forms.py
|
||||||
|
from django import forms
|
||||||
|
from jsignature.forms import JSignatureField
|
||||||
|
|
||||||
|
class SignatureForm(forms.Form):
|
||||||
|
signature = JSignatureField()
|
||||||
|
|
||||||
|
* In your template
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
{{ form.media }}
|
||||||
|
<form action="." method="POST">
|
||||||
|
{% for field in form %}
|
||||||
|
{{ field.label_tag }}
|
||||||
|
{{ field }}
|
||||||
|
{% endfor %}
|
||||||
|
<input type="submit" value="Save"/>
|
||||||
|
{% csrf_token %}
|
||||||
|
</form>
|
||||||
|
|
||||||
|
* Render image after form validation:
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
# views.py
|
||||||
|
from jsignature.utils import draw_signature
|
||||||
|
from myapp.forms import SignatureForm
|
||||||
|
|
||||||
|
def my_view(request):
|
||||||
|
form = SignatureForm(request.POST or None)
|
||||||
|
if form.is_valid():
|
||||||
|
signature = form.cleaned_data.get('signature')
|
||||||
|
if signature:
|
||||||
|
# as an image
|
||||||
|
signature_picture = draw_signature(signature)
|
||||||
|
# or as a file
|
||||||
|
signature_file_path = draw_signature(signature, as_file=True)
|
||||||
|
|
||||||
|
==================
|
||||||
|
CUSTOMIZATION
|
||||||
|
==================
|
||||||
|
|
||||||
|
JSignature plugin options are available in python:
|
||||||
|
|
||||||
|
* Globally, in your settings:
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
# settings.py
|
||||||
|
JSIGNATURE_WIDTH = 500
|
||||||
|
JSIGNATURE_HEIGHT = 200
|
||||||
|
|
||||||
|
* Specifically, in your form:
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
# forms.py
|
||||||
|
from jsignature.forms import JSignatureField
|
||||||
|
from jsignature.widgets import JSignatureWidget
|
||||||
|
|
||||||
|
JSignatureField(widget=JSignatureWidget(jsignature_attrs={'color': '#CCC'}))
|
||||||
|
|
||||||
|
Available settings are:
|
||||||
|
|
||||||
|
* ``JSIGNATURE_WIDTH`` (width)
|
||||||
|
* ``JSIGNATURE_HEIGHT`` (height)
|
||||||
|
* ``JSIGNATURE_COLOR`` (color)
|
||||||
|
* ``JSIGNATURE_BACKGROUND_COLOR`` (background-color)
|
||||||
|
* ``JSIGNATURE_DECOR_COLOR`` (decor-color)
|
||||||
|
* ``JSIGNATURE_LINE_WIDTH`` (lineWidth)
|
||||||
|
* ``JSIGNATURE_UNDO_BUTTON`` (UndoButton)
|
||||||
|
* ``JSIGNATURE_RESET_BUTTON`` (ResetButton)
|
||||||
|
|
||||||
|
==================
|
||||||
|
IN YOUR MODELS
|
||||||
|
==================
|
||||||
|
|
||||||
|
If you wan to store signatures, provided mixin gives a ``signature`` and a ``signature_date`` that update themselves:
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
from django.db import models
|
||||||
|
from jsignature.mixins import JSignatureFieldsMixin
|
||||||
|
|
||||||
|
class JSignatureModel(JSignatureFieldsMixin):
|
||||||
|
name = models.CharField()
|
||||||
|
|
||||||
|
|
||||||
|
==================
|
||||||
|
AUTHORS
|
||||||
|
==================
|
||||||
|
|
||||||
|
* Florent Lebreton <florent.lebreton@makina-corpus.com>
|
||||||
|
|
||||||
|
|makinacom|_
|
||||||
|
|
||||||
|
.. |makinacom| image:: http://depot.makina-corpus.org/public/logo.gif
|
||||||
|
.. _makinacom: http://www.makina-corpus.com
|
||||||
|
|
||||||
|
|||||||
@@ -3,27 +3,27 @@
|
|||||||
with jSignature jQuery plugin
|
with jSignature jQuery plugin
|
||||||
"""
|
"""
|
||||||
import json
|
import json
|
||||||
|
import six
|
||||||
|
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.core import validators
|
|
||||||
from django.core.exceptions import ValidationError
|
from django.core.exceptions import ValidationError
|
||||||
from .forms import JSignatureField as JSignatureFormField
|
|
||||||
|
from .forms import (
|
||||||
|
JSignatureField as JSignatureFormField,
|
||||||
|
JSIGNATURE_EMPTY_VALUES)
|
||||||
|
|
||||||
|
|
||||||
class JSignatureField(models.Field):
|
class JSignatureField(models.Field):
|
||||||
"""
|
"""
|
||||||
A model field handling a signature captured with jSignature
|
A model field handling a signature captured with jSignature
|
||||||
"""
|
"""
|
||||||
description = "A signature captured with jSignature"
|
description = "A signature captured with jSignature"
|
||||||
__metaclass__ = models.SubfieldBase
|
|
||||||
|
|
||||||
def get_internal_type(self):
|
def get_internal_type(self):
|
||||||
return 'TextField'
|
return 'TextField'
|
||||||
|
|
||||||
def to_python(self, value):
|
def to_python(self, value):
|
||||||
"""
|
if value in JSIGNATURE_EMPTY_VALUES:
|
||||||
Validates that the input can be red as a JSON object. Returns a Python
|
|
||||||
datetime.date object.
|
|
||||||
"""
|
|
||||||
if value in validators.EMPTY_VALUES:
|
|
||||||
return None
|
return None
|
||||||
elif isinstance(value, list):
|
elif isinstance(value, list):
|
||||||
return value
|
return value
|
||||||
@@ -32,13 +32,18 @@ class JSignatureField(models.Field):
|
|||||||
except ValueError:
|
except ValueError:
|
||||||
raise ValidationError('Invalid JSON format.')
|
raise ValidationError('Invalid JSON format.')
|
||||||
|
|
||||||
def get_prep_value(self, value):
|
def from_db_value(self, value, expression, connection, context):
|
||||||
return self.to_python(value)
|
if value in JSIGNATURE_EMPTY_VALUES:
|
||||||
|
|
||||||
def get_db_prep_value(self, value, connection, prepared=False):
|
|
||||||
if value in validators.EMPTY_VALUES:
|
|
||||||
return None
|
return None
|
||||||
elif isinstance(value, basestring):
|
try:
|
||||||
|
return json.loads(value)
|
||||||
|
except ValueError:
|
||||||
|
raise ValidationError('Invalid JSON format.')
|
||||||
|
|
||||||
|
def get_prep_value(self, value):
|
||||||
|
if value in JSIGNATURE_EMPTY_VALUES:
|
||||||
|
return None
|
||||||
|
elif isinstance(value, six.string_types):
|
||||||
return value
|
return value
|
||||||
elif isinstance(value, list):
|
elif isinstance(value, list):
|
||||||
return json.dumps(value)
|
return json.dumps(value)
|
||||||
@@ -49,6 +54,3 @@ class JSignatureField(models.Field):
|
|||||||
defaults.update(kwargs)
|
defaults.update(kwargs)
|
||||||
return super(JSignatureField, self).formfield(**defaults)
|
return super(JSignatureField, self).formfield(**defaults)
|
||||||
|
|
||||||
|
|
||||||
from south.modelsinspector import add_introspection_rules
|
|
||||||
add_introspection_rules([], ["jsignature.fields.JSignatureField"])
|
|
||||||
|
|||||||
@@ -8,6 +8,9 @@ from django.core import validators
|
|||||||
from django.core.exceptions import ValidationError
|
from django.core.exceptions import ValidationError
|
||||||
from .widgets import JSignatureWidget
|
from .widgets import JSignatureWidget
|
||||||
|
|
||||||
|
JSIGNATURE_EMPTY_VALUES = validators.EMPTY_VALUES + ('[]', )
|
||||||
|
|
||||||
|
|
||||||
class JSignatureField(Field):
|
class JSignatureField(Field):
|
||||||
"""
|
"""
|
||||||
A field handling a signature capture field with with jSignature
|
A field handling a signature capture field with with jSignature
|
||||||
@@ -16,10 +19,10 @@ class JSignatureField(Field):
|
|||||||
|
|
||||||
def to_python(self, value):
|
def to_python(self, value):
|
||||||
"""
|
"""
|
||||||
Validates that the input can be red as a JSON object. Returns a Python
|
Validates that the input can be red as a JSON object.
|
||||||
datetime.date object.
|
Returns a Python list (JSON object unserialized).
|
||||||
"""
|
"""
|
||||||
if value in validators.EMPTY_VALUES:
|
if value in JSIGNATURE_EMPTY_VALUES:
|
||||||
return None
|
return None
|
||||||
try:
|
try:
|
||||||
return json.loads(value)
|
return json.loads(value)
|
||||||
|
|||||||
BIN
jsignature/locale/fr/LC_MESSAGES/django.mo
Normal file
BIN
jsignature/locale/fr/LC_MESSAGES/django.mo
Normal file
Binary file not shown.
30
jsignature/locale/fr/LC_MESSAGES/django.po
Normal file
30
jsignature/locale/fr/LC_MESSAGES/django.po
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
# French translation for django-jsignature package.
|
||||||
|
# Copyright (C) 2013
|
||||||
|
# This file is distributed under the same license as the django-jsignature package.
|
||||||
|
# Florent Lebreton <florent.lebreton@makina-corpus.com>
|
||||||
|
#
|
||||||
|
msgid ""
|
||||||
|
msgstr ""
|
||||||
|
"Project-Id-Version: \n"
|
||||||
|
"Report-Msgid-Bugs-To: \n"
|
||||||
|
"POT-Creation-Date: 2013-09-18 11:10+0200\n"
|
||||||
|
"PO-Revision-Date: 2013-09-18 11:13+0100\n"
|
||||||
|
"Last-Translator: Florent Lebreton <lebreton.florent@wanadoo.fr>\n"
|
||||||
|
"Language-Team: contact@makina-corpus.com\n"
|
||||||
|
"MIME-Version: 1.0\n"
|
||||||
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
|
||||||
|
"X-Generator: Poedit 1.5.4\n"
|
||||||
|
|
||||||
|
#: mixins.py:13
|
||||||
|
msgid "Signature"
|
||||||
|
msgstr "Signature"
|
||||||
|
|
||||||
|
#: mixins.py:17
|
||||||
|
msgid "Signature date"
|
||||||
|
msgstr "Date de signature"
|
||||||
|
|
||||||
|
#: widgets.py:65
|
||||||
|
msgid "Reset"
|
||||||
|
msgstr "Réinitialiser"
|
||||||
@@ -7,6 +7,7 @@ from django.db import models
|
|||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from .fields import JSignatureField
|
from .fields import JSignatureField
|
||||||
|
|
||||||
|
|
||||||
class JSignatureFieldsMixin(models.Model):
|
class JSignatureFieldsMixin(models.Model):
|
||||||
""" Mixin class providing fields to store a signature with jSignature """
|
""" Mixin class providing fields to store a signature with jSignature """
|
||||||
signature = JSignatureField(
|
signature = JSignatureField(
|
||||||
@@ -24,9 +25,8 @@ class JSignatureFieldsMixin(models.Model):
|
|||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
|
|
||||||
is_new = self.pk is None
|
is_new = self.pk is None
|
||||||
original = not is_new and self.__class__.objects.get(pk=self.pk) or None
|
original = not is_new and self.__class__.objects.get(pk=self.pk)
|
||||||
|
|
||||||
print self.signature
|
|
||||||
if self.signature:
|
if self.signature:
|
||||||
if is_new or self.signature != original.signature:
|
if is_new or self.signature != original.signature:
|
||||||
self.signature_date = datetime.now()
|
self.signature_date = datetime.now()
|
||||||
|
|||||||
@@ -1,19 +1,29 @@
|
|||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
|
||||||
JSIGNATURE_WIDTH = getattr(settings, 'JSIGNATURE_WIDTH', 'ratio')
|
JSIGNATURE_WIDTH = getattr(
|
||||||
JSIGNATURE_HEIGHT = getattr(settings, 'JSIGNATURE_HEIGHT', 'ratio')
|
settings, 'JSIGNATURE_WIDTH', 'ratio')
|
||||||
JSIGNATURE_COLOR = getattr(settings, 'JSIGNATURE_COLOR', '#000')
|
JSIGNATURE_HEIGHT = getattr(
|
||||||
JSIGNATURE_BACKGROUND_COLOR = getattr(settings, 'JSIGNATURE_BACKGROUND_COLOR', '#FFF')
|
settings, 'JSIGNATURE_HEIGHT', 'ratio')
|
||||||
JSIGNATURE_DECOR_COLOR = getattr(settings, 'JSIGNATURE_DECOR_COLOR', '#DDD')
|
JSIGNATURE_COLOR = getattr(
|
||||||
JSIGNATURE_LINE_WIDTH = getattr(settings, 'JSIGNATURE_LINE_WIDTH', 0)
|
settings, 'JSIGNATURE_COLOR', '#000')
|
||||||
JSIGNATURE_UNDO_BUTTON = getattr(settings, 'JSIGNATURE_UNDO_BUTTON', False)
|
JSIGNATURE_BACKGROUND_COLOR = getattr(
|
||||||
|
settings, 'JSIGNATURE_BACKGROUND_COLOR', '#FFF')
|
||||||
|
JSIGNATURE_DECOR_COLOR = getattr(
|
||||||
|
settings, 'JSIGNATURE_DECOR_COLOR', '#DDD')
|
||||||
|
JSIGNATURE_LINE_WIDTH = getattr(
|
||||||
|
settings, 'JSIGNATURE_LINE_WIDTH', 0)
|
||||||
|
JSIGNATURE_UNDO_BUTTON = getattr(
|
||||||
|
settings, 'JSIGNATURE_UNDO_BUTTON', False)
|
||||||
|
JSIGNATURE_RESET_BUTTON = getattr(
|
||||||
|
settings, 'JSIGNATURE_RESET_BUTTON', True)
|
||||||
|
|
||||||
JSIGNATURE_DEFAULT_CONFIG = {
|
JSIGNATURE_DEFAULT_CONFIG = {
|
||||||
'width': JSIGNATURE_WIDTH,
|
'width': JSIGNATURE_WIDTH,
|
||||||
'height': JSIGNATURE_HEIGHT,
|
'height': JSIGNATURE_HEIGHT,
|
||||||
'color': JSIGNATURE_COLOR,
|
'color': JSIGNATURE_COLOR,
|
||||||
'background-color': JSIGNATURE_BACKGROUND_COLOR,
|
'background-color': JSIGNATURE_BACKGROUND_COLOR,
|
||||||
'decor-color': JSIGNATURE_DECOR_COLOR,
|
'decor-color': JSIGNATURE_DECOR_COLOR,
|
||||||
'lineWidth': JSIGNATURE_LINE_WIDTH,
|
'lineWidth': JSIGNATURE_LINE_WIDTH,
|
||||||
'UndoButton': JSIGNATURE_UNDO_BUTTON,
|
'UndoButton': JSIGNATURE_UNDO_BUTTON,
|
||||||
|
'ResetButton': JSIGNATURE_RESET_BUTTON,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +1,21 @@
|
|||||||
$(document).ready(function() {
|
$(document).ready(function() {
|
||||||
|
$(".jsign-container").each(function(){
|
||||||
|
var config = $(this).data('config');
|
||||||
|
var value = $(this).data('initial-value');
|
||||||
|
$(this).jSignature(config);
|
||||||
|
$(this).jSignature("setData", value, "native");
|
||||||
|
});
|
||||||
|
|
||||||
/* Each time user is done drawing a stroke, update value of hidden input */
|
/* Each time user is done drawing a stroke, update value of hidden input */
|
||||||
$(document).delegate(".jsign-container", "change", function(e) {
|
$(".jsign-container").on("change", function(e) {
|
||||||
var jSignature_data = $(this).jSignature('getData', 'native');
|
var jSignature_data = $(this).jSignature('getData', 'native');
|
||||||
var django_field_name = $(this).attr('id').split('_')[1];
|
var django_field_name = $(this).attr('id').split(/_(.+)/)[1];
|
||||||
$('#id_' + django_field_name).val(JSON.stringify(jSignature_data));
|
$('#id_' + django_field_name).val(JSON.stringify(jSignature_data));
|
||||||
});
|
});
|
||||||
|
|
||||||
/* Bind clear button */
|
/* Bind clear button */
|
||||||
$(document).delegate(".jsign-wrapper input", "click", function(e) {
|
$(".jsign-wrapper input").on("click", function(e) {
|
||||||
$(this).siblings('.jsign-container').jSignature('reset');
|
$(this).siblings('.jsign-container').jSignature('reset');
|
||||||
});
|
});
|
||||||
|
|
||||||
})
|
});
|
||||||
|
|||||||
10
jsignature/templates/jsignature/widget.html
Normal file
10
jsignature/templates/jsignature/widget.html
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<div class='jsign-wrapper'>
|
||||||
|
{{ hidden }}
|
||||||
|
<div id='{{ jsign_id }}'
|
||||||
|
data-config='{{ js_config }}'
|
||||||
|
data-initial-value='{{ value }}'
|
||||||
|
class='jsign-container'></div>
|
||||||
|
{% if config.ResetButton %}
|
||||||
|
<input type='button' value='{{ reset_btn_text }}' class="btn">
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
5
jsignature/tests/__init__.py
Normal file
5
jsignature/tests/__init__.py
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
from .widgets import JSignatureWidgetTest
|
||||||
|
from .forms import JSignatureFormFieldTest
|
||||||
|
from .fields import JSignatureFieldTest
|
||||||
|
from .mixins import JSignatureFieldsMixinTest
|
||||||
|
from .utils import UtilsTest
|
||||||
77
jsignature/tests/fields.py
Normal file
77
jsignature/tests/fields.py
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
import json
|
||||||
|
import six
|
||||||
|
|
||||||
|
from django.test import SimpleTestCase
|
||||||
|
from django.core.exceptions import ValidationError
|
||||||
|
|
||||||
|
from ..fields import JSignatureField
|
||||||
|
from ..forms import JSignatureField as JSignatureFormField
|
||||||
|
|
||||||
|
|
||||||
|
class JSignatureFieldTest(SimpleTestCase):
|
||||||
|
|
||||||
|
def test_to_python_empty(self):
|
||||||
|
f = JSignatureField()
|
||||||
|
for val in ['', [], '[]']:
|
||||||
|
self.assertIsNone(f.to_python(val))
|
||||||
|
|
||||||
|
def test_to_python_correct_value_python(self):
|
||||||
|
f = JSignatureField()
|
||||||
|
val = [{"x": [1, 2], "y": [3, 4]}]
|
||||||
|
self.assertEquals(val, f.to_python(val))
|
||||||
|
|
||||||
|
def test_to_python_correct_value_json(self):
|
||||||
|
f = JSignatureField()
|
||||||
|
val = [{"x": [1, 2], "y": [3, 4]}]
|
||||||
|
val_str = '[{"x":[1,2], "y":[3,4]}]'
|
||||||
|
self.assertEquals(val, f.to_python(val_str))
|
||||||
|
|
||||||
|
def test_to_python_incorrect_value(self):
|
||||||
|
f = JSignatureField()
|
||||||
|
val = 'foo'
|
||||||
|
self.assertRaises(ValidationError, f.to_python, val)
|
||||||
|
|
||||||
|
def test_from_db_value_empty(self):
|
||||||
|
f = JSignatureField()
|
||||||
|
self.assertIsNone(f.from_db_value(''))
|
||||||
|
|
||||||
|
def test_from_db_value_correct_value_json(self):
|
||||||
|
f = JSignatureField()
|
||||||
|
val = [{"x": [1, 2], "y": [3, 4]}]
|
||||||
|
val_str = '[{"x":[1,2], "y":[3,4]}]'
|
||||||
|
self.assertEquals(val, f.from_db_value(val_str))
|
||||||
|
|
||||||
|
def test_from_db_value_incorrect_value(self):
|
||||||
|
f = JSignatureField()
|
||||||
|
val = 'foo'
|
||||||
|
self.assertRaises(ValidationError, f.to_python, val)
|
||||||
|
|
||||||
|
def test_get_prep_value_empty(self):
|
||||||
|
f = JSignatureField()
|
||||||
|
for val in ['', [], '[]']:
|
||||||
|
self.assertIsNone(f.get_prep_value(val))
|
||||||
|
|
||||||
|
def test_get_prep_value_correct_values_python(self):
|
||||||
|
f = JSignatureField()
|
||||||
|
val = [{"x": [1, 2], "y": [3, 4]}]
|
||||||
|
val_prep = f.get_prep_value(val)
|
||||||
|
self.assertIsInstance(val_prep, six.string_types)
|
||||||
|
self.assertEquals(val, json.loads(val_prep))
|
||||||
|
|
||||||
|
def test_get_prep_value_correct_values_json(self):
|
||||||
|
f = JSignatureField()
|
||||||
|
val = [{"x": [1, 2], "y": [3, 4]}]
|
||||||
|
val_str = '[{"x":[1,2], "y":[3,4]}]'
|
||||||
|
val_prep = f.get_prep_value(val_str)
|
||||||
|
self.assertIsInstance(val_prep, six.string_types)
|
||||||
|
self.assertEquals(val, json.loads(val_prep))
|
||||||
|
|
||||||
|
def test_get_prep_value_incorrect_values(self):
|
||||||
|
f = JSignatureField()
|
||||||
|
val = type('Foo')
|
||||||
|
self.assertRaises(ValidationError, f.get_prep_value, val)
|
||||||
|
|
||||||
|
def test_formfield(self):
|
||||||
|
f = JSignatureField()
|
||||||
|
cls = f.formfield().__class__
|
||||||
|
self.assertTrue(issubclass(cls, JSignatureFormField))
|
||||||
27
jsignature/tests/forms.py
Normal file
27
jsignature/tests/forms.py
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
from django.test import SimpleTestCase
|
||||||
|
from django.core.exceptions import ValidationError
|
||||||
|
|
||||||
|
from ..widgets import JSignatureWidget
|
||||||
|
from ..forms import JSignatureField
|
||||||
|
|
||||||
|
|
||||||
|
class JSignatureFormFieldTest(SimpleTestCase):
|
||||||
|
|
||||||
|
def test_widget(self):
|
||||||
|
f = JSignatureField()
|
||||||
|
self.assertIsInstance(f.widget, JSignatureWidget)
|
||||||
|
|
||||||
|
def test_to_python_empty_values(self):
|
||||||
|
f = JSignatureField()
|
||||||
|
for val in ['', [], '[]']:
|
||||||
|
self.assertIsNone(f.to_python(val))
|
||||||
|
|
||||||
|
def test_to_python_correct_values(self):
|
||||||
|
f = JSignatureField()
|
||||||
|
val = '[{"x":[1,2], "y":[3,4]}]'
|
||||||
|
self.assertEquals([{'x': [1, 2], 'y': [3, 4]}], f.to_python(val))
|
||||||
|
|
||||||
|
def test_to_python_incorrect_values(self):
|
||||||
|
f = JSignatureField()
|
||||||
|
val = 'foo'
|
||||||
|
self.assertRaises(ValidationError, f.to_python, val)
|
||||||
61
jsignature/tests/mixins.py
Normal file
61
jsignature/tests/mixins.py
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
from datetime import date
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db.models import loading
|
||||||
|
from django.test import SimpleTestCase
|
||||||
|
from django.core.management import call_command
|
||||||
|
|
||||||
|
from .models import JSignatureTestModel
|
||||||
|
|
||||||
|
|
||||||
|
class JSignatureFieldsMixinTest(SimpleTestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
self.old_installed_apps = settings.INSTALLED_APPS
|
||||||
|
settings.INSTALLED_APPS = list(settings.INSTALLED_APPS)
|
||||||
|
settings.INSTALLED_APPS.append('jsignature.tests')
|
||||||
|
loading.cache.loaded = False
|
||||||
|
call_command('syncdb', verbosity=0)
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
settings.INSTALLED_APPS = self.old_installed_apps
|
||||||
|
|
||||||
|
def test_save_create(self):
|
||||||
|
# If an object is created signed, signature date must be set
|
||||||
|
signature_value = [{"x": [1, 2], "y": [3, 4]}]
|
||||||
|
i = JSignatureTestModel(signature=signature_value)
|
||||||
|
i.save()
|
||||||
|
i = JSignatureTestModel.objects.get(pk=i.pk)
|
||||||
|
self.assertEqual(date.today(), i.signature_date.date())
|
||||||
|
|
||||||
|
def test_save_no_change(self):
|
||||||
|
# If signature doesn't change, signature date must not be updated
|
||||||
|
signature_value = [{"x": [1, 2], "y": [3, 4]}]
|
||||||
|
i = JSignatureTestModel(signature=signature_value)
|
||||||
|
i.save()
|
||||||
|
i.signature_date = date(2013, 1, 1)
|
||||||
|
i.save()
|
||||||
|
i = JSignatureTestModel.objects.get(pk=i.pk)
|
||||||
|
self.assertEqual(date(2013, 1, 1), i.signature_date.date())
|
||||||
|
|
||||||
|
def test_save_change(self):
|
||||||
|
# If signature changes, signature date must be updated too
|
||||||
|
signature_value = [{"x": [1, 2], "y": [3, 4]}]
|
||||||
|
new_signature_value = [{"x": [5, 6], "y": [7, 8]}]
|
||||||
|
i = JSignatureTestModel(signature=signature_value,
|
||||||
|
signature_date=date(2013, 1, 1))
|
||||||
|
i.save()
|
||||||
|
i.signature_date = date(2013, 1, 1)
|
||||||
|
i.signature = new_signature_value
|
||||||
|
i.save()
|
||||||
|
i = JSignatureTestModel.objects.get(pk=i.pk)
|
||||||
|
self.assertEqual(date.today(), i.signature_date.date())
|
||||||
|
|
||||||
|
def test_save_none(self):
|
||||||
|
# If sinature is set to None, it must be the same for signature_date
|
||||||
|
signature_value = [{"x": [1, 2], "y": [3, 4]}]
|
||||||
|
i = JSignatureTestModel(signature=signature_value)
|
||||||
|
i.save()
|
||||||
|
i.signature = None
|
||||||
|
i.save()
|
||||||
|
i = JSignatureTestModel.objects.get(pk=i.pk)
|
||||||
|
self.assertIsNone(i.signature_date)
|
||||||
6
jsignature/tests/models.py
Normal file
6
jsignature/tests/models.py
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
""" Provides a dummy model implementing JSignatureFieldsMixin """
|
||||||
|
from ..mixins import JSignatureFieldsMixin
|
||||||
|
|
||||||
|
|
||||||
|
class JSignatureTestModel(JSignatureFieldsMixin):
|
||||||
|
pass
|
||||||
36
jsignature/tests/utils.py
Normal file
36
jsignature/tests/utils.py
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
import json
|
||||||
|
import os
|
||||||
|
import imghdr
|
||||||
|
from PIL import Image
|
||||||
|
from django.test import SimpleTestCase
|
||||||
|
|
||||||
|
from ..utils import draw_signature
|
||||||
|
|
||||||
|
DUMMY_VALUE = [{"x": [205, 210], "y": [59, 63]},
|
||||||
|
{"x": [205, 207], "y": [67, 64]}]
|
||||||
|
DUMMY_STR_VALUE = json.dumps(DUMMY_VALUE)
|
||||||
|
|
||||||
|
|
||||||
|
class UtilsTest(SimpleTestCase):
|
||||||
|
|
||||||
|
def test_inputs_bad_str_value(self):
|
||||||
|
self.assertRaises(ValueError, draw_signature, 'foo_bar')
|
||||||
|
|
||||||
|
def test_inputs_bad_type_value(self):
|
||||||
|
self.assertRaises(ValueError, draw_signature, object())
|
||||||
|
|
||||||
|
def test_inputs_good_list_value(self):
|
||||||
|
draw_signature(DUMMY_VALUE)
|
||||||
|
|
||||||
|
def test_inputs_good_str_value(self):
|
||||||
|
draw_signature(DUMMY_STR_VALUE)
|
||||||
|
|
||||||
|
def test_outputs_as_file(self):
|
||||||
|
output = draw_signature(DUMMY_VALUE, as_file=True)
|
||||||
|
self.assertTrue(os.path.isfile(output))
|
||||||
|
self.assertIsNotNone(imghdr.what(output))
|
||||||
|
|
||||||
|
def test_outputs_as_image(self):
|
||||||
|
output = draw_signature(DUMMY_VALUE)
|
||||||
|
self.assertTrue(issubclass(output.__class__, Image.Image))
|
||||||
|
self.assertTrue(all(output.getbbox()))
|
||||||
83
jsignature/tests/widgets.py
Normal file
83
jsignature/tests/widgets.py
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
import json
|
||||||
|
from pyquery import PyQuery as pq
|
||||||
|
import six
|
||||||
|
|
||||||
|
from django.test import SimpleTestCase
|
||||||
|
from django.core.exceptions import ValidationError
|
||||||
|
|
||||||
|
from ..widgets import JSignatureWidget
|
||||||
|
from ..settings import JSIGNATURE_HEIGHT
|
||||||
|
|
||||||
|
|
||||||
|
class JSignatureWidgetTest(SimpleTestCase):
|
||||||
|
|
||||||
|
def test_default_media(self):
|
||||||
|
widget = JSignatureWidget()
|
||||||
|
media = widget.media
|
||||||
|
media_js = list(media.render_js())
|
||||||
|
self.assertEqual(2, len(media_js))
|
||||||
|
media_js_str = "".join(media_js)
|
||||||
|
self.assertIn('jSignature.min.js', media_js_str)
|
||||||
|
self.assertIn('django_jsignature.js', media_js_str)
|
||||||
|
media_css = list(media.render_css())
|
||||||
|
self.assertEquals([], media_css)
|
||||||
|
|
||||||
|
def test_init(self):
|
||||||
|
w = JSignatureWidget()
|
||||||
|
self.assertEquals({}, w.jsignature_attrs)
|
||||||
|
given_attrs = {'width': 300, 'height': 100}
|
||||||
|
w = JSignatureWidget(jsignature_attrs=given_attrs)
|
||||||
|
self.assertEquals(given_attrs, w.jsignature_attrs)
|
||||||
|
|
||||||
|
def test_build_jsignature_id(self):
|
||||||
|
w = JSignatureWidget()
|
||||||
|
id = w.build_jsignature_id('foo')
|
||||||
|
self.assertEqual('jsign_foo', id)
|
||||||
|
|
||||||
|
def test_build_jsignature_config(self):
|
||||||
|
w = JSignatureWidget(jsignature_attrs={'width': 400})
|
||||||
|
config = w.build_jsignature_config()
|
||||||
|
self.assertEqual(400, config.get('width'))
|
||||||
|
self.assertEqual(JSIGNATURE_HEIGHT, config.get('height'))
|
||||||
|
|
||||||
|
def test_prep_value_empty_values(self):
|
||||||
|
w = JSignatureWidget()
|
||||||
|
for val in ['', [], '[]']:
|
||||||
|
self.assertEqual('[]', w.prep_value(val))
|
||||||
|
|
||||||
|
def test_prep_value_correct_values_python(self):
|
||||||
|
w = JSignatureWidget()
|
||||||
|
val = [{"x": [1, 2], "y": [3, 4]}]
|
||||||
|
val_prep = w.prep_value(val)
|
||||||
|
self.assertIsInstance(val_prep, six.string_types)
|
||||||
|
self.assertEquals(val, json.loads(val_prep))
|
||||||
|
|
||||||
|
def test_prep_value_correct_values_json(self):
|
||||||
|
w = JSignatureWidget()
|
||||||
|
val = [{"x": [1, 2], "y": [3, 4]}]
|
||||||
|
val_str = '[{"x":[1,2], "y":[3,4]}]'
|
||||||
|
val_prep = w.prep_value(val_str)
|
||||||
|
self.assertIsInstance(val_prep, six.string_types)
|
||||||
|
self.assertEquals(val, json.loads(val_prep))
|
||||||
|
|
||||||
|
def test_prep_value_incorrect_values(self):
|
||||||
|
w = JSignatureWidget()
|
||||||
|
val = type('Foo')
|
||||||
|
self.assertRaises(ValidationError, w.prep_value, val)
|
||||||
|
|
||||||
|
def test_render(self):
|
||||||
|
w = JSignatureWidget()
|
||||||
|
output = w.render(name='foo', value=None)
|
||||||
|
# Almost useless :/
|
||||||
|
self.assertEqual(1, len(pq('.jsign-wrapper', output)))
|
||||||
|
self.assertEqual(1, len(pq('[type=hidden]', output)))
|
||||||
|
|
||||||
|
def test_render_reset_button_true(self):
|
||||||
|
w = JSignatureWidget(jsignature_attrs={'ResetButton': True})
|
||||||
|
output = w.render(name='foo', value=None)
|
||||||
|
self.assertEqual(1, len(pq('[type=button]', output)))
|
||||||
|
|
||||||
|
def test_render_reset_button_false(self):
|
||||||
|
w = JSignatureWidget(jsignature_attrs={'ResetButton': False})
|
||||||
|
output = w.render(name='foo', value=None)
|
||||||
|
self.assertEqual(0, len(pq('[type=button]', output)))
|
||||||
@@ -3,12 +3,11 @@
|
|||||||
https://github.com/zivezab/django-autograph/blob/master/autograph/utils.py
|
https://github.com/zivezab/django-autograph/blob/master/autograph/utils.py
|
||||||
"""
|
"""
|
||||||
import json
|
import json
|
||||||
import cStringIO
|
|
||||||
from itertools import chain
|
from itertools import chain
|
||||||
from tempfile import NamedTemporaryFile
|
|
||||||
from PIL import Image, ImageDraw, ImageOps
|
from PIL import Image, ImageDraw, ImageOps
|
||||||
|
|
||||||
AA = 5 # super sampling gor antialiasing
|
AA = 5 # super sampling gor antialiasing
|
||||||
|
|
||||||
|
|
||||||
def draw_signature(data, as_file=False):
|
def draw_signature(data, as_file=False):
|
||||||
""" Draw signature based on lines stored in json_string.
|
""" Draw signature based on lines stored in json_string.
|
||||||
@@ -24,15 +23,16 @@ def draw_signature(data, as_file=False):
|
|||||||
raise ValueError
|
raise ValueError
|
||||||
|
|
||||||
# Compute box
|
# Compute box
|
||||||
width = max(chain(*[d['x'] for d in data])) + 10
|
width = max(chain(*[d['x'] for d in drawing])) + 10
|
||||||
height = max(chain(*[d['y'] for d in data])) + 10
|
height = max(chain(*[d['y'] for d in drawing])) + 10
|
||||||
|
|
||||||
# Draw image
|
# Draw image
|
||||||
im = Image.new("RGBA", (width*AA, height*AA))
|
im = Image.new("RGBA", (width*AA, height*AA))
|
||||||
draw = ImageDraw.Draw(im)
|
draw = ImageDraw.Draw(im)
|
||||||
for line in drawing:
|
for line in drawing:
|
||||||
len_line = len(line['x'])
|
len_line = len(line['x'])
|
||||||
points = [(line['x'][i]*AA, line['y'][i]*AA) for i in range(0, len_line)]
|
points = [(line['x'][i]*AA, line['y'][i]*AA)
|
||||||
|
for i in range(0, len_line)]
|
||||||
draw.line(points, fill="#000", width=2*AA)
|
draw.line(points, fill="#000", width=2*AA)
|
||||||
im = ImageOps.expand(im)
|
im = ImageOps.expand(im)
|
||||||
# Smart crop
|
# Smart crop
|
||||||
@@ -45,7 +45,6 @@ def draw_signature(data, as_file=False):
|
|||||||
if as_file:
|
if as_file:
|
||||||
ret = im._dump(format='PNG')
|
ret = im._dump(format='PNG')
|
||||||
else:
|
else:
|
||||||
ret = img
|
ret = im
|
||||||
|
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
|||||||
@@ -3,16 +3,29 @@
|
|||||||
with jSignature jQuery plugin
|
with jSignature jQuery plugin
|
||||||
"""
|
"""
|
||||||
import json
|
import json
|
||||||
|
import six
|
||||||
|
|
||||||
|
from django.template.loader import render_to_string
|
||||||
from django.forms.widgets import HiddenInput
|
from django.forms.widgets import HiddenInput
|
||||||
|
from django.core import validators
|
||||||
|
from django.core.exceptions import ValidationError
|
||||||
|
|
||||||
from django.utils.safestring import mark_safe
|
from django.utils.safestring import mark_safe
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from jsignature.settings import JSIGNATURE_DEFAULT_CONFIG
|
from jsignature.settings import JSIGNATURE_DEFAULT_CONFIG
|
||||||
|
|
||||||
|
JSIGNATURE_EMPTY_VALUES = validators.EMPTY_VALUES + ('[]', )
|
||||||
|
|
||||||
|
|
||||||
class JSignatureWidget(HiddenInput):
|
class JSignatureWidget(HiddenInput):
|
||||||
"""
|
"""
|
||||||
A widget handling a signature capture field with with jSignature
|
A widget handling a signature capture field with with jSignature
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
# Actually, this widget has a display so we want it to behave like a
|
||||||
|
# normal field, not a hidden one
|
||||||
|
is_hidden = False
|
||||||
|
|
||||||
class Media:
|
class Media:
|
||||||
js = ('js/jSignature.min.js',
|
js = ('js/jSignature.min.js',
|
||||||
'js/django_jsignature.js')
|
'js/django_jsignature.js')
|
||||||
@@ -22,20 +35,48 @@ class JSignatureWidget(HiddenInput):
|
|||||||
# Store jSignature js config
|
# Store jSignature js config
|
||||||
self.jsignature_attrs = jsignature_attrs or {}
|
self.jsignature_attrs = jsignature_attrs or {}
|
||||||
|
|
||||||
def render(self, name, value, attrs=None):
|
def build_jsignature_config(self):
|
||||||
|
""" Build javascript config for jSignature initialization.
|
||||||
# Build config
|
It's a dict with for which default values come from settings
|
||||||
jsign_id = 'jsign_%s' % name
|
and can be overriden by jsignature_attrs, given at widget
|
||||||
|
instanciation time """
|
||||||
jsignature_config = JSIGNATURE_DEFAULT_CONFIG.copy()
|
jsignature_config = JSIGNATURE_DEFAULT_CONFIG.copy()
|
||||||
jsignature_config.update(self.jsignature_attrs)
|
jsignature_config.update(self.jsignature_attrs)
|
||||||
|
return jsignature_config
|
||||||
|
|
||||||
|
def build_jsignature_id(self, name):
|
||||||
|
""" Build HTML id for jsignature container.
|
||||||
|
It's important because it's used in javascript code """
|
||||||
|
return 'jsign_%s' % name
|
||||||
|
|
||||||
|
def prep_value(self, value):
|
||||||
|
""" Prepare value before effectively render widget """
|
||||||
|
if value in JSIGNATURE_EMPTY_VALUES:
|
||||||
|
return "[]"
|
||||||
|
elif isinstance(value, six.string_types):
|
||||||
|
return value
|
||||||
|
elif isinstance(value, list):
|
||||||
|
return json.dumps(value)
|
||||||
|
raise ValidationError('Invalid format.')
|
||||||
|
|
||||||
|
def render(self, name, value, attrs=None):
|
||||||
|
""" Render widget """
|
||||||
|
# Build config
|
||||||
|
jsign_id = self.build_jsignature_id(name)
|
||||||
|
jsignature_config = self.build_jsignature_config()
|
||||||
|
|
||||||
|
# Prepare value
|
||||||
|
value = self.prep_value(value)
|
||||||
|
|
||||||
# Build output
|
# Build output
|
||||||
hidden_input = super(JSignatureWidget, self).render(name, value, attrs)
|
context = {
|
||||||
div = u'<div id="%s" class="jsign-container"></div>' % jsign_id
|
'hidden': super(JSignatureWidget, self).render(name, value, attrs),
|
||||||
clr = u'<input type="button" value="%s" class="btn">' % _('Reset')
|
'jsign_id': jsign_id,
|
||||||
js = u'$("#%s").jSignature(%s);' % (jsign_id, json.dumps(jsignature_config))
|
'reset_btn_text': _('Reset'),
|
||||||
js += u'$("#%s").jSignature("setData", %s,"native");' % (jsign_id, json.dumps(value))
|
'config': jsignature_config,
|
||||||
js = u'<script type="text/javascript">%s</script>' % js
|
'js_config': mark_safe(json.dumps(jsignature_config)),
|
||||||
out = u'<div class="jsign-wrapper">%s%s%s%s</div>' % (hidden_input, div, clr, js)
|
'value': mark_safe(value),
|
||||||
|
}
|
||||||
|
out = render_to_string('jsignature/widget.html', context)
|
||||||
|
|
||||||
return mark_safe(out)
|
return mark_safe(out)
|
||||||
|
|||||||
82
quicktest.py
Normal file
82
quicktest.py
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import argparse
|
||||||
|
from django.conf import settings
|
||||||
|
|
||||||
|
|
||||||
|
class QuickDjangoTest(object):
|
||||||
|
"""
|
||||||
|
A quick way to run the Django test suite without a fully-configured project.
|
||||||
|
|
||||||
|
Example usage:
|
||||||
|
|
||||||
|
>>> QuickDjangoTest('app1', 'app2')
|
||||||
|
|
||||||
|
Based on a script published by Lukasz Dziedzia at:
|
||||||
|
http://stackoverflow.com/questions/3841725/how-to-launch-tests-for-django-reusable-app
|
||||||
|
"""
|
||||||
|
DIRNAME = os.path.dirname(__file__)
|
||||||
|
INSTALLED_APPS = (
|
||||||
|
)
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
self.apps = args
|
||||||
|
self.run_tests()
|
||||||
|
|
||||||
|
def run_tests(self):
|
||||||
|
"""
|
||||||
|
Fire up the Django test suite developed for version 1.2
|
||||||
|
"""
|
||||||
|
settings.configure(
|
||||||
|
TEMPLATE_DIRS = ('jsignature/templates/',),
|
||||||
|
ROOT_URLCONF = 'jsignature.tests',
|
||||||
|
DEBUG = True,
|
||||||
|
DATABASES = {
|
||||||
|
'default': {
|
||||||
|
'ENGINE': 'django.db.backends.sqlite3',
|
||||||
|
'NAME': os.path.join(self.DIRNAME, 'database.db'),
|
||||||
|
'USER': '',
|
||||||
|
'PASSWORD': '',
|
||||||
|
'HOST': '',
|
||||||
|
'PORT': '',
|
||||||
|
}
|
||||||
|
},
|
||||||
|
MIDDLEWARE_CLASSES = (
|
||||||
|
'django.contrib.sessions.middleware.SessionMiddleware',
|
||||||
|
'django.middleware.common.CommonMiddleware',
|
||||||
|
'django.middleware.csrf.CsrfViewMiddleware',
|
||||||
|
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||||
|
'django.contrib.messages.middleware.MessageMiddleware',
|
||||||
|
),
|
||||||
|
INSTALLED_APPS = self.INSTALLED_APPS + self.apps
|
||||||
|
)
|
||||||
|
# Setup is needed for Django >= 1.7
|
||||||
|
import django
|
||||||
|
if hasattr(django, 'setup'):
|
||||||
|
django.setup()
|
||||||
|
try:
|
||||||
|
from django.test.runner import DiscoverRunner
|
||||||
|
failures = DiscoverRunner().run_tests(self.apps, verbosity=1)
|
||||||
|
except ImportError:
|
||||||
|
# DjangoTestSuiteRunner has been deprecated in Django 1.7
|
||||||
|
from django.test.simple import DjangoTestSuiteRunner
|
||||||
|
failures = DjangoTestSuiteRunner().run_tests(self.apps, verbosity=1)
|
||||||
|
if failures: # pragma: no cover
|
||||||
|
sys.exit(failures)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
"""
|
||||||
|
What do when the user hits this file from the shell.
|
||||||
|
|
||||||
|
Example usage:
|
||||||
|
|
||||||
|
$ python quicktest.py app1 app2
|
||||||
|
|
||||||
|
"""
|
||||||
|
parser = argparse.ArgumentParser(
|
||||||
|
usage="[args]",
|
||||||
|
description="Run Django tests on the provided applications."
|
||||||
|
)
|
||||||
|
parser.add_argument('apps', nargs='+', type=str)
|
||||||
|
args = parser.parse_args()
|
||||||
|
QuickDjangoTest(*args.apps)
|
||||||
@@ -1,2 +1,3 @@
|
|||||||
Django >= 1.4
|
|
||||||
pillow
|
pillow
|
||||||
|
pyquery
|
||||||
|
six
|
||||||
|
|||||||
BIN
screen.png
Normal file
BIN
screen.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 60 KiB |
40
setup.py
40
setup.py
@@ -5,24 +5,34 @@ here = os.path.abspath(os.path.dirname(__file__))
|
|||||||
|
|
||||||
setup(
|
setup(
|
||||||
name='django-jsignature',
|
name='django-jsignature',
|
||||||
version='0.7.5.dev0',
|
version='0.8',
|
||||||
author='Florent Lebreton',
|
author='Florent Lebreton',
|
||||||
author_email='florent.lebreton@makina-corpus.com',
|
author_email='florent.lebreton@makina-corpus.com',
|
||||||
url='https://github.com/makinacorpus/django-jsignature',
|
url='https://github.com/fle/django-jsignature',
|
||||||
download_url = "http://pypi.python.org/pypi/django-jsignature/",
|
download_url='https://github.com/fle/django-jsignature/tarball/0.8',
|
||||||
description="Use jSignature jQuery plugin in your django projects",
|
description='Use jSignature jQuery plugin in your django projects',
|
||||||
long_description=open(os.path.join(here, 'README.rst')).read(),
|
long_description=open(os.path.join(here, 'README.rst')).read() + '\n\n' +
|
||||||
|
open(os.path.join(here, 'CHANGES')).read(),
|
||||||
license='LPGL, see LICENSE file.',
|
license='LPGL, see LICENSE file.',
|
||||||
install_requires=['Django'],
|
install_requires=['Django'],
|
||||||
packages=find_packages(),
|
packages=find_packages(),
|
||||||
include_package_data = True,
|
include_package_data=True,
|
||||||
zip_safe = False,
|
zip_safe=False,
|
||||||
classifiers = ['Topic :: Utilities',
|
classifiers=[
|
||||||
'Natural Language :: English',
|
'Topic :: Utilities',
|
||||||
'Operating System :: OS Independent',
|
'Natural Language :: English',
|
||||||
'Intended Audience :: Developers',
|
'Operating System :: OS Independent',
|
||||||
'Environment :: Web Environment',
|
'Intended Audience :: Developers',
|
||||||
'Framework :: Django',
|
'Environment :: Web Environment',
|
||||||
'Development Status :: 4 - Beta',
|
'Framework :: Django',
|
||||||
'Programming Language :: Python :: 2.7'],
|
'Development Status :: 5 - Production/Stable',
|
||||||
|
'Programming Language :: Python',
|
||||||
|
'Programming Language :: Python :: 2',
|
||||||
|
'Programming Language :: Python :: 2.6',
|
||||||
|
'Programming Language :: Python :: 2.7',
|
||||||
|
'Programming Language :: Python :: 3',
|
||||||
|
'Programming Language :: Python :: 3.2',
|
||||||
|
'Programming Language :: Python :: 3.3',
|
||||||
|
'Programming Language :: Python :: 3.4'
|
||||||
|
],
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user