5 from django
import forms
6 from django
import template
7 from django
.conf
import settings
8 from django
.utils
import timezone
9 from django
.template
.base
import Context
10 from django
.contrib
.auth
.models
import User
11 from django
.core
.urlresolvers
import reverse_lazy
12 from django
.utils
.translation
import ugettext_lazy
as _
13 from django
.core
.validators
import RegexValidator
, MinLengthValidator
15 from booktype
.utils
import config
, misc
16 from booktype
.convert
import loader
as convert_loader
17 from booktype
.apps
.convert
import utils
as convert_utils
18 from booktype
.apps
.account
.models
import UserProfile
19 from booktype
.apps
.core
.forms
import BaseBooktypeForm
20 from booktype
.apps
.core
.models
import Role
, Permission
, BookSkeleton
21 from booktype
.apps
.core
.widgets
import GroupedCheckboxSelectMultiple
22 from booktype
.apps
.portal
.forms
import GroupCreateForm
23 from booktype
.apps
.portal
.widgets
import RemovableImageWidget
24 from booki
.editor
.models
import License
, Book
, BookiGroup
, Language
, METADATA_FIELDS
25 from booktype
.utils
.book
import create_book
, rename_book
, check_book_availability
26 from . import widgets
as cc_widgets
29 logger
= logging
.getLogger('booktype.controlcenter')
32 class BaseControlForm(BaseBooktypeForm
):
33 """Base class for Control Center forms"""
35 success_message
= None
37 cancel_url
= reverse_lazy('control_center:settings')
40 def initial_data(cls
):
44 def extra_context(cls
):
47 def get_cancel_url(self
):
48 return self
.cancel_url
50 def save_settings(self
, request
):
54 class SiteDescriptionForm(BaseControlForm
, forms
.Form
):
55 title
= forms
.CharField(
56 label
=_("Site title"),
58 error_messages
={'required': _('Site title is required.')},
61 tagline
= forms
.CharField(
66 frontpage_url
= forms
.CharField(
67 label
=_("Frontpage URL"),
70 help_text
=_('Can be a full absolute or relative url')
72 favicon
= forms
.FileField(
75 help_text
=_("Upload .ico file")
79 def initial_data(cls
):
81 'title': config
.get_configuration('BOOKTYPE_SITE_NAME'),
82 'tagline': config
.get_configuration('BOOKTYPE_SITE_TAGLINE'),
83 'frontpage_url': config
.get_configuration('BOOKTYPE_FRONTPAGE_URL')
86 def save_settings(self
, request
):
87 from uuid
import uuid4
89 config
.set_configuration(
90 'BOOKTYPE_SITE_NAME', self
.cleaned_data
['title'])
91 config
.set_configuration(
92 'BOOKTYPE_SITE_TAGLINE', self
.cleaned_data
['tagline'])
93 config
.set_configuration(
94 'BOOKTYPE_FRONTPAGE_URL', self
.cleaned_data
['frontpage_url'])
96 if 'favicon' in self
.files
:
97 # just check for any kind of silly error
99 prev_favicon
= config
.get_configuration('BOOKTYPE_SITE_FAVICON', None)
100 fh
, fname
= misc
.save_uploaded_as_file(self
.files
['favicon'])
101 rand_name
= 'favicon.%s.ico' % uuid4().hex[:8]
102 shutil
.move(fname
, '{}/{}'.format(settings
.STATIC_ROOT
, rand_name
))
104 config
.set_configuration(
105 'BOOKTYPE_SITE_FAVICON',
106 '{}{}'.format(settings
.STATIC_URL
, rand_name
)
109 # delete prev icon to avoid garbage
112 prev_name
= prev_favicon
.rsplit('/', 1)[-1]
113 os
.remove('{}/{}'.format(settings
.STATIC_ROOT
, prev_name
))
114 except Exception as err
:
115 logger
.exception("Unable to remove previous favicon. Msg: %s" % err
)
120 config
.save_configuration()
121 except config
.ConfigurationError
as err
:
125 class AppearanceForm(BaseControlForm
, forms
.Form
):
126 css
= forms
.CharField(
129 widget
=forms
.Textarea(attrs
={'rows': 20, 'cols': 40})
133 def initial_data(cls
):
135 f
= open('%s/css/_user.css' % settings
.STATIC_ROOT
, 'r')
136 css_content
= unicode(f
.read(), 'utf8')
141 return dict(css
=css_content
)
143 def save_settings(self
, request
):
145 # should really save it in a safe way
146 f
= open('%s/css/_user.css' % settings
.STATIC_ROOT
, 'w')
147 f
.write(self
.cleaned_data
['css'].encode('utf8'))
149 except IOError as err
:
153 class FrontpageForm(BaseControlForm
, forms
.Form
):
154 use_anonymous_page
= forms
.BooleanField(
155 label
=_('Use anonymous page'),
157 help_text
=_('Show a message, contact email or image to anonymous visitors.')
159 anonymous_message
= forms
.CharField(
160 label
=_('Anonymous page message'),
162 widget
=forms
.Textarea(attrs
={'rows': 8, 'cols': 40}),
163 help_text
=_('Message for displaying on anonymous page.')
165 anonymous_email
= forms
.EmailField(
166 label
=_('Anonymous page email'),
168 help_text
=_('Email for displaying on anonymous page.')
170 anonymous_image
= forms
.ImageField(
171 label
=_("Anonymous page image"),
173 help_text
=_("Use image/png files."),
174 widget
=RemovableImageWidget(attrs
={
175 'label_class': 'checkbox-inline',
176 'input_class': 'group-image-removable'
180 def clean_anonymous_image(self
):
181 data
= self
.cleaned_data
['anonymous_image']
182 if data
and data
.content_type
not in ['image/png']:
183 raise forms
.ValidationError("Wrong file content type.")
187 def initial_data(cls
):
191 '%s/templates/portal/welcome_message.html'
192 % settings
.BOOKTYPE_ROOT
,
195 _dict
['description'] = unicode(f
.read(), 'utf8')
198 _dict
['description'] = ''
200 _dict
['use_anonymous_page'] = config
.get_configuration('BOOKTYPE_FRONTPAGE_USE_ANONYMOUS_PAGE')
201 _dict
['anonymous_message'] = config
.get_configuration('BOOKTYPE_FRONTPAGE_ANONYMOUS_MESSAGE')
202 _dict
['anonymous_email'] = config
.get_configuration('BOOKTYPE_FRONTPAGE_ANONYMOUS_EMAIL')
203 _dict
['anonymous_image'] = config
.get_configuration('BOOKTYPE_FRONTPAGE_ANONYMOUS_IMAGE')
207 def save_settings(self
, request
):
208 static_root
= settings
.BOOKTYPE_ROOT
210 config
.set_configuration('BOOKTYPE_FRONTPAGE_USE_ANONYMOUS_PAGE', self
.cleaned_data
['use_anonymous_page'])
211 config
.set_configuration('BOOKTYPE_FRONTPAGE_ANONYMOUS_MESSAGE', self
.cleaned_data
['anonymous_message'])
212 config
.set_configuration('BOOKTYPE_FRONTPAGE_ANONYMOUS_EMAIL', self
.cleaned_data
['anonymous_email'])
214 # anonymous page image
215 destination_filename
= 'anonymous_image.png'
216 destination_dir
= '{0}/portal/frontpage/'.format(settings
.MEDIA_ROOT
)
217 destination_file_path
= '{dir}{filename}'.format(dir=destination_dir
, filename
=destination_filename
)
219 if 'anonymous_image_remove' in request
.POST
:
220 os
.remove(destination_file_path
)
221 config
.del_configuration('BOOKTYPE_FRONTPAGE_ANONYMOUS_IMAGE')
222 elif 'anonymous_image' in self
.files
:
224 fh
, fname
= misc
.save_uploaded_as_file(self
.files
['anonymous_image'])
226 if not os
.path
.exists(destination_dir
):
227 os
.makedirs(destination_dir
)
229 shutil
.move(fname
, destination_file_path
)
230 config
.set_configuration('BOOKTYPE_FRONTPAGE_ANONYMOUS_IMAGE',
231 '{0}portal/frontpage/anonymous_image.png'.format(settings
.MEDIA_URL
))
236 if not os
.path
.exists('%s/templates/portal/' % static_root
):
237 os
.makedirs('%s/templates/portal/' % static_root
)
241 '%s/templates/portal/welcome_message.html' % static_root
, 'w')
243 text_data
= self
.cleaned_data
.get('description', '')
244 for ch
in ['{%', '%}', '{{', '}}']:
245 text_data
= text_data
.replace(ch
, '')
247 f
.write(text_data
.encode('utf8'))
249 config
.save_configuration()
250 except IOError as err
:
252 except config
.ConfigurationError
as err
:
256 class LicenseForm(BaseControlForm
, forms
.ModelForm
):
257 abbrevation
= forms
.CharField(
258 label
=_("Abbreviation"),
260 error_messages
={'required': _('Abbreviation is required.')},
263 name
= forms
.CharField(
266 error_messages
={'required': _('License name is required.')},
269 url
= forms
.URLField(
270 label
=_("License URL"),
272 error_messages
={'required': _('License name is required.')},
276 success_message
= _('Successfully created new license.')
277 success_url
= "#license"
284 def extra_context(cls
):
285 return dict(licenses
=License
.objects
.all().order_by("name"))
287 def get_cancel_url(self
):
288 return "{0}{1}".format(self
.cancel_url
, self
.success_url
)
290 def save_settings(self
, request
):
294 class BookSettingsForm(BaseControlForm
, forms
.Form
):
296 "Default: visible. If this box is unchecked, create/import wizard will suggest to mark book as hidden by "
300 "If it is turned on then track changes will be enabled for all the users.")
302 create_book_visible
= forms
.BooleanField(
303 label
=_('Visible by default'),
305 help_text
=_(hlp_visible
))
307 track_changes
= forms
.BooleanField(
308 label
=_('Track changes'),
310 help_text
=_(hlp_track
))
312 create_book_license
= forms
.ModelChoiceField(
313 label
=_('Default License'),
314 queryset
=License
.objects
.all().order_by("name"),
316 help_text
=_("Default license for newly created books."))
318 create_book_language
= forms
.ModelChoiceField(
319 label
=_('Default Language'),
320 queryset
=Language
.objects
.all(),
322 help_text
=_("Default language for newly created books."))
324 create_book_metadata
= forms
.MultipleChoiceField(
325 label
=_('Metadata to be filled'),
328 widget
=forms
.CheckboxSelectMultiple(),
329 help_text
=_("Select the metadata fields to be filled in book creation wizard (selected fields will be optional)")
332 def __init__(self
, *args
, **kwargs
):
333 super(BookSettingsForm
, self
).__init
__(*args
, **kwargs
)
335 for name
, field
in self
.fields
.items():
336 if name
== 'create_book_metadata':
337 metadata_choices
= []
339 for meta_field
, label
, standard
in METADATA_FIELDS
:
340 metadata_choices
.append((
341 "%s.%s" % (standard
, meta_field
), label
))
343 field
.choices
= metadata_choices
346 def initial_data(cls
):
347 lic
= config
.get_configuration('CREATE_BOOK_LICENSE')
348 if lic
and lic
!= '':
350 license
= License
.objects
.get(abbrevation
=lic
)
351 except License
.DoesNotExist
:
356 lang
= config
.get_configuration('CREATE_BOOK_LANGUAGE')
357 if lang
and lang
!= '':
359 language
= Language
.objects
.get(abbrevation
=lang
)
360 except License
.DoesNotExist
:
365 create_book_metadata
= config
.get_configuration('CREATE_BOOK_METADATA', [])
368 'create_book_visible': config
.get_configuration('CREATE_BOOK_VISIBLE'),
369 'create_book_license': license
,
370 'create_book_language': language
,
371 'create_book_metadata': create_book_metadata
,
372 'track_changes': config
.get_configuration('BOOK_TRACK_CHANGES')
375 def save_settings(self
, request
):
376 config
.set_configuration(
377 'CREATE_BOOK_VISIBLE', self
.cleaned_data
['create_book_visible'])
379 config
.set_configuration(
380 'BOOK_TRACK_CHANGES', self
.cleaned_data
['track_changes'])
382 license
= self
.cleaned_data
.get('create_book_license')
383 if license
is not None:
384 config
.set_configuration(
385 'CREATE_BOOK_LICENSE', license
.abbrevation
)
387 config
.set_configuration('CREATE_BOOK_LICENSE', '')
389 lang
= self
.cleaned_data
.get('create_book_language')
391 config
.set_configuration(
392 'CREATE_BOOK_LANGUAGE', lang
.abbrevation
)
394 config
.set_configuration('CREATE_BOOK_LANGUAGE', '')
396 # let's save metadata fields to be used
397 create_book_metadata
= self
.cleaned_data
.get('create_book_metadata', [])
398 config
.set_configuration('CREATE_BOOK_METADATA', create_book_metadata
)
401 config
.save_configuration()
402 except config
.ConfigurationError
as err
:
406 class PrivacyForm(BaseControlForm
, forms
.Form
):
407 user_register
= forms
.BooleanField(
408 label
=_('Anyone can register'),
410 help_text
=_('Anyone can register on the site and create an account')
412 create_books
= forms
.BooleanField(
413 label
=_('Only the superuser can create books'),
416 import_books
= forms
.BooleanField(
417 label
=_('Only the superuser can import books'),
422 def initial_data(cls
):
424 'user_register': config
.get_configuration('FREE_REGISTRATION'),
425 'create_books': config
.get_configuration('ADMIN_CREATE_BOOKS'),
426 'import_books': config
.get_configuration('ADMIN_IMPORT_BOOKS')
429 def save_settings(self
, request
):
430 config
.set_configuration(
431 'FREE_REGISTRATION', self
.cleaned_data
['user_register'])
432 config
.set_configuration(
433 'ADMIN_CREATE_BOOKS', self
.cleaned_data
['create_books'])
434 config
.set_configuration(
435 'ADMIN_IMPORT_BOOKS', self
.cleaned_data
['import_books'])
438 config
.save_configuration()
439 except config
.ConfigurationError
as err
:
443 class AddPersonForm(BaseControlForm
, forms
.ModelForm
):
444 username
= forms
.CharField(
448 'required': _('Username is required.'),
449 'ivalid': _("Illegal characters in username.")
454 r
"^[\w\d\@\.\+\-\_]+$",
455 message
=_("Illegal characters in username.")
457 MinLengthValidator(3)
460 first_name
= forms
.CharField(
461 label
=_('Full name'),
463 error_messages
={'required': _('Full name is required.')},
466 email
= forms
.EmailField(
469 error_messages
={'required': _('Email is required.')},
472 description
= forms
.CharField(
473 label
=_("User description"),
475 widget
=forms
.Textarea
477 password1
= forms
.CharField(
480 error_messages
={'required': _('Password is required.')},
482 widget
=forms
.PasswordInput
484 password2
= forms
.CharField(
485 label
=_('Password confirmation'),
487 error_messages
={'required': _('Password is required.')},
489 widget
=forms
.PasswordInput
,
490 help_text
=_("Enter the same password as above, for verification.")
492 send_email
= forms
.BooleanField(
493 label
=_('Notify person by email'),
497 is_superuser
= forms
.BooleanField(
498 label
=_("This person is a superuser"),
502 success_message
= _('Successfully created new account.')
503 success_url
= reverse_lazy('control_center:people_list')
505 def __init__(self
, *args
, **kwargs
):
506 super(AddPersonForm
, self
).__init
__(*args
, **kwargs
)
507 self
.fields
.keyOrder
= ['username', 'first_name', 'email',
508 'description', 'password1', 'password2',
509 'send_email', 'is_superuser']
514 'password', 'last_login', 'groups',
515 'user_permissions', 'date_joined',
516 'is_staff', 'last_name', 'is_active'
519 def get_cancel_url(self
):
520 return self
.success_url
522 def clean_username(self
):
524 User
.objects
.get(username
=self
.cleaned_data
['username'])
525 except User
.DoesNotExist
:
528 raise forms
.ValidationError(_("That username is already taken."))
530 return self
.cleaned_data
['username']
532 def clean_password2(self
):
533 if self
.cleaned_data
['password2'] != self
.cleaned_data
['password1']:
534 raise forms
.ValidationError(_("Passwords do not match."))
536 return self
.cleaned_data
['password2']
538 def save_settings(self
, request
):
540 username
=self
.cleaned_data
['username'],
541 email
=self
.cleaned_data
['email'],
542 password
=self
.cleaned_data
['password2'],
544 if self
.cleaned_data
.get('is_superuser', False):
545 user
= User
.objects
.create_superuser(**user_data
)
547 user
= User
.objects
.create_user(**user_data
)
549 user
.first_name
= self
.cleaned_data
['first_name']
552 profile
= UserProfile
.objects
.get_or_create(user
=user
)[0]
553 profile
.description
= self
.cleaned_data
['description']
556 if self
.cleaned_data
.get('send_email', False):
557 t
= template
.loader
.get_template(
558 'booktypecontrol/new_person_email.html')
560 "username": self
.cleaned_data
['username'],
561 "password": self
.cleaned_data
['password2'],
562 "server": settings
.BOOKTYPE_URL
565 from django
.core
.mail
import EmailMultiAlternatives
566 emails
= [self
.cleaned_data
['email']]
567 site_name
= config
.get_configuration('BOOKTYPE_SITE_NAME', 'Booktype')
569 msg
= EmailMultiAlternatives(
570 _('You have a new %s account') % site_name
,
571 content
, settings
.DEFAULT_FROM_EMAIL
, emails
573 msg
.attach_alternative(content
, "text/html")
575 msg
.send(fail_silently
=False)
576 except Exception as e
:
578 '[CCENTER] Unable to send invite email to %s msg: %s' %
579 (self
.cleaned_data
['email'], e
)
585 class ArchivedUsersForm(BaseControlForm
, forms
.Form
):
589 def extra_context(cls
):
591 'archived_people': User
.objects
.filter(is_active
=False).order_by("username")
595 class EditPersonInfoForm(BaseControlForm
, forms
.ModelForm
):
596 username
= forms
.CharField(
601 'required': _('Username is required.'),
602 'ivalid': _("Illegal characters in username.")
606 r
"^[\w\d\@\.\+\-\_]+$",
607 message
=_("Illegal characters in username.")
609 MinLengthValidator(3)
612 first_name
= forms
.CharField(
613 label
=_('Full name'),
615 error_messages
={'required': _('Full name is required.')},
618 email
= forms
.EmailField(
621 error_messages
={'required': _('Email is required.')},
624 profile
= forms
.ImageField(
625 label
=_('Profile picture'),
627 widget
=RemovableImageWidget(attrs
={
628 'label_class': 'checkbox-inline',
629 'input_class': 'group-image-removable'
632 description
= forms
.CharField(
633 label
=_("User description"),
635 widget
=forms
.Textarea
638 is_superuser
= forms
.BooleanField(
639 label
=_("This person is a superuser"),
642 is_active
= forms
.BooleanField(
643 label
=_("Active account"),
645 help_text
=_("Indicate whether user's account is archived or active.")
648 def __init__(self
, *args
, **kwargs
):
649 super(EditPersonInfoForm
, self
).__init
__(*args
, **kwargs
)
650 self
.fields
.keyOrder
= ['username', 'first_name', 'email',
651 'description', 'is_superuser']
656 'password', 'last_login', 'groups',
657 'user_permissions', 'date_joined',
658 'is_staff', 'last_name'
661 def get_cancel_url(self
):
662 return reverse_lazy('control_center:people_list')
665 class PasswordForm(BaseControlForm
, forms
.Form
):
667 'password_mismatch': _("The two password fields didn't match."),
670 password1
= forms
.CharField(
673 error_messages
={'required': _('Password is required.')},
675 widget
=forms
.PasswordInput
677 password2
= forms
.CharField(
678 label
=_('Password confirmation'),
681 error_messages
={'required': _('Password is required.')},
682 widget
=forms
.PasswordInput
,
683 help_text
=_("Enter the same password as above, for verification.")
685 send_login_data
= forms
.BooleanField(
686 label
=_('Send login data'),
688 help_text
=_('Send new login data to the user via email.'),
691 short_message
= forms
.CharField(
692 label
=_('Short message'),
694 help_text
=_('Will be added in the bottom of email message text, after new user credentials.'),
695 widget
=forms
.Textarea(attrs
={'rows': 4}),
696 initial
=_('You can change password in your profile settings page.')
699 def __init__(self
, user
, *args
, **kwargs
):
701 super(PasswordForm
, self
).__init
__(*args
, **kwargs
)
703 def clean_password2(self
):
704 password1
= self
.cleaned_data
.get('password1')
705 password2
= self
.cleaned_data
.get('password2')
706 if password1
and password2
:
707 if password1
!= password2
:
708 raise forms
.ValidationError(
709 self
.error_messages
['password_mismatch'])
712 def save(self
, commit
=True):
713 self
.user
.set_password(self
.cleaned_data
['password1'])
719 class AddBookForm(BaseControlForm
, forms
.Form
):
720 title
= forms
.CharField(
722 error_messages
={'required': _('Title is required.')},
726 description
= forms
.CharField(
727 label
=_('Description'),
729 widget
=forms
.Textarea
731 owner
= forms
.ModelChoiceField(
733 error_messages
={'required': _('Book owner is required.')},
734 queryset
=User
.objects
.all().order_by("username"),
737 license
= forms
.ModelChoiceField(
739 queryset
=License
.objects
.all().order_by("name"),
740 error_messages
={'required': _('License is required.')},
743 is_hidden
= forms
.BooleanField(
744 label
=_('Hide this book from other people'),
747 cover
= forms
.ImageField(
748 label
=_('Book image'),
752 success_message
= _('Successfully created new book.')
754 def clean_title(self
):
755 if not check_book_availability(self
.cleaned_data
['title']):
756 raise forms
.ValidationError(_("That book already exists."))
757 return self
.cleaned_data
['title']
759 def save_settings(self
, request
):
761 self
.cleaned_data
['owner'],
762 self
.cleaned_data
['title']
764 book
.license
= self
.cleaned_data
['license']
765 book
.description
= self
.cleaned_data
['description']
766 book
.hidden
= self
.cleaned_data
['is_hidden']
769 if 'cover' in self
.files
:
771 fh
, fname
= misc
.save_uploaded_as_file(self
.files
['cover'])
772 book
.set_cover(fname
)
782 class ListOfBooksForm(BaseControlForm
, forms
.Form
):
786 def extra_context(cls
):
788 'books': Book
.objects
.all().order_by("title")
792 class BookRenameForm(BaseControlForm
, forms
.ModelForm
):
793 title
= forms
.CharField(
796 error_messages
={'required': _('Title is required.')},
799 url_title
= forms
.SlugField(
800 label
=_("URL title"),
803 error_messages
={'invalid': _("Illegal characters in URL title.")},
804 help_text
=_("If you leave this field empty, a URL\
805 title will be assigned automatically.")
811 'status', 'language',
813 'created', 'published',
814 'permission', 'cover',
815 'description', 'hidden',
818 fields
= ['title', 'url_title']
820 def get_cancel_url(self
):
821 return "{0}#list-of-books".format(self
.cancel_url
)
823 def save(self
, *args
, **kwargs
):
824 if self
.instance
.pk
and self
.has_changed():
825 book
= Book
.objects
.get(url_title__iexact
=self
.initial
['url_title'])
828 self
.cleaned_data
['title'],
829 self
.cleaned_data
['url_title']
831 return super(BookRenameForm
, self
).save(*args
, **kwargs
)
833 def clean_url_title(self
):
834 url_title
= self
.cleaned_data
['url_title']
836 return misc
.booktype_slugify(self
.cleaned_data
['title'])
840 class PublishingForm(BaseControlForm
, forms
.Form
):
841 """Dinamically added fields based on converters modules"""
843 def __init__(self
, *args
, **kwargs
):
844 super(PublishingForm
, self
).__init
__(*args
, **kwargs
)
846 # if we don't have external (additional) converters enabled,
847 # we must hide this options from form
848 converters
= self
.get_converters()
851 # first we need to order converters alphabetically
852 for key
, conv_klass
in converters
.items():
853 verbose_name
= getattr(conv_klass
, 'verbose_name', None)
856 "`{0}` module doesn't have verbose_name attribute specified.".format(
857 conv_klass
.__name
__))
860 # using interpolation to avoid getting the __proxy__ object from ugettext_lazy
861 labels_map
["%s" % verbose_name
] = key
863 sorted_labels
= sorted(labels_map
.keys())
864 for verbose_key
in sorted_labels
:
865 conv_name
= labels_map
[verbose_key
]
866 conv_klass
= converters
[conv_name
]
868 self
.fields
['publish_{0}'.format(conv_name
)] = forms
.BooleanField(
869 label
=verbose_key
, required
=False)
872 def get_converters():
873 return convert_loader
.find_all(module_names
=convert_utils
.get_converter_module_names())
876 def initial_data(cls
):
877 publish_options
= config
.get_configuration('PUBLISH_OPTIONS')
880 for key
in cls
.get_converters().keys():
881 values_map
['publish_{0}'.format(key
)] = key
in publish_options
885 def save_settings(self
, request
):
887 for _opt
in self
.get_converters().keys():
888 if self
.cleaned_data
.get('publish_{0}'.format(_opt
)):
891 config
.set_configuration('PUBLISH_OPTIONS', opts
)
894 config
.save_configuration()
895 except config
.ConfigurationError
as err
:
899 class PublishingDefaultsForm(BaseControlForm
, forms
.Form
):
900 book_css
= forms
.CharField(
903 widget
=forms
.Textarea(attrs
={
905 'style': 'max-width: 500px'
908 ebook_css
= forms
.CharField(
909 label
=_('E-Book CSS'),
911 widget
=forms
.Textarea(attrs
={
913 'style': 'max-width: 500px'
916 pdf_css
= forms
.CharField(
919 widget
=forms
.Textarea(attrs
={
921 'style': 'max-width: 500px'
924 odt_css
= forms
.CharField(
927 widget
=forms
.Textarea(attrs
={
929 'style': 'max-width: 500px'
934 def initial_data(cls
):
936 'book_css': config
.get_configuration('BOOKTYPE_CSS_BOOK', ''),
937 'ebook_css': config
.get_configuration('BOOKTYPE_CSS_EBOOK', ''),
938 'pdf_css': config
.get_configuration('BOOKTYPE_CSS_PDF', ''),
939 'odt_css': config
.get_configuration('BOOKTYPE_CSS_ODT', '')
942 def save_settings(self
, request
):
943 data
= self
.__class
__.initial_data()
945 if self
.cleaned_data
['book_css'] != data
['book_css']:
946 config
.set_configuration(
947 'BOOKTYPE_CSS_BOOK', self
.cleaned_data
['book_css'])
949 if self
.cleaned_data
['ebook_css'] != data
['ebook_css']:
950 config
.set_configuration(
951 'BOOKTYPE_CSS_EBOOK', self
.cleaned_data
['ebook_css'])
953 if self
.cleaned_data
['pdf_css'] != data
['pdf_css']:
954 config
.set_configuration(
955 'BOOKTYPE_CSS_PDF', self
.cleaned_data
['pdf_css'])
957 if self
.cleaned_data
['odt_css'] != data
['odt_css']:
958 config
.set_configuration(
959 'BOOKTYPE_CSS_ODT', self
.cleaned_data
['odt_css'])
962 config
.save_configuration()
963 except config
.ConfigurationError
as err
:
967 class ListOfGroupsForm(BaseControlForm
, forms
.Form
):
971 def extra_context(cls
):
973 'groups': BookiGroup
.objects
.all().order_by("name")
977 class AddGroupForm(BaseControlForm
, GroupCreateForm
, forms
.ModelForm
):
979 success_message
= _('Successfully created new group.')
984 'name', 'description', 'owner'
987 def save_settings(self
, request
):
988 group
= self
.save(commit
=False)
989 group
.url_name
= misc
.booktype_slugify(group
.name
)
990 group
.created
= timezone
.now()
993 # auto-join owner as team member
994 group
.members
.add(request
.user
)
996 # set group image if exists in post data
997 group_image
= self
.files
.get('group_image', None)
999 self
.set_group_image(group
.pk
, group_image
)
1004 class ListOfRolesForm(BaseControlForm
, forms
.Form
):
1008 def extra_context(cls
):
1010 'roles': Role
.objects
.all().order_by("name")
1014 class AddRoleForm(BaseControlForm
, forms
.ModelForm
):
1016 success_message
= _('Successfully created new role.')
1017 success_url
= '#list-of-roles'
1019 def __init__(self
, *args
, **kwargs
):
1020 super(AddRoleForm
, self
).__init
__(*args
, **kwargs
)
1022 for name
, field
in self
.fields
.items():
1023 if name
== 'permissions':
1024 field
.widget
= GroupedCheckboxSelectMultiple(
1025 choices
=Permission
.objects
.all(),
1027 'group_by': 'app_name',
1028 'css_class': 'grouped_perms'
1034 exclude
= ['members']
1036 'description': forms
.Textarea
1039 def get_cancel_url(self
):
1040 return "{0}{1}".format(self
.cancel_url
, self
.success_url
)
1042 def save_settings(self
, request
):
1047 class DefaultRolesForm(BaseControlForm
, forms
.Form
):
1049 success_url
= '#default-roles'
1050 anonymous
= 'anonymous_users'
1051 registered
= 'registered_users'
1053 anonymous_users
= forms
.ChoiceField(
1054 choices
=(), required
=False,
1055 label
=_('Role for anonymous users')
1057 registered_users
= forms
.ChoiceField(
1058 choices
=(), required
=False,
1059 label
=_('Role for registered users')
1062 def __init__(self
, *args
, **kwargs
):
1063 super(DefaultRolesForm
, self
).__init
__(*args
, **kwargs
)
1064 new_choices
= [('__no_role__', _('None'))] + [(r
.name
, r
.name
) for r
in Role
.objects
.all()]
1066 for name
, field
in self
.fields
.items():
1067 field
.choices
= new_choices
1070 def initial_data(cls
):
1072 cls
.anonymous
: config
.get_configuration(
1073 'DEFAULT_ROLE_%s' % cls
.anonymous
,
1076 cls
.registered
: config
.get_configuration(
1077 'DEFAULT_ROLE_%s' % cls
.registered
,
1082 def save_settings(self
, request
):
1083 config
.set_configuration(
1084 'DEFAULT_ROLE_%s' % self
.anonymous
,
1085 self
.cleaned_data
[self
.anonymous
]
1087 config
.set_configuration(
1088 'DEFAULT_ROLE_%s' % self
.registered
,
1089 self
.cleaned_data
[self
.registered
]
1093 config
.save_configuration()
1094 except config
.ConfigurationError
as err
:
1098 class ListOfSkeletonsForm(BaseControlForm
, forms
.ModelForm
):
1100 description
= forms
.CharField(
1101 label
=_('Description'),
1103 widget
=forms
.Textarea(attrs
={'rows': 5, 'cols': 10})
1106 success_message
= _("Successfully created new Book Skeleton")
1107 success_url
= "#list-of-skeletons"
1110 model
= BookSkeleton
1114 'skeleton_file': cc_widgets
.BkFileWidget(attrs
={'accept': 'application/epub+zip'})
1118 def extra_context(cls
):
1119 return dict(skeletons
=BookSkeleton
.objects
.all().order_by("name"))
1121 def get_cancel_url(self
):
1122 return "{0}{1}".format(self
.cancel_url
, self
.success_url
)
1124 def save_settings(self
, request
):
1128 class UserSearchForm(forms
.Form
):
1129 search
= forms
.CharField(
1130 label
=_('Search users'),
1133 help_text
=_('Search by username, email, first name or last name'),
1134 widget
=forms
.TextInput(attrs
={
1135 'placeholder': 'Example: Tolkien',
1136 'class': 'form-control',