ToolPart Team

Innovative Intelligence
Aug 04

Fenntartható OpenERP telepítés

Ügyfeleink joggal kérik, hogy folyamatosan kövessük az általunk forgalmazott ERP rendszerek újabb és újabb változatait. Ezt mi karbantartási szerződésünk keretében természetesen vállaljuk is. Az alábbiakban bemutatjuk azt a néhány egyszerű technikát, amivel technológiai oldalról a rendszer frissítése biztonságosan végrehajtható.

Virtualenv

Először is fontos, hogy az OpenERP rendszereket külön-külön virtuális környezetbe telepítjük. Így - ha szükséges - könnyen vissza tudunk állni a korábbi kódra.

Adatbázis

Migráláskor különösen fontos az adatok megőrzése. Ezért fontos, hogy a létező adatokról biztonsági mentést készítünk, amit szükség esetén vissza tudunk tölteni a rendszerbe.

Előzetes tesztelés

Jól ismert axióma, hogy minden program tartalmaz hibákat. Ez azt is jelenti, hogy az új verziók akár új hibákat is tartalmazhatnak. Ennek elkerülése érdekében a rendszert a telepítés előtt saját cégünknél rakjuk fel, és ott használjuk. Így az esetleges hibákat még az ügyfélnek való telepítés előtt megtaláljuk. Külön figyelmet szentelünk a saját fejlesztésű modulok kompatibilitásának ellenőrzésére.

Biztonság az ügyfélnek

Megbízható partnerként, a maximumot hozzuk ki magunkból, és addig ülünk a gépeink előtt míg ügyfeleinknek megelégedetten nem bólintanak.

Apr 19

Pisa XHTML2PDF utf encoding problem

In our eKe project we wanted to generate pdf documents from html files. We have chosen XHTML2PDF for doing this.

We have used Pisa because we needed a simple and easily integrable pdf generator. For using pisa you need to include some modules from your python code and create your own templates for generating the document. Here is an example python code fragment:

    def write_pdf(template_src, context_dict):
        template = get_template(template_src)
        context = Context(context_dict)
        html  = template.render(context)
        result = StringIO.StringIO()
        pdf = pisa.pisaDocument(StringIO.StringIO(
            html.encode("UTF-8")), result)
        if not pdf.err:
            return http.HttpResponse(result.getvalue(), \
                 mimetype='application/pdf')
        return http.HttpResponse('There was an error during pdf generation! %s' % cgi.escape(html))

So in theory it is very easy to use, we have manage to work it correctly for generating English texts in ten minutes. However we had a problem with Pisa when we wanted to create a PDF in Hungarian language. It was not generating the hungarian characters: ő ű Ő Ű (UTF-8 character encoding) instead it only showed rectangles. We have checked it with ISO-8859-2 encoding and it was working fine. This was not enough for us. We wanted to create a language independent program, where the users could choose from any language, from Turkish to Polish and Russian so we had to use UTF-8 encoding.

We have searched a lot of places on the Internet to solve this problem and according to the help of others this would work:

In the python code the pdf generating line is the following:

    pdf = pisa.pisaDocument(StringIO.StringIO(
        html.encode("UTF-8")), result, link_callback=fetch_resources,
        encoding="utf-8", xhtml=True)

In the template file you have to insert this to the head section:

        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />

And add this to your css file or html files "style" section.

