blob: a527dd9fb845fe880d4e66632151226b0053f652 [file] [log] [blame]
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
from formencode import validators as fev
import ew as ew_core
import ew.jinja2_ew as ew
from allura.lib import validators as V
from allura.lib.widgets import form_fields as ffw
from allura.lib.widgets import forms as ff
from allura import model as M
class NullValidator(fev.FancyValidator):
perform_validation = True
def _to_python(self, value, state):
return value
def _from_python(self, value, state):
return value
# Discussion forms
class ModerateThread(ff.CsrfForm):
defaults = dict(
ew.SimpleForm.defaults,
submit_text=None)
class buttons(ew_core.NameList):
delete = ew.SubmitButton(label='Delete Thread')
class ModeratePost(ew.SimpleForm):
template = 'jinja:allura:templates/widgets/moderate_post.html'
defaults = dict(
ew.SimpleForm.defaults,
submit_text=None)
class AttachPost(ff.ForgeForm):
defaults = dict(
ff.ForgeForm.defaults,
submit_text='Attach File',
enctype='multipart/form-data')
@property
def fields(self):
fields = [
ew.InputField(name='file_info', field_type='file',
label='New Attachment')
]
return fields
class ModeratePosts(ew.SimpleForm):
template = 'jinja:allura:templates/widgets/moderate_posts.html'
defaults = dict(
ew.SimpleForm.defaults,
submit_text=None)
def resources(self):
for r in super(ModeratePosts, self).resources():
yield r
yield ew.JSScript('''
(function($){
var tbl = $('form table');
var checkboxes = $('input[type=checkbox]', tbl);
$('a[href=#]', tbl).click(function () {
checkboxes.each(function () {
if(this.checked) { this.checked = false; }
else { this.checked = true; }
});
return false;
});
}(jQuery));''')
class PostFilter(ff.ForgeForm):
defaults = dict(
ew.SimpleForm.defaults,
submit_text=None,
method='GET')
fields = [
ew.HiddenField(
name='page',
validator=fev.Int()),
ew.FieldSet(label='Post Filter', fields=[
ew.SingleSelectField(
name='status',
label='Show posts with status',
options=[
ew.Option(py_value='-', label='Any'),
ew.Option(py_value='spam', label='Spam'),
ew.Option(py_value='pending',
label='Pending moderation'),
ew.Option(py_value='ok', label='Ok')],
if_missing='pending'),
ew.InputField(name='username',
label='Show post filtered by username'),
ew.SubmitButton(label='Filter Posts')
])
]
class TagPost(ff.ForgeForm):
# this ickiness is to override the default submit button
def __call__(self, **kw):
result = super(TagPost, self).__call__(**kw)
submit_button = ffw.SubmitButton(label=result['submit_text'])
result['extra_fields'] = [submit_button]
result['buttons'] = [submit_button]
return result
fields = [ffw.LabelEdit(label='Labels', name='labels', className='title')]
def resources(self):
for r in ffw.LabelEdit(name='labels').resources():
yield r
class EditPost(ff.ForgeForm):
template = 'jinja:allura:templates/widgets/edit_post.html'
antispam = True
defaults = dict(
ff.ForgeForm.defaults,
show_subject=False,
value=None,
att_name='file_info')
@property
def fields(self):
fields = ew_core.NameList()
fields.append(ffw.MarkdownEdit(name='text'))
fields.append(ew.HiddenField(name='forum', if_missing=None))
if ew_core.widget_context.widget:
# we are being displayed
if ew_core.widget_context.render_context.get('show_subject', self.show_subject):
fields.append(
ew.TextField(name='subject', attrs=dict(style="width:97%")))
else:
# We are being validated
validator = fev.UnicodeString(not_empty=True, if_missing='')
fields.append(ew.TextField(name='subject', validator=validator))
fields.append(NullValidator(name=self.att_name))
return fields
def resources(self):
for r in ew.TextField(name='subject').resources():
yield r
for r in ffw.MarkdownEdit(name='text').resources():
yield r
yield ew.JSScript('''$(document).ready(function () {
$("a.attachment_form_add_button").click(function(evt){
$(this).hide();
$(".attachment_form_fields", this.parentNode).show();
evt.preventDefault();
});
$("a.cancel_edit_post").click(function(evt){
$("textarea", this.parentNode).val($("input.original_value", this.parentNode).val());
$(".attachment_form_fields input", this.parentNode).val('');
evt.preventDefault();
});
});''')
class NewTopicPost(EditPost):
template = 'jinja:allura:templates/widgets/new_topic_post.html'
defaults = dict(
EditPost.defaults,
show_subject=True,
forums=None)
class _ThreadsTable(ew.TableField):
template = 'jinja:allura:templates/widgets/threads_table.html'
class fields(ew_core.NameList):
_id = ew.HiddenField(validator=V.Ming(M.Thread))
subscription = ew.Checkbox(suppress_label=True)
subject = ffw.DisplayOnlyField(label='Topic')
url = ffw.DisplayOnlyField()
num_replies = ffw.DisplayOnlyField(label='Posts')
num_views = ffw.DisplayOnlyField(label='Views')
last_post = ffw.DisplayOnlyField(label='Last Post')
class SubscriptionForm(ew.SimpleForm):
template = 'jinja:allura:templates/widgets/subscription_form.html'
value = None
threads = None
show_subject = False
allow_create_thread = False
limit = None
page = 0
count = 0
submit_text = 'Update Subscriptions'
params = ['value', 'threads', 'limit', 'page', 'count',
'show_subject', 'allow_create_thread']
class fields(ew_core.NameList):
page_list = ffw.PageList()
page_size = ffw.PageSize()
threads = _ThreadsTable()
def resources(self):
for r in super(SubscriptionForm, self).resources():
yield r
yield ew.JSScript('''
$(window).load(function () {
$('tbody').children(':even').addClass('even');
});''')
# Widgets
class HierWidget(ew_core.Widget):
widgets = {}
def prepare_context(self, context):
response = super(HierWidget, self).prepare_context(context)
response['widgets'] = self.widgets
for w in self.widgets.values():
w.parent_widget = self
return response
def resources(self):
for w in self.widgets.itervalues():
for r in w.resources():
yield r
class Attachment(ew_core.Widget):
template = 'jinja:allura:templates/widgets/attachment.html'
params = ['value', 'post']
value = None
post = None
class DiscussionHeader(HierWidget):
template = 'jinja:allura:templates/widgets/discussion_header.html'
params = ['value']
value = None
widgets = dict(
edit_post=EditPost(submit_text='New Thread'))
class ThreadHeader(HierWidget):
template = 'jinja:allura:templates/widgets/thread_header.html'
defaults = dict(
HierWidget.defaults,
value=None,
page=None,
limit=None,
count=None,
show_moderate=False)
widgets = dict(
page_list=ffw.PageList(),
page_size=ffw.PageSize(),
moderate_thread=ModerateThread(),
tag_post=TagPost())
class Post(HierWidget):
template = 'jinja:allura:templates/widgets/post_widget.html'
defaults = dict(
HierWidget.defaults,
value=None,
indent=0,
page=0,
limit=25,
show_subject=False,
)
widgets = dict(
moderate_post=ModeratePost(),
edit_post=EditPost(submit_text='Post'),
attach_post=AttachPost(submit_text='Attach'),
attachment=Attachment())
def resources(self):
for r in super(Post, self).resources():
yield r
for w in self.widgets.itervalues():
for r in w.resources():
yield r
yield ew.CSSScript('''
div.moderate {
color:grey;
}
''')
yield ew.JSLink('js/jquery.lightbox_me.js')
yield ew.JSScript('''
(function () {
$('div.discussion-post').each(function () {
var post = this;
$('.submit', post).button();
$('.moderate_post', post).click(function(e){
e.preventDefault();
var mod = $(this).text();
if ($(this).hasClass('delete')) mod = 'Delete';
else if ($(this).hasClass('approve')) mod = 'Approve';
else if ($(this).hasClass('spam')) mod = 'Spam';
else if ($(this).hasClass('undo')) mod = 'Undo';
if (mod === 'Delete' && !confirm('Really delete this post?')) {
return;
}
$.ajax({
type: 'POST',
url: this.parentNode.action,
data: jQuery(this.parentNode).serialize(),
success: function() {
if (mod === 'Delete'){
$(post).remove();
}
else if (mod === 'Approve'){
$('a.shortlink, form.moderate_spam, form.moderate_approve', post).toggle();
$('div.moderate', post).removeClass('moderate');
}
else if (mod == 'Spam'){
spam_block_display($(post), 'show_spam');
}
else if (mod == 'Undo'){
spam_block_display($(post), 'hide_spam');
}
}
});
});
function spam_block_display($post, display_type) {
var spam_block = $post.find('.info.grid-15.spam-present');
var row = $post.find('.row').eq(0);
if (display_type == 'show_spam') {
spam_block.show();
row.hide();
} else if (display_type == 'hide_spam') {
spam_block.hide();
row.show();
}
}
function get_cm($elem) { return $('.CodeMirror', $elem)[0].CodeMirror; }
if($('a.edit_post', post)){
$('a.edit_post', post).click(function (evt) {
evt.preventDefault();
$('.display_post', post).hide();
// remove the options column, but have to adjust the width of the middle section which is
// already hard-coded
var $opts = $('.options:first', post);
var opts_width = $opts.outerWidth(true);
$opts.hide();
var $post_middle = $('div.grid-14:first', post);
$post_middle.data('original-width', $post_middle.width());
$post_middle.width($post_middle.width() + opts_width);
var $edit_post_form = $('.edit_post_form', post);
var cm = get_cm($edit_post_form);
$edit_post_form.show();
cm.refresh();
cm.focus();
});
$("a.cancel_edit_post", post).click(function(evt){
$('.display_post', post).show();
$('.options', post).show();
$('.edit_post_form', post).hide();
var $post_middle = $('div.grid-14:first', post);
$post_middle.width($post_middle.data('original-width'));
});
}
if($('.reply_post', post)){
$('.reply_post', post).click(function (evt) {
evt.preventDefault();
var $reply_post_form = $('.reply_post_form', post);
var cm = get_cm($reply_post_form);
$reply_post_form.show();
cm.focus();
});
$('.reply_post', post).button();
}
if($('.add_attachment', post)){
$('.add_attachment', post).click(function (evt) {
evt.preventDefault();
$('.add_attachment_form', post).show();
});
}
if($('.shortlink', post)){
var popup = $('.shortlink_popup', post);
$('.shortlink', post).click(function(evt){
evt.preventDefault();
popup.lightbox_me({
onLoad: function() {
$('input', popup).select();
}
});
});
$('.close', popup).bind('click', function() {
popup.hide();
});
}
});
//lightbox_me script to create lightbox of attached images
$('.lightbox').click(function(e) {
var image_source = $(this).data("src");
var image = $('#lightbox').find('img:first');
image.attr("src", image_source);
$('#lightbox').lightbox_me({
centered: true
});
e.preventDefault();
});
}());
''')
class PostThread(ew_core.Widget):
template = 'jinja:allura:templates/widgets/post_thread.html'
defaults = dict(
ew_core.Widget.defaults,
value=None,
indent=0,
page=0,
limit=25,
show_subject=False,
parent=None,
children=None)
class Thread(HierWidget):
template = 'jinja:allura:templates/widgets/thread_widget.html'
name = 'thread'
defaults = dict(
HierWidget.defaults,
value=None,
page=None,
limit=50,
count=None,
show_subject=False,
new_post_text='+ New Comment')
widgets = dict(
page_list=ffw.PageList(),
thread_header=ThreadHeader(),
post_thread=PostThread(),
post=Post(),
edit_post=EditPost(submit_text='Submit'))
def resources(self):
for r in super(Thread, self).resources():
yield r
for w in self.widgets.itervalues():
for r in w.resources():
yield r
yield ew.JSScript('''
$(document).ready(function () {
var thread_tag = $('a.thread_tag');
var thread_spam = $('a.sidebar_thread_spam');
var tag_thread_holder = $('#tag_thread_holder');
var allow_moderate = $('#allow_moderate');
var mod_thread_link = $('#mod_thread_link');
var mod_thread_form = $('#mod_thread_form');
if (mod_thread_link.length) {
if (mod_thread_form.length) {
mod_thread_link.click(function (e) {
mod_thread_form.show();
return false;
});
}
}
if (thread_tag.length) {
if (tag_thread_holder.length) {
var submit_button = $('input[type="submit"]', tag_thread_holder);
var cancel_button = $('<a href="#" class="btn link">Cancel</a>').click(function(evt){
evt.preventDefault();
tag_thread_holder.hide();
thread_tag.removeClass('active');
});
submit_button.after(cancel_button);
thread_tag.click(function (e) {
tag_thread_holder.show();
thread_tag.addClass('active');
// focus the submit to scroll to the form, then focus the subject for them to start typing
submit_button.focus();
$('input[type="text"]', tag_thread_holder).focus();
return false;
});
}
}
if (thread_spam.length) {
if (allow_moderate.length) {
thread_spam[0].style.display='block';
}
}
});
''')
class Discussion(HierWidget):
template = 'jinja:allura:templates/widgets/discussion.html'
defaults = dict(
HierWidget.defaults,
value=None,
threads=None,
show_subject=False,
allow_create_thread=False)
widgets = dict(
discussion_header=DiscussionHeader(),
edit_post=EditPost(submit_text='New Topic'),
subscription_form=SubscriptionForm())
def resources(self):
for r in super(Discussion, self).resources():
yield r