cms.actions + website.actions; Sounds section; player: bug fix (ask for restart on live stream), actions; remove website.Sound (not really used): move chmod/public into programs.Sound

This commit is contained in:
bkfox
2016-07-08 01:17:02 +02:00
parent e971f3f0b5
commit 88a5a9556e
19 changed files with 456 additions and 173 deletions

126
cms/actions.py Normal file
View File

@ -0,0 +1,126 @@
"""
Actions are used to add controllers available to the end user.
They are attached to models, and tested (+ rendered if it is okay)
before rendering each instance of the models.
For the moment it only can execute javascript code. There is also
a javascript mini-framework in order to make it easy. The code of
the action is then registered and executable on users actions.
"""
class Actions(type):
"""
General class that is used to register and manipulate actions
"""
registry = []
def __new__(cls, name, bases, attrs):
cl = super().__new__(cls, name, bases, attrs)
if name != 'Action':
cls.registry.append(cl)
return cl
@classmethod
def make(cl, request, object_list = None, object = None):
"""
Make action on the given object_list or object
"""
if object_list:
in_list = True
else:
object_list = [object]
in_list = False
for object in object_list:
if not hasattr(object, 'actions') or not object.actions:
continue
object.actions = [
action.test(request, object, in_list)
if type(action) == cl and issubclass(action, Action) else
str(action)
for action in object.actions
]
object.actions = [ code for code in object.actions if code ]
@classmethod
def register_code(cl):
"""
Render javascript code that can be inserted somewhere to register
all actions
"""
return '\n'.join(action.register_code() for action in cl.registry)
class Action(metaclass=Actions):
"""
An action available to the end user.
Don't forget to note in docstring the needed things.
"""
id = ''
"""
Unique ID for the given action
"""
symbol = ''
"""
UTF-8 symbol for the given action
"""
title = ''
"""
Used to render the correct button for the given action
"""
code = ''
"""
If set, used as javascript code executed when the action is
activated
"""
@classmethod
def register_code(cl):
"""
Render a Javascript code that append the code to the available actions.
Used by Actions
"""
if not cl.code:
return ''
return """
Actions.register('{cl.id}', '{cl.symbol}', '{cl.title}', {cl.code})
""".format(cl = cl)
@classmethod
def has_me(cl, object):
return hasattr(object, 'actions') and cl.id in object.actions
@classmethod
def to_str(cl, object, url = None, **data):
"""
Utility class to add the action on the object using the
given data.
"""
if cl.has_me(object):
return
code = \
'<a class="action" {onclick} action="{cl.id}" {data} title="{cl.title}">' \
'{cl.symbol}<label>{cl.title}</label>' \
'</a>'.format(
href = '' if not url else 'href="' + url + '"',
onclick = 'onclick="return Actions.run(event, \'{cl.id}\');"' \
.format(cl = cl)
if cl.id and cl.code else '',
data = ' '.join('data-{k}="{v}"'.format(k=k, v=v)
for k,v in data.items()),
cl = cl
)
return code
@classmethod
def test(cl, request, object, in_list):
"""
Test if the given object can have the generated action. If yes, return
the generated content, otherwise, return None
in_list: object is rendered in a list
"""

View File

