diff --git a/.gitignore b/.gitignore index 26abb08..b393d99 100644 --- a/.gitignore +++ b/.gitignore @@ -1,12 +1,6 @@ +.idea *.sqlite3 +result *.pyc *.pyo __pycache__ -*.DS_Store -*.egg* - -dist/ -docs/_build/ -.idea/ -node_modules/ -result diff --git a/translatable_fields/CHANGELOG.rst b/translatable_fields/CHANGELOG.rst deleted file mode 100644 index 126a25d..0000000 --- a/translatable_fields/CHANGELOG.rst +++ /dev/null @@ -1,7 +0,0 @@ -Changelog -========= - -0.0.1 ------ - -* Initial release diff --git a/translatable_fields/LICENSE b/translatable_fields/LICENSE deleted file mode 100644 index 67ec320..0000000 --- a/translatable_fields/LICENSE +++ /dev/null @@ -1,20 +0,0 @@ -Copyright (c) 2012 Selwin Ong - -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. \ No newline at end of file diff --git a/translatable_fields/README.rst b/translatable_fields/README.rst deleted file mode 100644 index 9998a5a..0000000 --- a/translatable_fields/README.rst +++ /dev/null @@ -1,90 +0,0 @@ -========================== -Django Translatable Fields -========================== - -Translatable model fields for Django with admin integration. Uses PostgreSQL JSONField. - -Installation -============ - - -* Add application in `settings.py` - -.. code:: python - - INSTALLED_APPS = ( - ... - 'translatable_fields', - ... - ) - -* Specify languages in `settings.py` - -.. code:: python - - # Internationalization - - LANGUAGE_CODE = 'en' - LANGUAGES = ( - ('en', 'English'), - ('ru', 'Русский') - ) - - -* Add `TranslatableField` model fields - -.. code:: python - - from django.db import models - from django.utils.translation import ugettext_lazy as _ - - from translatable_fields.models import TranslatableField - - - class Position(models.Model): - ... - title = TranslatableField( - verbose_name=_('title') - ) - description = TranslatableField( - verbose_name=_('description') - ) - ... - -* Create custom model admin form - -.. code:: python - - from django import forms - from django.contrib.postgres.forms import JSONField - from ckeditor_uploader.widgets import CKEditorUploadingWidget - - from careers.models.position import Position - from translatable_fields.widgets import TranslatableWidget - - - class PositionAdminForm(forms.ModelForm): - title = JSONField(widget=TranslatableWidget(widget=forms.TextInput)) - description = JSONField(widget=TranslatableWidget(widget=CKEditorUploadingWidget)) - - class Meta: - model = Position - fields = ( - ... - 'title', - 'description', - ... - ) - -* Create custom model admin with custom form - -.. code:: python - - from django.contrib import admin - - from careers.forms.admin.position import PositionAdminForm - - - class PositionAdmin(admin.ModelAdmin): - form = PositionAdminForm - diff --git a/translatable_fields/__init__.py b/translatable_fields/__init__.py deleted file mode 100644 index 901e511..0000000 --- a/translatable_fields/__init__.py +++ /dev/null @@ -1 +0,0 @@ -VERSION = "0.0.1" diff --git a/translatable_fields/models.py b/translatable_fields/models.py deleted file mode 100644 index 33c3bc2..0000000 --- a/translatable_fields/models.py +++ /dev/null @@ -1,26 +0,0 @@ -from django.contrib.postgres.fields import JSONField - -from translatable_fields.value import TranslatableValue - - -class TranslatableField(JSONField): - def from_db_value(self, value, *args, **kwargs): - if value is None: - return value - - instance = TranslatableValue() - instance.update(value) - - return instance - - def to_python(self, value): - if isinstance(value, TranslatableValue): - return value - - if value is None: - return value - - instance = TranslatableValue() - instance.update(value) - - return instance diff --git a/translatable_fields/static/translatable_fields/translatable_fields.css b/translatable_fields/static/translatable_fields/translatable_fields.css deleted file mode 100755 index 4575037..0000000 --- a/translatable_fields/static/translatable_fields/translatable_fields.css +++ /dev/null @@ -1,47 +0,0 @@ -.translatable_field { - display: inline-block; -} - -.translatable_field__tabs { - display: block; - margin: 0 0 6px 0; -} - -.translatable_field__tabs-item { - display: inline-block; - margin-left: 5px; - border-radius: 4px; - padding: 0 10px; - background: #79aec8; - color: #fff; - font-weight: 400; - opacity: 0.5; -} - -.translatable_field__tabs-item:first-child { - margin-left: 0; -} - -.translatable_field__tabs-item:hover { - background: #417690; - opacity: 1; -} - -.translatable_field__tabs-item label, .inline-group .translatable_field__tabs-item label { - width: auto; - font-size: 11px; - padding: 2px 0 1px 0; - text-decoration: none; - color: #fff; -} - -.translatable_field__tabs-item_active, -.translatable_field__tabs-item_active:hover { - background: #79aec8; - border-color: #79aec8; - opacity: 1; -} - -.translatable_field p.file-upload { - margin-left: 0; -} diff --git a/translatable_fields/static/translatable_fields/translatable_fields.js b/translatable_fields/static/translatable_fields/translatable_fields.js deleted file mode 100755 index e922f34..0000000 --- a/translatable_fields/static/translatable_fields/translatable_fields.js +++ /dev/null @@ -1,37 +0,0 @@ -(function($) { - var syncTabs = function($container, lang) { - $container.find('.translatable_field__tabs-item label:contains("'+lang+'")').each(function(){ - $(this).parents('.translatable_field').find('.translatable_field__tabs-item').removeClass('translatable_field__tabs-item_active'); - $(this).parents('.translatable_field__tabs-item').addClass('translatable_field__tabs-item_active'); - $(this).parents('.translatable_field').find('.translatable_field__widgets-item').hide(); - $('#'+$(this).attr('for')).parents('.translatable_field__widgets-item').show(); - }); - }; - - $(function (){ - $('.translatable_field__widgets-item').hide(); - // set first tab as active - $('.translatable_field').each(function () { - $(this).find('.translatable_field__tabs-item:first').addClass('translatable_field__tabs-item_active'); - syncTabs($(this), $(this).find('.translatable_field__tabs-item:first label').text()); - }); - // try set active last selected tab - if (window.sessionStorage) { - var lang = window.sessionStorage.getItem('translatable-field-lang'); - if (lang) { - $('.translatable_field').each(function () { - syncTabs($(this), lang); - }); - } - } - - $('.translatable_field__tabs-item label').click(function(event) { - event.preventDefault(); - syncTabs($(this).parents('.translatable_field'), $(this).text()); - if (window.sessionStorage) { - window.sessionStorage.setItem('translatable-field-lang', $(this).text()); - } - return false; - }); - }); -})(django.jQuery); diff --git a/translatable_fields/templates/translatable_fields/multiwidget.html b/translatable_fields/templates/translatable_fields/multiwidget.html deleted file mode 100755 index cc80bc6..0000000 --- a/translatable_fields/templates/translatable_fields/multiwidget.html +++ /dev/null @@ -1,17 +0,0 @@ -
-
- {% for widget in widget.subwidgets %} -
- -
- {% endfor %} -
-
- {% for widget in widget.subwidgets %} -
-{# {% include widget.template_name %}#} - {{ widget.html }} -
- {% endfor %} -
-
diff --git a/translatable_fields/value.py b/translatable_fields/value.py deleted file mode 100644 index 141d6e2..0000000 --- a/translatable_fields/value.py +++ /dev/null @@ -1,18 +0,0 @@ -from django.conf import settings -from django.utils import translation - - -class TranslatableValue(dict): - def __str__(self): - language = translation.get_language() or settings.LANGUAGE_CODE - languages = [language] - - if len(self): - languages.append(list(self.keys())[0]) - - for lang_code in languages: - value = self.get(lang_code) - if value: - return value or "" - - return "" diff --git a/translatable_fields/widgets.py b/translatable_fields/widgets.py deleted file mode 100644 index 6461d28..0000000 --- a/translatable_fields/widgets.py +++ /dev/null @@ -1,123 +0,0 @@ -import json - -from django import forms -from django.conf import settings - - -class TranslatableWidget(forms.MultiWidget): - template_name = "translatable_fields/multiwidget.html" - widget = forms.Textarea - - class Media: - js = (settings.STATIC_URL + "translatable_fields/translatable_fields.js",) - css = { - "all": ( - settings.STATIC_URL + "translatable_fields/translatable_fields.css", - ) - } - - def __init__(self, widget=None, *args, **kwargs): - if widget: - self.widget = widget - - initial_widgets = [self.widget for _ in settings.LANGUAGES] - - super().__init__(initial_widgets, *args, **kwargs) - - for ((lang_code, lang_name), sub_widget) in zip( - settings.LANGUAGES, self.widgets - ): - sub_widget.attrs["lang"] = lang_code - sub_widget.lang_code = lang_code - sub_widget.lang_name = lang_name - - def decompress(self, value): - """ - Returns a list of decompressed values for the given compressed value. - The given value can be assumed to be valid, but not necessarily - non-empty. - """ - - if isinstance(value, str): - try: - value = json.loads(value) - except ValueError: - value = {} - - result = [] - for lang_code, _ in settings.LANGUAGES: - if value: - result.append(value.get(lang_code)) - else: - result.append(None) - - return result - - def value_from_datadict(self, data, files, name): - result = dict( - [ - ( - widget.lang_code, - widget.value_from_datadict(data, files, name + "_%s" % i), - ) - for i, widget in enumerate(self.widgets) - ] - ) - if all(map(lambda x: x == "", result.values())): - return "" - return result - - def get_context(self, name, value, attrs): - context = super(forms.MultiWidget, self).get_context(name, value, attrs) - if self.is_localized: - for widget in self.widgets: - widget.is_localized = self.is_localized - # value is a list of values, each corresponding to a widget - # in self.widgets. - if not isinstance(value, list): - value = self.decompress(value) - - final_attrs = context["widget"]["attrs"] - input_type = final_attrs.pop("type", None) - id_ = final_attrs.get("id") - subwidgets = [] - - for i, widget in enumerate(self.widgets): - if input_type is not None: - widget.input_type = input_type - widget_name = "%s_%s" % (name, i) - try: - widget_value = value[i] - except IndexError: - widget_value = None - if id_: - widget_attrs = final_attrs.copy() - widget_attrs["id"] = "%s_%s" % (id_, i) - else: - widget_attrs = final_attrs - widget_attrs = self.build_widget_attrs(widget, widget_value, widget_attrs) - widget_context = widget.get_context( - widget_name, widget_value, widget_attrs - )["widget"] - widget_context.update( - dict(lang_code=widget.lang_code, lang_name=widget.lang_name) - ) - - widget_context["html"] = widget.render( - widget_name, widget_value, widget_attrs - ) - subwidgets.append(widget_context) - context["widget"]["subwidgets"] = subwidgets - - return context - - @staticmethod - def build_widget_attrs(widget, value, attrs): - attrs = dict(attrs) # Copy attrs to avoid modifying the argument. - - if ( - not widget.use_required_attribute(value) or not widget.is_required - ) and "required" in attrs: - del attrs["required"] - - return attrs