Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
AE UTBM
Sith
Commits
38622c98
Commit
38622c98
authored
Jun 01, 2017
by
Skia
🤘
Browse files
Merge branch 'wip' into 'master'
Forum improvements See merge request
!75
parents
2f5bd7d2
f3c1ab4a
Pipeline
#1009
passed with stage
in 4 minutes and 45 seconds
Changes
28
Pipelines
1
Hide whitespace changes
Inline
Side-by-side
club/migrations/0008_auto_20170515_2214.py
0 → 100644
View file @
38622c98
# -*- coding: utf-8 -*-
from
__future__
import
unicode_literals
from
django.db
import
migrations
,
models
class
Migration
(
migrations
.
Migration
):
dependencies
=
[
(
'club'
,
'0007_auto_20170324_0917'
),
]
operations
=
[
migrations
.
AlterField
(
model_name
=
'club'
,
name
=
'id'
,
field
=
models
.
AutoField
(
primary_key
=
True
,
serialize
=
False
,
db_index
=
True
),
),
]
club/models.py
View file @
38622c98
...
...
@@ -39,6 +39,7 @@ class Club(models.Model):
"""
The Club class, made as a tree to allow nice tidy organization
"""
id
=
models
.
AutoField
(
primary_key
=
True
,
db_index
=
True
)
name
=
models
.
CharField
(
_
(
'name'
),
max_length
=
64
)
parent
=
models
.
ForeignKey
(
'Club'
,
related_name
=
'children'
,
null
=
True
,
blank
=
True
)
unix_name
=
models
.
CharField
(
_
(
'unix name'
),
max_length
=
30
,
unique
=
True
,
...
...
@@ -151,11 +152,21 @@ class Club(models.Model):
return
False
return
sub
.
is_subscribed
_memberships
=
{}
def
get_membership_for
(
self
,
user
):
"""
Returns the current membership the given user
"""
return
self
.
members
.
filter
(
user
=
user
.
id
).
filter
(
end_date
=
None
).
first
()
try
:
return
Club
.
_memberships
[
self
.
id
][
user
.
id
]
except
:
m
=
self
.
members
.
filter
(
user
=
user
.
id
).
filter
(
end_date
=
None
).
first
()
try
:
Club
.
_memberships
[
self
.
id
][
user
.
id
]
=
m
except
:
Club
.
_memberships
[
self
.
id
]
=
{}
Club
.
_memberships
[
self
.
id
][
user
.
id
]
=
m
return
m
class
Membership
(
models
.
Model
):
"""
...
...
core/__init__.py
View file @
38622c98
...
...
@@ -22,3 +22,4 @@
#
#
default_app_config
=
'core.apps.SithConfig'
core/apps.py
0 → 100644
View file @
38622c98
# -*- coding:utf-8 -*
#
# Copyright 2017
# - Skia <skia@libskia.so>
#
# Ce fichier fait partie du site de l'Association des Étudiants de l'UTBM,
# http://ae.utbm.fr.
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License a published by the Free Software
# Foundation; either version 3 of the License, or (at your option) any later
# version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
# details.
#
# You should have received a copy of the GNU General Public License along with
# this program; if not, write to the Free Sofware Foundation, Inc., 59 Temple
# Place - Suite 330, Boston, MA 02111-1307, USA.
#
#
from
django.apps
import
AppConfig
from
django.dispatch
import
receiver
from
django.db.models.signals
import
pre_save
,
post_save
,
m2m_changed
class
SithConfig
(
AppConfig
):
name
=
'core'
verbose_name
=
"Core app of the Sith"
def
ready
(
self
):
from
core.models
import
User
,
Group
from
club.models
import
Club
,
Membership
from
forum.models
import
Forum
def
clear_cached_groups
(
sender
,
**
kwargs
):
if
kwargs
[
'model'
]
==
Group
:
User
.
_group_ids
=
{}
User
.
_group_name
=
{}
def
clear_cached_memberships
(
sender
,
**
kwargs
):
User
.
_club_memberships
=
{}
Club
.
_memberships
=
{}
Forum
.
_club_memberships
=
{}
print
(
"Connecting signals!"
)
m2m_changed
.
connect
(
clear_cached_groups
,
weak
=
False
,
dispatch_uid
=
"clear_cached_groups"
)
post_save
.
connect
(
clear_cached_memberships
,
weak
=
False
,
sender
=
Membership
,
# Membership is cached
dispatch_uid
=
"clear_cached_memberships_membership"
)
post_save
.
connect
(
clear_cached_memberships
,
weak
=
False
,
sender
=
Club
,
# Club has a cache of Membership
dispatch_uid
=
"clear_cached_memberships_club"
)
post_save
.
connect
(
clear_cached_memberships
,
weak
=
False
,
sender
=
Forum
,
# Forum has a cache of Membership
dispatch_uid
=
"clear_cached_memberships_forum"
)
# TODO: there may be a need to add more cache clearing
core/models.py
View file @
38622c98
...
...
@@ -223,14 +223,25 @@ class User(AbstractBaseUser):
s
=
self
.
subscriptions
.
last
()
return
s
.
is_valid_now
()
if
s
is
not
None
else
False
_club_memberships
=
{}
_group_names
=
{}
_group_ids
=
{}
def
is_in_group
(
self
,
group_name
):
"""If the user is in the group passed in argument (as string or by id)"""
group_id
=
0
g
=
None
if
isinstance
(
group_name
,
int
):
# Handle the case where group_name is an ID
g
=
Group
.
objects
.
filter
(
id
=
group_name
).
first
()
if
group_name
in
User
.
_group_ids
.
keys
():
g
=
User
.
_group_ids
[
group_name
]
else
:
g
=
Group
.
objects
.
filter
(
id
=
group_name
).
first
()
User
.
_group_ids
[
group_name
]
=
g
else
:
g
=
Group
.
objects
.
filter
(
name
=
group_name
).
first
()
if
group_name
in
User
.
_group_names
.
keys
():
g
=
User
.
_group_names
[
group_name
]
else
:
g
=
Group
.
objects
.
filter
(
name
=
group_name
).
first
()
User
.
_group_names
[
group_name
]
=
g
if
g
:
group_name
=
g
.
name
group_id
=
g
.
id
...
...
@@ -245,18 +256,26 @@ class User(AbstractBaseUser):
if
group_name
==
settings
.
SITH_MAIN_MEMBERS_GROUP
:
# We check the subscription if asked
return
self
.
is_subscribed
if
group_name
[
-
len
(
settings
.
SITH_BOARD_SUFFIX
):]
==
settings
.
SITH_BOARD_SUFFIX
:
from
club.models
import
Club
name
=
group_name
[:
-
len
(
settings
.
SITH_BOARD_SUFFIX
)]
c
=
Club
.
objects
.
filter
(
unix_name
=
name
).
first
()
mem
=
c
.
get_membership_for
(
self
)
if
name
in
User
.
_club_memberships
.
keys
():
mem
=
User
.
_club_memberships
[
name
]
else
:
from
club.models
import
Club
c
=
Club
.
objects
.
filter
(
unix_name
=
name
).
first
()
mem
=
c
.
get_membership_for
(
self
)
User
.
_club_memberships
[
name
]
=
mem
if
mem
:
return
mem
.
role
>
settings
.
SITH_MAXIMUM_FREE_ROLE
return
False
if
group_name
[
-
len
(
settings
.
SITH_MEMBER_SUFFIX
):]
==
settings
.
SITH_MEMBER_SUFFIX
:
from
club.models
import
Club
name
=
group_name
[:
-
len
(
settings
.
SITH_MEMBER_SUFFIX
)]
c
=
Club
.
objects
.
filter
(
unix_name
=
name
).
first
()
mem
=
c
.
get_membership_for
(
self
)
if
name
in
User
.
_club_memberships
.
keys
():
mem
=
User
.
_club_memberships
[
name
]
else
:
from
club.models
import
Club
c
=
Club
.
objects
.
filter
(
unix_name
=
name
).
first
()
mem
=
c
.
get_membership_for
(
self
)
User
.
_club_memberships
[
name
]
=
mem
if
mem
:
return
True
return
False
...
...
core/static/core/style.scss
View file @
38622c98
...
...
@@ -48,8 +48,8 @@ a {
.ib
{
display
:
inline-block
;
padding
:
2
px
;
margin
:
2
px
;
padding
:
1
px
;
margin
:
1
px
;
}
.w_big
{
...
...
@@ -57,11 +57,11 @@ a {
}
.w_medium
{
width
:
4
5
%
;
width
:
4
7
%
;
}
.w_small
{
width
:
2
0
%
;
width
:
2
3
%
;
}
/*--------------------------------HEADER-------------------------------*/
...
...
@@ -271,11 +271,15 @@ code {
}
blockquote
{
margin
:
10
px
;
padding
:
5
px
;
margin
:
5
px
;
padding
:
2
px
;
border
:
solid
1px
$black-color
;
}
blockquote
h5
:first-child
{
font-size
:
100%
;
}
.edit-bar
{
display
:
block
;
margin
:
4px
;
...
...
@@ -498,86 +502,132 @@ textarea {
/*------------------------------FORUM----------------------------------*/
.topic
a
,
.forum
a
,
.category
a
{
color
:
$black-color
;
}
#forum
{
a
{
color
:
$black-color
;
}
.topic
a
:hover
,
.forum
a
:hover
,
.category
a
:hover
{
color
:
#424242
;
text-decoration
:
underline
;
}
a
:hover
{
color
:
#424242
;
text-decoration
:
underline
;
}
.topic
{
border
:
solid
$primary-neutral-color
1px
;
padding
:
2px
;
margin
:
2px
;
}
.topic
{
border
:
solid
$primary-neutral-color
1px
;
padding
:
1px
;
margin
:
1px
;
p
{
margin
:
1px
;
font-size
:
smaller
;
}
}
.forum
{
background
:
$primary-neutral-light-color
;
padding
:
2px
;
margin
:
2px
;
}
.tools
{
font-size
:
x-small
;
border
:
none
;
a
{
padding
:
1px
;
}
}
.category
{
background
:
$secondary-color
;
}
.title
{
font-size
:
small
;
font-weight
:
bold
;
padding
:
2px
;
}
.message
{
padding
:
2px
;
margin
:
2px
;
background
:
$white-color
;
&
:nth-child
(
odd
)
{
.last_message
date
{
white-space
:
nowrap
;
}
.last_message
span
{
white-space
:
nowrap
;
text-overflow
:
ellipsis
;
overflow
:hidden
;
width
:
100%
;
display
:
block
;
}
.forum
{
background
:
$primary-neutral-light-color
;
padding
:
1px
;
margin
:
1px
;
p
{
margin
:
1px
;
font-size
:
smaller
;
}
}
h5
{
font-size
:
100%
;
.category
{
margin-top
:
5px
;
background
:
$secondary-color
;
.title
{
text-transform
:
uppercase
;
}
}
&
.unread
{
background
:
#d8e7f3
;
.message
{
padding
:
1px
;
margin
:
1px
;
background
:
$white-color
;
&
:nth-child
(
odd
)
{
background
:
$primary-neutral-light-color
;
}
.title
{
font-size
:
100%
;
}
&
.unread
{
background
:
#d8e7f3
;
}
}
}
.msg_author.deleted
{
background
:
#ffcfcf
;
}
.msg_author.deleted
{
background
:
#ffcfcf
;
}
.msg_content
{
&
.deleted
{
background
:
#ffefef
;
.msg_content
{
&
.deleted
{
background
:
#ffefef
;
}
display
:
inline-block
;
width
:
80%
;
vertical-align
:
top
;
}
display
:
inline-block
;
width
:
80%
;
vertical-align
:
top
;
}
.msg_author
{
display
:
inline-block
;
width
:
19%
;
text-align
:
center
;
background
:
$primary-light-color
;
img
{
max-width
:
70%
;
margin
:
0px
auto
;
.msg_author
{
display
:
inline-block
;
width
:
19%
;
text-align
:
center
;
background
:
$primary-light-color
;
img
{
max-width
:
70%
;
margin
:
0px
auto
;
}
}
}
.msg_meta
{
font-size
:
small
;
list-style-type
:
none
;
li
{
padding
:
2px
;
margin
:
2px
;
.msg_header
{
display
:
inline-block
;
width
:
100%
;
font-size
:
small
;
}
}
.forum_signature
{
color
:
#C0C0C0
;
border-top
:
1px
solid
#C0C0C0
;
a
{
.msg_meta
{
font-size
:
small
;
list-style-type
:
none
;
li
{
padding
:
1px
;
margin
:
1px
;
}
}
.forum_signature
{
color
:
#C0C0C0
;
&
:hover
{
text-decoration
:
underline
;
border-top
:
1px
solid
#C0C0C0
;
a
{
color
:
#C0C0C0
;
&
:hover
{
text-decoration
:
underline
;
}
}
}
}
...
...
core/templates/core/macros.jinja
View file @
38622c98
...
...
@@ -2,6 +2,10 @@
<a
href=
"
{{
url
(
"core:user_profile"
,
user_id
=
user.id
)
}}
"
>
{{
user.get_display_name
()
}}
</a>
{%
-
endmacro
%}
{%
macro
user_profile_link_short_name
(
user
)
-
%}
<a
href=
"
{{
url
(
"core:user_profile"
,
user_id
=
user.id
)
}}
"
>
{{
user.get_short_name
()
}}
</a>
{%
-
endmacro
%}
{%
macro
user_link_with_pict
(
user
)
-
%}
<a
href=
"
{{
url
(
"core:user_profile"
,
user_id
=
user.id
)
}}
"
class=
"mini_profile_link"
>
{{
user.get_mini_item
()
|
safe
}}
...
...
core/templates/core/
doku_
to_markdown.jinja
→
core/templates/core/to_markdown.jinja
View file @
38622c98
{%
extends
"core/base.jinja"
%}
{%
block
title
%}
{%
trans
%}
Doku t
o Markdown
{%
endtrans
%}
{%
trans
%}
T
o Markdown
{%
endtrans
%}
{%
endblock
%}
{%
block
content
%}
<form
action=
""
method=
"post"
enctype=
"multipart/form-data"
>
{%
csrf_token
%}
<input
type=
"radio"
name=
"syntax"
value=
"doku"
{%
if
request.POST
[
'syntax'
]
!=
"bbcode"
%}
checked
{%
endif
%}
>
Doku
</input>
<input
type=
"radio"
name=
"syntax"
value=
"bbcode"
{%
if
request.POST
[
'syntax'
]
==
"bbcode"
%}
checked
{%
endif
%}
>
BBCode
</input>
<textarea
name=
"text"
id=
"text"
rows=
"30"
cols=
"80"
>
{{
-
text
-
}}
</textarea>
...
...
core/templates/core/user_tools.jinja
View file @
38622c98
...
...
@@ -108,7 +108,7 @@
</ul>
<h4>
{%
trans
%}
Other tools
{%
endtrans
%}
</h4>
<ul>
<li><a
href=
"
{{
url
(
'core:
doku_
to_markdown'
)
}}
"
>
{%
trans
%}
Convert dokuwiki syntax to Markdown
{%
endtrans
%}
</a></li>
<li><a
href=
"
{{
url
(
'core:to_markdown'
)
}}
"
>
{%
trans
%}
Convert dokuwiki
/BBcode
syntax to Markdown
{%
endtrans
%}
</a></li>
<li><a
href=
"
{{
url
(
'trombi:user_tools'
)
}}
"
>
{%
trans
%}
Trombi tools
{%
endtrans
%}
</a></li>
</ul>
...
...
core/urls.py
View file @
38622c98
...
...
@@ -28,7 +28,7 @@ from core.views import *
urlpatterns
=
[
url
(
r
'^$'
,
index
,
name
=
'index'
),
url
(
r
'^
doku_
to_markdown$'
,
Doku
ToMarkdownView
.
as_view
(),
name
=
'
doku_
to_markdown'
),
url
(
r
'^to_markdown$'
,
ToMarkdownView
.
as_view
(),
name
=
'to_markdown'
),
url
(
r
'^notifications$'
,
NotificationList
.
as_view
(),
name
=
'notification_list'
),
url
(
r
'^notification/(?P<notif_id>[0-9]+)$'
,
notification
,
name
=
'notification'
),
...
...
core/utils.py
View file @
38622c98
...
...
@@ -66,6 +66,7 @@ def exif_auto_rotate(image):
return
image
def
doku_to_markdown
(
text
):
"""This is a quite correct doku translator"""
text
=
re
.
sub
(
r
'([^:]|^)\/\/(.*?)\/\/'
,
r
'*\2*'
,
text
)
# Italic (prevents protocol:// conflict)
text
=
re
.
sub
(
r
'<del>(.*?)<\/del>'
,
r
'~~\1~~'
,
text
,
flags
=
re
.
DOTALL
)
# Strike (may be multiline)
text
=
re
.
sub
(
r
'<sup>(.*?)<\/sup>'
,
r
'^\1^'
,
text
)
# Superscript (multiline not supported, because almost never used)
...
...
@@ -93,8 +94,10 @@ def doku_to_markdown(text):
text
=
re
.
sub
(
r
'\\{2,}[\s]'
,
r
' \n'
,
text
)
# Carriage return
text
=
re
.
sub
(
r
'\[\[(.*?)(\|(.*?))?\]\]'
,
r
'[\3](\1)'
,
text
)
# Links
text
=
re
.
sub
(
r
'{{(.*?)(\|(.*?))?}}'
,
r
''
,
text
)
# Images
text
=
re
.
sub
(
r
'\[\[(.*?)\|(.*?)\]\]'
,
r
'[\2](\1)'
,
text
)
# Links
text
=
re
.
sub
(
r
'\[\[(.*?)\]\]'
,
r
'[\1](\1)'
,
text
)
# Links 2
text
=
re
.
sub
(
r
'{{(.*?)\|(.*?)}}'
,
r
''
,
text
)
# Images
text
=
re
.
sub
(
r
'{{(.*?)(\|(.*?))?}}'
,
r
''
,
text
)
# Images 2
text
=
re
.
sub
(
r
'{\[(.*?)(\|(.*?))?\]}'
,
r
'[\1](\1)'
,
text
)
# Video (transform to classic links, since we can't integrate them)
text
=
re
.
sub
(
r
'###(\d*?)###'
,
r
'[[[\1]]]'
,
text
)
# Progress bar
...
...
@@ -117,14 +120,58 @@ def doku_to_markdown(text):
quote_level
+=
1
try
:
new_text
.
append
(
"> "
*
quote_level
+
"##### "
+
quote
.
group
(
2
))
line
=
line
.
replace
(
quote
.
group
(
0
),
''
)
except
:
new_text
.
append
(
"> "
*
quote_level
)
line
=
line
.
replace
(
quote
.
group
(
0
),
''
)
final_quote_level
=
quote_level
# Store quote_level to use at the end, since it will be modified during quit iteration
final_newline
=
False
for
quote
in
quit
:
# Quit quotes (support multiple at a time)
line
=
line
.
replace
(
quote
.
group
(
0
),
''
)
quote_level
-=
1
final_newline
=
True
new_text
.
append
(
"> "
*
final_quote_level
+
line
)
# Finally append the line
if
final_newline
:
new_text
.
append
(
"
\n
"
)
# Add a new line to ensure the separation between the quote and the following text
else
:
new_text
.
append
(
line
)
return
"
\n
"
.
join
(
new_text
)
def
bbcode_to_markdown
(
text
):
"""This is a very basic BBcode translator"""
text
=
re
.
sub
(
r
'\[b\](.*?)\[\/b\]'
,
r
'**\1**'
,
text
,
flags
=
re
.
DOTALL
)
# Bold
text
=
re
.
sub
(
r
'\[i\](.*?)\[\/i\]'
,
r
'*\1*'
,
text
,
flags
=
re
.
DOTALL
)
# Italic
text
=
re
.
sub
(
r
'\[u\](.*?)\[\/u\]'
,
r
'__\1__'
,
text
,
flags
=
re
.
DOTALL
)
# Underline
text
=
re
.
sub
(
r
'\[s\](.*?)\[\/s\]'
,
r
'~~\1~~'
,
text
,
flags
=
re
.
DOTALL
)
# Strike (may be multiline)
text
=
re
.
sub
(
r
'\[strike\](.*?)\[\/strike\]'
,
r
'~~\1~~'
,
text
,
flags
=
re
.
DOTALL
)
# Strike 2
text
=
re
.
sub
(
r
'article://'
,
r
'page://'
,
text
)
text
=
re
.
sub
(
r
'dfile://'
,
r
'file://'
,
text
)
text
=
re
.
sub
(
r
'\[url=(.*?)\](.*)\[\/url\]'
,
r
'[\2](\1)'
,
text
)
# Links
text
=
re
.
sub
(
r
'\[url\](.*)\[\/url\]'
,
r
'\1'
,
text
)
# Links 2
text
=
re
.
sub
(
r
'\[img\](.*)\[\/img\]'
,
r
''
,
text
)
# Images
new_text
=
[]
quote_level
=
0
for
line
in
text
.
splitlines
():
# Tables and quotes
enter
=
re
.
finditer
(
r
'\[quote(=(.+?))?\]'
,
line
)
quit
=
re
.
finditer
(
r
'\[/quote\]'
,
line
)
if
enter
or
quit
:
# Quote part
for
quote
in
enter
:
# Enter quotes (support multiple at a time)
quote_level
+=
1
try
:
new_text
.
append
(
"> "
*
quote_level
+
"##### "
+
quote
.
group
(
2
))
except
:
new_text
.
append
(
"> "
*
quote_level
)
line
=
line
.
replace
(
quote
.
group
(
0
),
''
)
final_quote_level
=
quote_level
# Store quote_level to use at the end, since it will be modified during quit iteration
final_newline
=
False
for
quote
in
quit
:
# Quit quotes (support multiple at a time)
line
=
line
.
replace
(
quote
.
group
(
0
),
''
)
quote_level
-=
1
final_newline
=
True
new_text
.
append
(
"> "
*
final_quote_level
+
line
)
# Finally append the line
if
final_newline
:
new_text
.
append
(
"
\n
"
)
# Add a new line to ensure the separation between the quote and the following text
else
:
new_text
.
append
(
line
)
...
...
core/views/site.py
View file @
38622c98
...
...
@@ -37,7 +37,7 @@ from itertools import chain
from
haystack.query
import
SearchQuerySet
from
core.models
import
User
,
Notification
from
core.utils
import
doku_to_markdown
from
core.utils
import
doku_to_markdown
,
bbcode_to_markdown
from
club.models
import
Club
def
index
(
request
,
context
=
None
):
...
...
@@ -98,17 +98,20 @@ def search_json(request):
}
return
JsonResponse
(
result
)
class
Doku
ToMarkdownView
(
TemplateView
):
template_name
=
"core/
doku_
to_markdown.jinja"
class
ToMarkdownView
(
TemplateView
):
template_name
=
"core/to_markdown.jinja"
def
post
(
self
,
request
,
*
args
,
**
kwargs
):
self
.
text
=
request
.
POST
[
'text'
]
self
.
text_md
=
doku_to_markdown
(
self
.
text
)
if
request
.
POST
[
'syntax'
]
==
"doku"
:
self
.
text_md
=
doku_to_markdown
(
self
.
text
)
else
:
self
.
text_md
=
bbcode_to_markdown
(
self
.
text
)
context
=
self
.
get_context_data
(
**
kwargs
)
return
self
.
render_to_response
(
context
)
def
get_context_data
(
self
,
**
kwargs
):
kwargs
=
super
(
Doku
ToMarkdownView
,
self
).
get_context_data
(
**
kwargs
)
kwargs
=
super
(
ToMarkdownView
,
self
).
get_context_data
(
**
kwargs
)
try
:
kwargs
[
'text'
]
=
self
.
text
kwargs
[
'text_md'
]
=
self
.
text_md
...
...
counter/migrations/0012_auto_20170515_2202.py
0 → 100644
View file @
38622c98
# -*- coding: utf-8 -*-
from
__future__
import
unicode_literals
from
django.db
import
migrations
,
models
class
Migration
(
migrations
.
Migration
):
dependencies
=
[
(
'counter'
,
'0011_auto_20161004_2039'
),
]
operations
=
[
migrations
.
AlterField
(
model_name
=
'permanency'
,
name
=
'end'
,
field
=
models
.
DateTimeField
(
db_index
=
True
,
verbose_name
=
'end date'
,
null
=
True
),
),
]
counter/models.py
View file @
38622c98
...
...
@@ -431,7 +431,7 @@ class Permanency(models.Model):
user
=
models
.
ForeignKey
(
User
,
related_name
=
"permanencies"
,
verbose_name
=
_
(
"user"
))
counter
=
models
.
ForeignKey
(
Counter
,
related_name
=
"permanencies"
,
verbose_name
=
_
(
"counter"
))
start
=
models
.
DateTimeField
(
_
(
'start date'
))
end
=
models
.
DateTimeField
(
_
(
'end date'
),
null
=
True
)
end
=
models
.
DateTimeField
(
_
(
'end date'
),
null
=
True
,
db_index
=
True
)
activity
=
models
.
DateTimeField
(
_
(
'last activity date'
),
auto_now
=
True
)