Commit 792d66da authored by Skia's avatar Skia

Add nice whole file support

parent 6fbf6074
Pipeline #106 failed with stage
in 1 minute and 52 seconds
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('accounting', '0016_auto_20160807_2000'),
]
operations = [
migrations.AlterField(
model_name='operation',
name='invoice',
field=models.ForeignKey(blank=True, related_name='operations', to='core.SithFile', null=True, verbose_name='invoice'),
),
]
......@@ -7,7 +7,7 @@ from django.utils.translation import ugettext_lazy as _
from django.template import defaultfilters
from decimal import Decimal
from core.models import User
from core.models import User, SithFile
from club.models import Club
class CurrencyField(models.DecimalField):
......@@ -156,7 +156,7 @@ class Operation(models.Model):
remark = models.TextField(_('remark'), max_length=255)
mode = models.CharField(_('payment method'), max_length=255, choices=settings.SITH_ACCOUNTING_PAYMENT_METHOD)
cheque_number = models.IntegerField(_('cheque number'), default=-1)
invoice = models.FileField(upload_to='invoices', verbose_name=_("invoice"), null=True, blank=True)
invoice = models.ForeignKey(SithFile, related_name='operations', verbose_name=_("invoice"), null=True, blank=True)
done = models.BooleanField(_('is done'), default=False)
accounting_type = models.ForeignKey('AccountingType', related_name="operations", verbose_name=_("accounting type"))
target_type = models.CharField(_('target type'), max_length=10,
......
......@@ -27,6 +27,7 @@
<td>{% trans %}Nature{% endtrans %}</td>
<td>{% trans %}Done{% endtrans %}</td>
<td>{% trans %}Comment{% endtrans %}</td>
<td>{% trans %}File{% endtrans %}</td>
<td>{% trans %}Actions{% endtrans %}</td>
</tr>
</thead>
......@@ -51,6 +52,11 @@
<td>{% trans %}No{% endtrans %}</td>
{% endif %}
<td>{{ o.remark }}</td>
{% if o.invoice %}
<td><a href="{{ url('core:download') + '?file=' + o.invoice.name }}">{{ o.invoice.name }}</a></td>
{% else %}
<td>-</td>
{% endif %}
<td>
{% if not o.journal.closed %}
<a href="{{ url('accounting:op_edit', op_id=o.id) }}">{% trans %}Edit{% endtrans %}</a>
......
......@@ -4,8 +4,10 @@ from django.shortcuts import render
from django.core.urlresolvers import reverse_lazy
from django.forms.models import modelform_factory
from django.forms import HiddenInput
from django.forms.extras.widgets import SelectDateWidget
from core.views import CanViewMixin, CanEditMixin, CanEditPropMixin, CanCreateMixin
from core.views.forms import SelectFile
from accounting.models import BankAccount, ClubAccount, GeneralJournal, Operation, AccountingType, Company
# Accounting types
......@@ -166,7 +168,7 @@ class OperationCreateView(CanCreateMixin, CreateView):
form_class = modelform_factory(Operation,
fields=['amount', 'label', 'remark', 'journal', 'target_type', 'target_id', 'target_label', 'date', 'mode',
'cheque_number', 'invoice', 'accounting_type', 'done'],
widgets={'journal': HiddenInput})
widgets={'journal': HiddenInput, 'date': SelectDateWidget})
template_name = 'core/create.jinja'
def get_initial(self):
......@@ -183,8 +185,10 @@ class OperationEditView(CanEditMixin, UpdateView):
"""
model = Operation
pk_url_kwarg = "op_id"
fields = ['amount', 'label', 'remark', 'target_type', 'target_id', 'target_label', 'date', 'mode', 'cheque_number',
'invoice', 'accounting_type', 'done']
form_class = modelform_factory(Operation,
fields = ['amount', 'label', 'remark', 'target_type', 'target_id', 'target_label', 'date', 'mode', 'cheque_number',
'invoice', 'accounting_type', 'done'],
widgets={'date': SelectDateWidget, 'invoice': SelectFile})
template_name = 'core/edit.jinja'
# Company views
......
from django.contrib import admin
from core.models import User, Page, RealGroup
from core.models import User, Page, RealGroup, SithFile
from django.contrib.auth.models import Group as AuthGroup
......@@ -7,4 +7,5 @@ admin.site.register(User)
admin.site.unregister(AuthGroup)
admin.site.register(RealGroup)
admin.site.register(Page)
admin.site.register(SithFile)
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
import core.models
from django.conf import settings
class Migration(migrations.Migration):
dependencies = [
('core', '0008_pagerev_revision'),
]
operations = [
migrations.CreateModel(
name='SithFile',
fields=[
('id', models.AutoField(serialize=False, verbose_name='ID', auto_created=True, primary_key=True)),
('name', models.CharField(max_length=30, verbose_name='file name')),
('file', models.FileField(upload_to=core.models.get_directory, blank=True, null=True, verbose_name='file')),
('is_folder', models.BooleanField(default=True, verbose_name='is folder')),
('mime_type', models.CharField(max_length=30, verbose_name='mime type')),
('size', models.IntegerField(default=0, verbose_name='size')),
('date', models.DateTimeField(auto_now=True, verbose_name='date')),
('edit_groups', models.ManyToManyField(to='core.Group', blank=True, verbose_name='edit group', related_name='editable_files')),
('owner', models.ForeignKey(related_name='owned_files', to=settings.AUTH_USER_MODEL, verbose_name='owner')),
('parent', models.ForeignKey(blank=True, related_name='children', to='core.SithFile', null=True, verbose_name='parent')),
('view_groups', models.ManyToManyField(to='core.Group', blank=True, verbose_name='view group', related_name='viewable_files')),
],
options={
'verbose_name': 'file',
},
),
migrations.AlterField(
model_name='page',
name='edit_groups',
field=models.ManyToManyField(to='core.Group', blank=True, verbose_name='edit group', related_name='editable_page'),
),
migrations.AlterField(
model_name='page',
name='owner_group',
field=models.ForeignKey(default=1, related_name='owned_page', to='core.Group', verbose_name='owner group'),
),
migrations.AlterField(
model_name='page',
name='parent',
field=models.ForeignKey(on_delete=django.db.models.deletion.SET_NULL, blank=True, related_name='children', to='core.Page', null=True, verbose_name='parent'),
),
migrations.AlterField(
model_name='page',
name='view_groups',
field=models.ManyToManyField(to='core.Group', blank=True, verbose_name='view group', related_name='viewable_page'),
),
]
......@@ -287,6 +287,108 @@ class Preferences(models.Model):
help_text=_('Show your account statistics to others'),
)
def get_directory(instance, filename):
return './{0}/{1}'.format(instance.get_parent_path(), filename)
class SithFile(models.Model):
name = models.CharField(_('file name'), max_length=30, blank=False)
parent = models.ForeignKey('self', related_name="children", verbose_name=_("parent"), null=True, blank=True)
file = models.FileField(upload_to=get_directory, verbose_name=_("file"), null=True, blank=True)
owner = models.ForeignKey(User, related_name="owned_files", verbose_name=_("owner"))
edit_groups = models.ManyToManyField(Group, related_name="editable_files", verbose_name=_("edit group"), blank=True)
view_groups = models.ManyToManyField(Group, related_name="viewable_files", verbose_name=_("view group"), blank=True)
is_folder = models.BooleanField(_("is folder"), default=True)
mime_type = models.CharField(_('mime type'), max_length=30)
size = models.IntegerField(_("size"), default=0)
date = models.DateTimeField(_('date'), auto_now=True)
class Meta:
verbose_name = _("file")
def is_owned_by(self, user):
return user.id == self.owner.id
def delete(self):
for c in self.children.all():
c.delete()
self.file.delete()
return super(SithFile, self).delete()
def clean(self):
"""
Cleans up the file
"""
super(SithFile, self).clean()
if '/' in self.name:
raise ValidationError(_("Character '/' not authorized in name"))
if self == self.parent:
raise ValidationError(
_('Loop in folder tree'),
code='loop',
)
if (self == self.parent or (self.parent is not None and self in self.get_parent_list())):
raise ValidationError(
_('Loop in folder tree'),
code='loop',
)
if self.parent and self.parent.is_file:
raise ValidationError(_('You can not make a file be a children of a non folder file'))
if ((self.parent is None and SithFile.objects.exclude(id=self.id).filter(parent=None, name=self.name).exists()) or
(self.parent and self.parent.children.exclude(id=self.id).filter(name=self.name).exists())):
raise ValidationError(
_('Duplicate file'),
code='duplicate',
)
if self.is_folder:
try:
self.file.delete()
except: pass
self.file = None
self.mime_type = "inode/directory"
if self.is_file and (self.file is None or self.file == ""):
raise ValidationError(_("You must provide a file"))
def save(self, *args, **kwargs):
copy_rights = False
if self.id is None:
copy_rights = True
super(SithFile, self).save(*args, **kwargs)
if copy_rights:
self.copy_rights()
def copy_rights(self):
"""Copy, if possible, the rights of the parent folder"""
if self.parent is not None:
self.edit_groups = self.parent.edit_groups.all()
self.view_groups = self.parent.view_groups.all()
self.save()
def __getattribute__(self, attr):
if attr == "is_file":
return not self.is_folder
else:
return object.__getattribute__(self, attr)
def __str__(self):
if self.is_folder:
return _("Folder: ") + self.name
else:
return _("File: ") + self.name
def get_parent_list(self):
l = []
p = self.parent
while p is not None:
l.append(p)
p = p.parent
return l
def get_parent_path(self):
return '/'.join([p.name for p in self.get_parent_list()])
def get_display_name(self):
return self.name
class LockError(Exception):
"""There was a lock error on the object"""
pass
......@@ -311,17 +413,16 @@ class Page(models.Model):
query, but don't rely on it when playing with a Page object, use get_full_name() instead!
"""
name = models.CharField(_('page name'), max_length=30, blank=False)
parent = models.ForeignKey('self', related_name="children", null=True, blank=True, on_delete=models.SET_NULL)
parent = models.ForeignKey('self', related_name="children", verbose_name=_("parent"), null=True, blank=True, on_delete=models.SET_NULL)
# Attention: this field may not be valid until you call save(). It's made for fast query, but don't rely on it when
# playing with a Page object, use get_full_name() instead!
_full_name = models.CharField(_('page name'), max_length=255, blank=True)
owner_group = models.ForeignKey(Group, related_name="owned_page",
owner_group = models.ForeignKey(Group, related_name="owned_page", verbose_name=_("owner group"),
default=settings.SITH_GROUPS['root']['id'])
edit_groups = models.ManyToManyField(Group, related_name="editable_page", blank=True)
view_groups = models.ManyToManyField(Group, related_name="viewable_page", blank=True)
edit_groups = models.ManyToManyField(Group, related_name="editable_page", verbose_name=_("edit group"), blank=True)
view_groups = models.ManyToManyField(Group, related_name="viewable_page", verbose_name=_("view group"), blank=True)
lock_mutex = {}
class Meta:
unique_together = ('name', 'parent')
permissions = (
......
......@@ -8,9 +8,9 @@ select,
button,
textarea
{
margin: 0;
border: 1px solid;
padding: 4px;
margin: 1px;
border: none;
padding: 1px;
display: inline-block;
vertical-align: middle;
white-space: normal;
......
This diff is collapsed.
This diff is collapsed.
console.log('Guy');
$( function() {
dialog = $( ".choose_file_widget" ).dialog({
autoOpen: false,
modal: true,
});
$( ".choose_file_button" ).button().on( "click", function() {
dialog.dialog( "open" );
});
} );
This diff is collapsed.
Copyright jQuery Foundation and other contributors, https://jquery.org/
This software consists of voluntary contributions made by many
individuals. For exact contribution history, see the revision history
available at https://github.com/jquery/jquery-ui
The following license applies to all parts of this software except as
documented below:
====
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.
====
Copyright and related rights for sample code are waived via CC0. Sample
code is defined as all source code contained within the demos directory.
CC0: http://creativecommons.org/publicdomain/zero/1.0/
====
All files located in the node_modules and external directories are
externally maintained libraries used by this software which have their
own licenses; we recommend you read them, as their terms may differ from
the terms above.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This source diff could not be displayed because it is too large. You can view the blob instead.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
{
"name": "jquery-ui",
"title": "jQuery UI",
"description": "A curated set of user interface interactions, effects, widgets, and themes built on top of the jQuery JavaScript Library.",
"version": "1.12.0",
"homepage": "http://jqueryui.com",
"author": {
"name": "jQuery Foundation and other contributors",
"url": "https://github.com/jquery/jquery-ui/blob/1.12.0/AUTHORS.txt"
},
"main": "ui/widget.js",
"maintainers": [
{
"name": "Scott González",
"email": "scott.gonzalez@gmail.com",
"url": "http://scottgonzalez.com"
},
{
"name": "Jörn Zaefferer",
"email": "joern.zaefferer@gmail.com",
"url": "http://bassistance.de"
},
{
"name": "Mike Sherov",
"email": "mike.sherov@gmail.com",
"url": "http://mike.sherov.com"
},
{
"name": "TJ VanToll",
"email": "tj.vantoll@gmail.com",
"url": "http://tjvantoll.com"
},
{
"name": "Felix Nagel",
"email": "info@felixnagel.com",
"url": "http://www.felixnagel.com"
},
{
"name": "Alex Schmitz",
"email": "arschmitz@gmail.com",
"url": "https://github.com/arschmitz"
}
],
"repository": {
"type": "git",
"url": "git://github.com/jquery/jquery-ui.git"
},
"bugs": "https://bugs.jqueryui.com/",
"license": "MIT",
"scripts": {
"test": "grunt"
},
"dependencies": {},
"devDependencies": {
"commitplease": "2.3.0",
"grunt": "0.4.5",
"grunt-bowercopy": "1.2.4",
"grunt-cli": "0.1.13",
"grunt-compare-size": "0.4.0",
"grunt-contrib-concat": "0.5.1",
"grunt-contrib-csslint": "0.5.0",
"grunt-contrib-jshint": "0.12.0",
"grunt-contrib-qunit": "1.0.1",
"grunt-contrib-requirejs": "0.4.4",
"grunt-contrib-uglify": "0.11.1",
"grunt-git-authors": "3.1.0",
"grunt-html": "6.0.0",
"grunt-jscs": "2.1.0",
"load-grunt-tasks": "3.4.0",
"rimraf": "2.5.1",
"testswarm": "1.1.0"
},
"keywords": []
}
/**
* @author zhixin wen <wenzhixin2010@gmail.com>
*/
.ms-parent {
display: inline-block;
position: relative;
vertical-align: middle;
}
.ms-choice {
display: block;
width: 100%;
height: 26px;
padding: 0;
overflow: hidden;
cursor: pointer;
border: 1px solid #aaa;
text-align: left;
white-space: nowrap;
line-height: 26px;
color: #444;
text-decoration: none;
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
border-radius: 4px;
background-color: #fff;
}
.ms-choice.disabled {
background-color: #f4f4f4;
background-image: none;
border: 1px solid #ddd;
cursor: default;
}
.ms-choice > span {
position: absolute;
top: 0;
left: 0;
right: 20px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
display: block;
padding-left: 8px;
}
.ms-choice > span.placeholder {
color: #999;
}
.ms-choice > div {
position: absolute;
top: 0;
right: 0;
width: 20px;
height: 25px;
background: url('multiple-select.png') left top no-repeat;
}
.ms-choice > div.open {
background: url('multiple-select.png') right top no-repeat;
}
.ms-drop {
width: 100%;
overflow: hidden;
display: none;
margin-top: -1px;
padding: 0;
position: absolute;
z-index: 1000;
background: #fff;
color: #000;
border: 1px solid #aaa;
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
border-radius: 4px;
}
.ms-drop.bottom {
top: 100%;
-webkit-box-shadow: 0 4px 5px rgba(0, 0, 0, .15);
-moz-box-shadow: 0 4px 5px rgba(0, 0, 0, .15);
box-shadow: 0 4px 5px rgba(0, 0, 0, .15);
}
.ms-drop.top {
bottom: 100%;
-webkit-box-shadow: 0 -4px 5px rgba(0, 0, 0, .15);
-moz-box-shadow: 0 -4px 5px rgba(0, 0, 0, .15);
box-shadow: 0 -4px 5px rgba(0, 0, 0, .15);
}
.ms-search {
display: inline-block;
margin: 0;
min-height: 26px;
padding: 4px;
position: relative;
white-space: nowrap;
width: 100%;
z-index: 10000;
}
.ms-search input {
width: 100%;
height: auto !important;
min-height: 24px;
padding: 0 20px 0 5px;
margin: 0;
outline: 0;
font-family: sans-serif;
font-size: 1em;
border: 1px solid #aaa;
-webkit-border-radius: 0;
-moz-border-radius: 0;
border-radius: 0;
-webkit-box-shadow: none;
-moz-box-shadow: none;
box-shadow: none;
background: #fff url('multiple-select.png') no-repeat 100% -22px;
background: url('multiple-select.png') no-repeat 100% -22px, -webkit-gradient(linear, left bottom, left top, color-stop(0.85, white), color-stop(0.99, #eeeeee));
background: url('multiple-select.png') no-repeat 100% -22px, -webkit-linear-gradient(center bottom, white 85%, #eeeeee 99%);
background: url('multiple-select.png') no-repeat 100% -22px, -moz-linear-gradient(center bottom, white 85%, #eeeeee 99%);
background: url('multiple-select.png') no-repeat 100% -22px, -o-linear-gradient(bottom, white 85%, #eeeeee 99%);
background: url('multiple-select.png') no-repeat 100% -22px, -ms-linear-gradient(top, #ffffff 85%, #eeeeee 99%);
background: url('multiple-select.png') no-repeat 100% -22px, linear-gradient(top, #ffffff 85%, #eeeeee 99%);
}
.ms-search, .ms-search input {
-webkit-box-sizing: border-box;
-khtml-box-sizing: border-box;
-moz-box-sizing: border-box;
-ms-box-sizing: border-box;
box-sizing: border-box;
}
.ms-drop ul {
overflow: auto;
margin: 0;
padding: 5px 8px;
}
.ms-drop ul > li {
list-style: none;
display: list-item;
background-image: none;
position: static;
}
.ms-drop ul > li .disabled {
opacity: .35;
filter: Alpha(Opacity=35);
}
.ms-drop ul > li.multiple {
display: block;
float: left;
}
.ms-drop ul > li.group {
clear: both;
}
.ms-drop ul > li.multiple label {
width: 100%;
display: block;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.ms-drop ul > li label {
font-weight: normal;
display: block;
white-space: nowrap;
}
.ms-drop ul > li label.optgroup {
font-weight: bold;
}
.ms-drop input[type="checkbox"] {
vertical-align: middle;
}
.ms-drop .ms-no-results {
display: none;
}
......@@ -27,6 +27,12 @@ header a:hover {
color: #2D3;
}
#popupheader {
width: 88%;
margin: 0px auto;
padding: 0.3em 1%;
}
/*---------------------------------NAV---------------------------------*/
nav {
display: block;
......@@ -54,7 +60,6 @@ nav a:hover {
margin: 0px auto;
padding: 1em 1%;
background: white;
overflow: auto;
}
h1, h2, h3, h4, h5, h6 {
......@@ -177,6 +182,9 @@ footer{
}
/*--------------------------------MODALE-------------------------------*/
.choose_file_widget {
display: inline;
}