@font-face {
  font-family: "DejaVuSans";
  src: url("http://www.toolpart.hu/static/media/medialibrary/2010/04/
DejaVuSans.ttf");
}

html {
        font-family: "DejaVuSans";
}

But it still didn't worked. The problem was with the url of the font file. Somehow the program didn't recognized it. We have also tried it with

    src: url( {{ MEDIA_URL }}fonts/DejaVuSans.ttf);

but it didn't worked this way.

Then we used absolute path (of django, not linux, so I think it should be no problem if you are using your application on different servers).

@font-face {
    font-family: "DejaVu";
    src: url(/static/fonts/DejaVuSans.ttf);

}

where /static/ is my {{ media_root }} directory.

This way you can use correct UTF-8 encodig in pdf documents generated with Pisa. Hope it helps.

Apr 08

Beszámoló a konferenciáról

Több mint 30 fordítóiroda képviselője gyűlt össze február 25-én, Budapesten, az ECE City Center, Corner Rendezvényközpontjában, hogyt részt vehessen a ToolPart Team Kft. által megszervezett „Fordítóirodák a középpontban” című fordítói szakmai konferencián. Amellett, hogy a rendezvényen innovatív jellegű programok kerültek bemutatásra, a résztvevők számára alkalom nyílt arra is, hogy mind egymással, mind pedig az előadókkal megtárgyalhassák a felmerülő kérdéseket, illetve, hogy egy nyílt fórum keretein belül tudjanak beszélgetni a szakmabeli hallgatóság többi tagjával az őket ért benyomásokról.

a konferencia szünetében

A ToolPart Team Kft.l vezető programozója, Zoboki Tamás bemutatta az eKe Translation Management Software müködését, amely a cég fő termékének számít. Bemutatásra kerültek a fordítás menedzsment szoftver fő alkalmazási lehetőségei, sajátosságai. A program tulajdonságai között leginkább említésre méltó a fordítóirodák igényeit illető nagymértékű testreszabhatóság, az internetalapúság, ebből kifolyólag a gyors és zökkenőment elérhetőség, valamint a rugalmas szerkezeti felépítés.

A Kilgray Kft. ügyvezető igazgatója, Kis Balázs részletes tájékoztatást nyújtott a MemoQ fordítástámogató szoftver alkalmazási lehetőségeiről az érdeklődőknek. A program konferencián említett tulajdonságai közt a legfontosabbak a moduláris felépítés, hálózati támogatottság, integráltság és a nagyfokú kompatibilitás voltak.

A Private Equity Tanácsadó Kft. képviselője, Havran Zsolt bővebb tájékoztatást nyújtott az ide vonatkozó magyarországi Európai Uniós pályázati lehetőségekről. Ezek a pályázatok információs és kommunikációs technológiai megoldások bevezetését célozzák meg, növelve ezáltal a kis- és középvállalkozások jövedelemtermelő és versenyképességét. Jelen esetet tekintve ezek a kezdeményezések a közeljövőben pozítiv hatással lesznek mind a fordítóirodák tevékenységére, mind pedig az Unió elképzeléseinek megvalósulására.

Nagy Viktor beszélget az M-Prospecttel

Nagy Viktor, a ToolPart Team Kft. ügyvezető igazgatója pozítivan vélekedett a konferencia eredményeivel kapcsolatban: „A fordítóirodák intenzív érdeklődést mutatnak cégünk termékeit illetően, mi pedig igyekszünk, hogy mind a belföldi, mind pedig a külföldi piacon meg tudjuk állni a helyünket és felül tudjuk múlni partnereink elvárásait. Frissen indult cégként nagyon fontosnak tartjuk, hogy képesek legyünk hatékonyan ráhangolódni az ügyfeleink szükségleteire és, hogy olyan innovatív megoldásokkal lépjünk elő, amelyek minden esetben vonzóak lehetnek az együttmüködő felek számára. Emellett profi programozógárdánk minden keretek közt nagy hangsúlyt fektet arra, hogy ne az ügyfélnek kelljen alkalmazkodnia a programhoz, hanem a mi programunk alkalmazkodjon az ügyfél igényeihez.”

Mar 01

Our mostly loved and hated command

On a shared hosting @ webfaction having your permissions set up properly is definitely important and difficult. Especially, when you would like to give read access to your static web-accessible files.

Your three friends are

  • getfacl,
  • setfacl, and
  • find

While the best ones are find . -type f -exec setfacl -m d:o:r-- '{}' ;

and it's so hard to find on the net!

Feb 11

Fordítóirodák a középpontban

Cégünk szakmai konferenciát szervez fordítók és fordítóirodák számára, melynek címe:

Fordítóirodák a középpontban
projektmenedzsment, fordító memória és pályázati támogatások

Előadó cégek:

  • ToolPart Team Kft. (eKe)
  • Kilgray Kft. (MemoQ)
  • Private Equity Tanácsadó Kft.

A rendezvény helyszíne: ECE City Center irodaház, Corner Rendezvényközpont – Anker terem (1051 Budapest, Bajcsy-Zsilinszky út 12.)

A rendezvény időpontja: 2010. február 25., 8.00 óra

Program:

8.00 – 8.15
regisztráció
8.15 – 9.00
az eKe Translation Management Software bemutatása, (projektmenedzsment szoftver fordítóirodák számára). Előadó: ToolPart Team Kft.
9.00 – 9.30
szünet
9.30 – 10.15
a MemoQ fordító memória program ismertetése. Előadó: Kilgray Kft.
10.15 – 11.00
fordítóirodák által igénybe vehető, aktuális pályázati lehetőségek ismertetése. Előadó: Private Equity Tanácsadó Kft.

Amennyiben Ön egyéni fordító vagy fordítóiroda tagja, és részt kíván venni a konferencián, részvételi szándékát, kérjük, az alábbi e-mail címünkön jelezze: toolpart@toolpart.hu

Bármilyen további kérdése van, szívesen állunk rendelkezésére elérhetőségeinken.

Feb 11

FeinCMS LocaleLookupMiddleware

As we are running a multilingual website we needed an easy way to place links around the web. Like we would like to put a link on a forum for translators so that they can find our eKe Translation Project Management software, but when they arrice on our site they get to the page with their request.LANGUAGE_CODE. This could be easily achieved with a simple middleware presented below.

from feincms.module.page.models import Page
from django.http import HttpResponseRedirect, Http404
from django.conf import settings

class LanguageLookupMiddleware(object):
    '''
    Redirect to a language specific page if it exists. First look up the request's 
    language, then the default language.
    '''
    def process_response(self, request, response):
        if response.status_code != 404:
            return response # No need to check for a flatpage for non-404 responses.

try:
            language = [short for short, long in settings.LANGUAGES if short==request.LANGUAGE_CODE][0]
        except KeyError:
            pass
        else:
            response = self.get_page(language, request.path) and \
                HttpResponseRedirect('/%s%s' % (language, request.path)) or response

if response.status_code == 404:
            response = self.get_page(settings.LANGUAGES[0][0], request.path) and \
                HttpResponseRedirect('/%s%s' % (settings.LANGUAGES[0][0], request.path)) or \
                response
        return response

def get_page(self, language, path):
        try:
            return Page.objects.page_for_path_or_404('/%s%s' % (language, path))
        except Http404:
            return False

What this middleware does? First it checks for a page request.LANGUAGE_CODE + request.path. If nothing is found then moves on and redirects you to settings.LANGUAGES[0][0] + request.path or gives a 404 error if the page does not exist.

Feb 07

How to translate your company?

As we work from a small country in a international company from the very beginning it was important for us to translate our website, every document we write on several languages, and create our software with proper i18n support. On this path we have already gathered some insights, and we hope you will find these useful too, and perhaps could share your view as well. So how do we hold our translations up-to-date on several languages?

First things first

First of all we have selected one of our collegues to be responsible for every translation job in the company. He is the one who coordinates the translators' work, receives new source and translated files as well, and sends them back to their source.

Second, as there are hundreds of languages in the world, we decided on a language preference list. No document is written before the same document exists on a more important language. (Was this correct english? Anyway, let's continue.)

  1. English
  2. German
  3. Russian
  4. Hungarian
  5. Roumanian

As we are from Hungary, and have people who speak good English and/or German, but never both, Hungarian actually become the 2nd on the list, but this was a must. So we always keep in mind that the German translations are more important to have them ready.

An interesting insight that came out from this ordering was that one of our collegues who was not very friendly with our international-openness actually accepted this decision, and his approach changed a lot thanks to this small decision. This showed us how important well articulated decisions are.

Bits and bytes

But what software do we use? Actually we use only one, but recommend two others. The reason is very simple. We use pootle to administer software translations. This is an immense help. We just wrote two scripts that syncs between our repository (managed with Bazaar) and pootle's, and that's all. This way our translation files are always up-to-date, and the translators can easily reach them whenever they see fit.

Then we have tested several tools to help the translators choose the best software. Our earlier work as IT consultant thought us that users are not always stupid, they are usually simply ignorant. Thus pointing them out some good applications can change their usual workflow. As we work with student and amateur translators to do the job we decided to check out these apps quickly as they often don't have the necessary background. Finally, we've found 5 notable applications:

The first two apps are closed source, proprietary apps for translators. They are well-known in the translating industry, but as we were looking for some software for students our main interest was in their feature lists. There we've learned terms like translation memory (TM), TM server, glossary, vocabulary etc... So we were looking for this functionality in the other applications.

We have found that virtaal is really nice and easy to use under Windows and Ubuntu 9.10+, but failed to make it run under Ubuntu 8.4 (LTS) in 5 minutes. Otherwise it seems the be a really nice software. Besides Virtaal, OmegaT can handle po files as well.

Then for our promotional material and website we have two tools, for the offline guys we recommend OmegaT. It's a really nice software, but a bit slow. On the other hand for the wired we opt for Google Translator Toolkit. in either case we use only one translation memory for everything (per language), and we expect that in the future this will allow us to get a huge efficiency gain.

As one tool is often better than two, we are going with OmegaT and have dropped Virtaal. But Google's Translator Toolkit stays still as it's the easiest to use.

Setting up OmegaT

An OmegaT project consists a whole directory structure. For every project you can define where your translation memories, glossary and dictionary files are stored, and you should put your translatable files under another subdirectory. Similarly the translated files will be generated in a 5th directory.

In order to gain the most synergy from our translations, we recommend to set up the system as the following directory structure:

translations/toolpart
- tm
- glossary
- dictionary
- project1
  - target
- project2
  - target

while the source directory can be set to anywhere on the system.

This is our workflow. Do you have some other ideas? We would be really interested in them.

Feb 05

Related objects listing under the admin site

Sometimes you might not want to create admin inlines, but still want to link to some related data. We faced this problem in the eKe Project Management app where the related objects were often too complex to be shown as inlines. Thus we came up with a simple solution to extend the admin interface. Our solution simply gives a listing of related objects underneath the inline elements. It collects automatically all the reverse relationships ending on _set, but allows to exclude or register new relationships as well.

Admin - Related objects

First of all we need a simple utility object. We had a brilliant idea for the name of this module: utils.py.

class AlreadyRegistered(Exception):
    pass

class RelatedObjectLookup(object):
    '''
    Retrieves related objects defined by reverse relationships or given by the user.
    '''

def __init__(self):
        self._related = []
        self._exclude = []

def register(self, relation):
        '''
        Add a relationship to be looked up
        '''
        if relation in self._exclude:
            self._exclude.remove(relation)
        if relation in self._related:
            raise AlreadyRegistered("The relationship %s is alread registered" % relation)
        self._related.append(relation)

def unregister(self, relation):
        '''
        Exclude a relationship from the lookup
        '''
        if relation in self._related:
            self._related.remove(relation)
        if relation in self._exclude:
            pass
            #raise AlreadyRegistered("The relationship %s is alread registered" % relation)
        else:
            self._exclude.append(relation)

def get_related_objects(self, klass):
        '''
        Get related objects as a dictionary of verbose_name_plural -> related_set.all()
        '''
        related = [field for field in dir(klass) if field.endswith('_set') and field not in self._exclude]
        related.extend(self._related)
        # get distinct values
        related = {}.fromkeys(related).keys()

# get rid of wrong fields
        for field in related:
            try:
                getattr(klass, field)
            except AttributeError:
                related.remove(field)

# sort by model name
        def get_model_name_plural(field):
                return getattr(klass, field).model._meta.verbose_name_plural
        related = sorted(related, key=get_model_name_plural)

# put it all together
        objects = []
        for field in related:
            try:
                admin_add_url = getattr(klass, field).model.get_admin_add_url()
            except AttributeError:
                admin_add_url = False
            objects.append({'model': getattr(klass, field).model._meta.verbose_name_plural,
                            'data': getattr(klass, field).all(),
                            'admin_add_url': admin_add_url, 
                            })
        return objects

def __call__(self, klass):
        return self.get_related_objects(klass)

related_objects = RelatedObjectLookup()

This defines a simple registry class to add and retrieve related objects. To wire this up in our application we had to edit one of the admin templates. Thanks to django's template blocks finally we overwrite just the bare minimum. As we want to extend a change view, we should use its template $TEMPLATE_DIR/$YOUR_APP/$YOUR_MODEL/change_form.html is the template we should edit. Thankfully it contains a {{after_related_objects}} block that is perfect for our purpose.

{% extends "admin/change_form.html" %}
{% load i18n captureas %}

{% block after_related_objects %}

{% trans "Related objects" %}

{% for related_object in original.get_related_objects %} {% captureas model_name_plural %}{{ related_object.model }}{% endcaptureas %}

{% blocktrans %}Related {{ model_name_plural }}{% endblocktrans %}

    {% for element in related_object.data %} {% if element.get_admin_change_url %}
  • {{ element }}
  • {% else %}
  • {{ element }}
  • {% endif %} {% empty %}
  • {% blocktrans %}No related {{ model_name_plural }} found{% endblocktrans %}
  • {% endfor %} {% if related_object.admin_add_url %}
  • {% trans "Add new" %}
  • {% endif %}
{% endfor %}
{% endblock %}

A couple of notes are in order here. First as we live in an international environment i18n is crucial for us. Unfortunately blocktrans can't handle non-simple variables, as a result we need the captureas template tags too. Once these are loaded we loop over {{ object.get_related_objects }}, but hey ... such a method does not exist yet! This will be the next step once we have finished explaining the template code.

In the loop we check for two more method calls {{ element.get_admin_change_url }} and {{ element.get_admin_add_url }}. As one might guess from the methods' name these provide the url to add and change a model instance. We opted for this approach because by default the RelatedObjectLookup class registers all the related objects, but by checking for these url methods we can easily filter out unnecessary objects; actually we just don't provide these methods.

But how do these methods look like and where do they live? Let's assume you have two models:

  • Blog
  • Post

Clearly we would like to show the related Posts for a Blog. To accomplish this we should add the following code to our two models

from utils import related_objects
class Blog(models.Model):

....

def get_related_objects(self):
        return related_objects

class Post(models.Model):

....

@staticmethod
    @models.permalink
    def get_admin_add_url():
        return ('admin:myapp_post_add',)

@permalink
    def get_admin_change_url(self):
        return ('admin:myapp_post_change', (self.pk,))

That's all! Of course, if we would like to add other objects we should extend utils.related_objects appropriately. We can do this either by importing it e.g. in your apps urls.py to extend the registered objects of a different app's model, or by simply extending it in the model's get_related_objects method. Actually, we are using both approaches in our project management application.

Jan 28

FeinCMS Integration with Mingus

After our site was finished we wanted to add a blog to it. Althought FeinCMS was the most suitable Content Management System for us, its blog content is more of an example and not really ussable. We have tried to make it work, but it was only a waste of time. You can see on http://groups.google.com/group/django-feincms that others have the same problem. We have searched the net to find a blog engine that was easy to use and we could easily integrate it with feincms. We have decided to use Mingus. Mingus itself leverages only existing reusable apps, and django-snippets. The heart of Mingus is the basic-blog from django-basic-apps. It is simple and well designed.

The integration of Mingus with FeinCMS was quite easy. I have downloaded the latest Django-Mingus package from GitHub. Then put it in the class path so my previously built FeinCMS page can access it, then installed all the apps from the requirements file.

  • settings.py: after this I have added the 'mingus' to INSTALLED_APPS I've had to merge mingus' settings with feincms'.
  • urls.py as feincms is set up to handle all the pages, it is the last entry in our urls.py. So I've merged mingus' urlpatterns by adding one line

    url(r'^blog/', include('mingus.urls')),

  • Template files: Mingus has a base.html that is extended by all their templates. We had a base.html as well, but it had nothing mingus related, and we didn't wanted to add everything to it as our feincms templates extend it as well. Moreover mingus uses much more javascript than our simple CMS pages, etc.. But Mingus has a blog/base_blog.html page as well. So we've decided to overwrite this one, and all the mingus extends to use this one. As sometimes we need disqus, this turned out to be a bit tricky. Finally, we've defined a new block called disqus that is usually rewritten to be empty.

  • JavaScript and css: one of the reasons why we've chosen mingus was that it incorporates plenty of nice scripts, like the prettify javascript. Of course these should be added to our templates as well, moreover we had to copy/symlink them under MEDIA_ROOT.

As a last step we've run syncdb, and now we have a nice mingus blog together with feincms.

Jan 27

Integrating WYMEditor with django

There are several possibilities to integrate a WYSIWYG editor under the admin. But what to do if you want a general solution? For our eKE Translation Project Management we opted to use WYMEditor, and wrote a couple of snippets to get it properly working.

There are several possibilities to integrate a WYSIWYG editor under the admin. But what to do if you want a general solution that works with small differences under the admin and your main site? For our eKe Translation Project Management we opted to use WYMEditor, and wrote a couple of snippets to get it properly in the system.

The first step was to load WYMEditor for every textarea on a page. Using jQuery this was done by

$(document).ready(function() { $("textarea").wymeditor({updateSelector: ":submit", updateEvent: "click"}); });

This works fine for a basic editor, but misses two things. First of all, as we build software for the global market, we should be able to specify the language WYMEditor uses. We can specify the current page's language in the html template, and pass that variable into $().wymeditor() as an option.

So we overwrote "admin/base_site.html" to add some javascript. The proper place seems to be the extrahead block, but actually this is false. As admin templates often include {{block.super}} only at the end of their extrahead overwrite, and the javascript above gets included in the extrahead (using the Media class of the given ModelAdmin) we have to add the script before extrahead. A possibility is the extrastyle block.

{% block extrastyle %} {{ block.super }} {% endblock %}

and add the necessary code to our wymeditor loader:

$(document).ready(function() { var wym_options = {updateSelector: ":submit", updateEvent: "click"} $.extend(wym_options, wym_custom); $("textarea").wymeditor(wym_options); });

Even better now, but what about the user frontend? Even the simplest contact us form is greatly enhanced by a well-working and simple WYSIWYG interface. Unfortunately, our current result is a bit confusing to the user as a v1.0 user has no clue what "Containers" and "Classes" might mean. So we should disable these. Similarly, one might prefer hiding the wymeditor logo as well. To achieve these we finally created a default.js file that is included in base.html and admin/base_site.html as well with the following code

varwym_defaults={updateSelector:":submit",updateEvent:"click",logoHtml:'',};

and added to our base.html

&lt;scriptlanguage="JavaScript"type="text/javascript"&gt;varwym_custom={lang:'{{ request.LANGUAGE_CODE|lower }}',containersHtml:'',classesHtml:'',}&lt;/script&gt;

and finally we initialize the wymeditors using

$(document).ready(function(){varwym_options={}$.extend(wym_options,wym_defaults,wym_custom);$("textarea").wymeditor(wym_options);});

Now everything works like charm. Both our staff and users have a nice user experience, and we did not need much coding either. But we like coding! So to ease our future work we have created a simple Widget that adds the basic wymeditor script and our wymeditor loader script to the form's media

classGUITextAreaWidget(forms.Textarea):passclassMedia:js=("js/jquery.wymeditor.min.js","js/fancyedit.js")

where fancyedit.js contains the wymeditor initializing code.

Jan 26

Use wysiwyg editor in django admin

When the site structure was finished we had to migrate our content to it. In FeinCMS there are two content types available for storing data: Raw Content and Rich Text. Raw Content is storing and showing simple html code. Rich Text also does the same, just it uses the TinyMCE Wysiwyg Editor to show end edit the data. TinyMCE is a simple javascript based wysiwyg editor what can be integrated to any textarea, because its javascript based, you have to enable javascript in you browser. This way in Rich Text it is much simpler to format texts, tags, links, etc. The latest Mingus also has the TinyMCE integrated to it so it can be used there, too.

In the following you can read the most simplest way to integrate TinyMCE with your admin page:

1. You can download TinyMCE from here.

2. Now, unzip the package you've just downloaded and go to "script" and copy the whole "tiny_mce" folder and put it in your settings.MEDIA_ROOT directory in your project.

3. Create a new HTML file at the following location: "templates/admin/base_site.html". This will replace the default "base_site.html" from the admin app templates.

4. Add the following code to the newly created file, reload your admin page and voila, it works:

{% extends "admin/base.html" %}
{% load i18n %}
{% block title %}{{ title }} | {% trans "Django Admin" %}{% endblock %}
{% block extrahead %}
<script type="text/javascript" src="/{{media_root}}/tiny_mce/tiny_mce.js"></script>
<script type="text/javascript">
tinyMCE.init({
// General options
mode : "textareas",
theme : "advanced",
plugins : "safari,pagebreak,style,layer,table,save,advhr,advimage,advlink,emotions,iespell,inlinepopups,insertdatetime,preview,media,searchreplace,print,contextmenu,paste,directionality,fullscreen,noneditable,visualchars,nonbreaking,xhtmlxtras,template",
// Theme options
theme_advanced_buttons1 : "save,newdocument,|,bold,italic,underline,strikethrough,|,justifyleft,justifycenter,justifyright,justifyfull,styleselect,formatselect,fontselect,fontsizeselect",
theme_advanced_buttons2 : "cut,copy,paste,pastetext,pasteword,|,search,replace,|,bullist,numlist,|,outdent,indent,blockquote,|,undo,redo,|,link,unlink,anchor,image,cleanup,help,code,|,insertdate,inserttime,preview,|,forecolor,backcolor",
theme_advanced_buttons3 : "tablecontrols,|,hr,removeformat,visualaid,|,sub,sup,|,charmap,emotions,iespell,media,advhr,|,print,|,ltr,rtl,|,fullscreen",
theme_advanced_buttons4 : "insertlayer,moveforward,movebackward,absolute,|,styleprops,|,cite,abbr,acronym,del,ins,attribs,|,visualchars,nonbreaking,template,pagebreak",
theme_advanced_toolbar_location : "top",
theme_advanced_toolbar_align : "left",
theme_advanced_statusbar_location : "bottom",
theme_advanced_resizing : true,
});
</script>
{% endblock %}
Jan 25

FeinCMS

Our homepage is managed in FeinCMS, the best Content Management System in Django what is available on the Internet. FeinCMS has a few interesting features:

Page structuring: It lets you reorder page content blocks using a drag-drop interface, and you can add as many content blocks to a region (e.g. the sidebar, the main content region, etc.). It provides helper functions, which provide ordered lists of page content blocks.

Custom contents (ContactForm, RawContent, RichTextContent, ApplicationContent, etc.): FeinCMS knows nothing about content - just enough to create an admin interface for your own page content types. It allows you to easily add your own content types, which you can then manage through a javascript-enhanced interface.

Easily manageable multi-language support: You can connect together pages from different languages as translations of each other. During run-time in every page a link is available to its translations. This way a multi-language site can be easily created.

Principles of information protection | Legal Notice