@ -171,6 +171,11 @@ class Post (models.Model, Routable):
"""
Fields on which routes.SearchRoute must run the search
"""
actions = None
"""
Actions are a list of actions available to the end user for this model.
See aircox.cms.actions for more information
"""
def get_comments(self):
"""

View File

@ -17,7 +17,8 @@ from django.utils.translation import ugettext as _, ugettext_lazy
from honeypot.decorators import check_honeypot
from aircox.cms.forms import CommentForm
import aircox.cms.decorators as decorators
from aircox.cms.exposures import expose
from aircox.cms.actions import Actions
class Viewable:
@ -51,7 +52,7 @@ class Viewable:
setattr(Sub, k, v)
if hasattr(cl, '_exposure'):
return decorators.expose(Sub)
return expose(Sub)
return Sub
@ -224,6 +225,7 @@ class ListItem:
image = None
info = None
url = None
actions = None
css_class = None
attrs = None
@ -285,7 +287,7 @@ class List(Section):
def get_object_list(self):
return self.object_list
def prepare_object_list(self, object_list):
def prepare_list(self, object_list):
"""
Prepare objects before context is sent to the template renderer.
Return the object_list that is prepared.
@ -302,7 +304,7 @@ class List(Section):
instances of Post or ListItem.
If object_list is not given, call `get_object_list` to retrieve it.
Prepare the object_list using `self.prepare_object_list`.
Prepare the object_list using `self.prepare_list`.
Set `request`, `object`, `object_list` and `kwargs` in self.
"""
@ -314,10 +316,11 @@ class List(Section):
object_list = self.object_list or self.get_object_list()
if not object_list and not self.message_empty:
return
self.object_list = object_list
self.object_list = object_list
if object_list:
object_list = self.prepare_object_list(object_list)
object_list = self.prepare_list(object_list)
Actions.make(request, object_list = object_list)
context = super().get_context_data(request, object, *args, **kwargs)
context.update({
@ -500,7 +503,7 @@ class Search(Section):
)
@decorators.expose
@expose
class Calendar(Section):
model = None
template_name = "aircox/cms/calendar.html"
@ -535,11 +538,12 @@ class Calendar(Section):
})
return context
@decorators.expose
@expose
def render_exp(cl, *args, year, month, **kwargs):
year = int(year)
month = int(month)
return cl.render(*args, year = year, month = month, **kwargs)
render_exp._exposure.name = 'render'
render_exp._exposure.pattern = '(?P<year>[0-9]{4})/(?P<month>[0-1]?[0-9])'

View File

@ -13,7 +13,6 @@ var Actions = {
}
},
/// Init an existing action HTML element
init_action: function(item, action_id, data, url) {
var action = this.registry[action_id];
@ -54,15 +53,15 @@ var Actions = {
action = Actions.registry[action];
if(!action)
return
return;
data = item.data || item.dataset;
action.handler(data, item);
action.handler(item.data || item.dataset, item);
return true;
},
};
/*
document.addEventListener('DOMContentLoaded', function(event) {
var items = document.querySelectorAll('.action[action]');
for(var i = 0; i < items.length; i++) {
@ -72,6 +71,7 @@ document.addEventListener('DOMContentLoaded', function(event) {
Actions.init_action(item, action_id, data);
}
}, false);
*/
/// Small utility used to make XMLHttpRequests, and map results on objects.

View File

@ -60,10 +60,9 @@
{% if object.actions and 'actions' in list.fields %}
<div class="actions">
{% for action_id,action_data in object.actions.items %}
<a class="action" action="{{action_id}}"
{% for k,v in action_data.items %}data-{{ k }}="{{ v }}"{% endfor %}></a>
{% endfor %}
{% for action in object.actions %}
{{ action|safe }}
{% endfor %}
</div>
{% endif %}

View File

@ -14,6 +14,14 @@
<link rel="stylesheet" href="{% static website.styles %}" type="text/css">
{% endif %}
<script src="{% static "aircox/cms/scripts.js" %}"></script>
{% if actions %}
<script>
{{ actions|safe }}
</script>
{% endif %}
<title>{% if title %}{{ title|striptags }} - {% endif %}{{ website.name }}</title>
</head>
<body>

View File

@ -2,6 +2,7 @@ from django import template
from django.core.urlresolvers import reverse
import aircox.cms.utils as utils
import aircox.cms.actions as actions
register = template.Library()
@ -40,8 +41,12 @@ def threads(post, sep = '/'):
for post in posts[:-1] if post.published
])
@register.filter(name='around')
def around(page_num, n):
"""
Return a range of value around a given number.
"""
return range(page_num-n, page_num+n+1)

View File

@ -6,8 +6,9 @@ from django.utils.translation import ugettext as _, ugettext_lazy
from django.contrib import messages
from django.http import Http404
from aircox.cms.actions import Actions
import aircox.cms.sections as sections
import aircox.cms.sections as sections_
sections_ = sections # used for name clashes
class BaseView:
@ -101,6 +102,7 @@ class BaseView:
for k, v in self.menus.items()
if v is not self
}
context['actions'] = Actions.register_code()
context['embed'] = False
else:
context['embed'] = True
@ -163,7 +165,7 @@ class PostListView(BaseView, ListView):
return qs
def init_list(self):
def prepare_list(self):
if not self.list:
self.list = sections.List(
truncate = 32,
@ -180,8 +182,11 @@ class PostListView(BaseView, ListView):
if field in self.list.fields
]
# done in list
# Actions.make(self.request, object_list = self.object_list)
def get_context_data(self, **kwargs):
self.init_list()
self.prepare_list()
self.add_css_class('list')
context = super().get_context_data(**kwargs)