Jump to content


Photo

Module: Languages (multi language content management)


  • Please log in to reply
125 replies to this topic

#1 Oliver

Oliver

    Sr. Member

  • Members
  • PipPipPipPip
  • 134 posts
  • 27

  • LocationBasel, Switzerland

Posted 20 August 2011 - 07:41 AM

Don’t use with PW2.2 because of potential conflicts with its built-in multi-language support.
Module will be replaced by a «context» manager module allowing you to define language specific page trees (among other possibilities) based upon PW2.2’s own language management.

Download Module ProcessLanguages, Version 0.08, more alpha than beta, from GitHub:

https://github.com/L...rocessLanguages

# 2011-11-19 0.08. Language specific error404 pages implemented.

# 2011-11-10 Test version with basic features (0.07). Added template variable to enable switching between translations.

# 2011-11-10 Test version with basic features (0.05). Uninstall still gets kind of manual. ;) Mapping doesn’t work properly, when pages from outside are moved into a language’s page tree.

 

And that’s how it all started:
Hey, I’m working on an module to implement multiple language support in ProcessWire.

Until now the module provides an admin panel to set up additional languages. Every language is represented in the site’s page hierarchy by an own home page automatically generated as a child of the site’s root. The language’s home page id is associated with the language entry in the admin panel. If the site’s root is called, a hooked function automatically redirects to the home page of the language defined as default.

The module is inspired by the concept of modx module Babel by Jacob Class.

The next steps on my list are:
  • Setting up a db table associating the different language versions of a page with each other.
  • (If possible: ) Adding an additional tab to the Page Edit top menu (Content, Children, etc.) providing an overview of the other language versions of the edited page…
    Question: Is there a possibility to hook into an event allowing me to change the page edit top menu and add a "language" tab to the edit form? Any idea?
  • … and allowing to create language clones of a page by copying it to other languages’ page structure.
Would be great if there were some hints or suggestions to make it work and maybe more useful in the end!
Thanks in advance.

#2 jbroussia

jbroussia

    Full Member

  • Members
  • PipPipPip
  • 72 posts
  • 13

  • LocationFrance

Posted 20 August 2011 - 08:07 AM

That's good news.

Would/will it be possible to indicate if a page has to be cloned to other languages ? Some pages could be language specific, that is, only available in some language (think of local news for example)...

#3 Oliver

Oliver

    Sr. Member

  • Members
  • PipPipPipPip
  • 134 posts
  • 27

  • LocationBasel, Switzerland

Posted 20 August 2011 - 08:31 AM

The cloning won’t happen automatically. If it works out the way I want it to, you’ll be able to mark the languages in the page edit languages tab you want a clone to be created for. Maybe there should be an option to mirror the default language’s page structure completely on creation of an additional language, too?

---

Jbroussia, just found your post: http://processwire.c...opic,391.0.html
Funny, you were thinking of the same approach just two weeks before me!

---

As you guys already talked about in the other thread, cloning pages isn’t that easy.

I decided to deal with cloning a languages page tree completely on creation of a new language–as long as I have no idea how to manipulate the page edit admin page. But because of the complexity of the page-fields-structure, I’ve no idea of all the things that have to be duplicated with and for each page to get a kind of clone–in view of fields, values, roles etc.

Would be great to get a hint here, too.  ???

[Edited by Adam] Glued three of your posts together. If you want to enhance/edit your message (add another idea(s), etc.), please use 'Edit' function, instead of double (triple, ...) posting. Thank you!

#4 jbroussia

jbroussia

    Full Member

  • Members
  • PipPipPip
  • 72 posts
  • 13

  • LocationFrance

Posted 20 August 2011 - 03:17 PM

Well if the page copy/clone functionality is added to the core API, a big step will be done.

#5 ryan

ryan

    Reiska

  • Administrators
  • 7,810 posts
  • 6604

  • LocationAtlanta, GA

Posted 21 August 2011 - 11:13 AM

Since you guys need this sooner rather than later, I'll put it higher up in the priority list. Cloning a page will be relatively easy. Haven't tried it, but I think this may work:

<?php
$cloned = clone $page; 
$cloned->setIsNew(true); 
$cloned->id = 0; 
$cloned->name = $cloned->name . "-clone";
$cloned->save();

Assuming that much works, what I can say for certain would not work is file fields. This is where the process would become more complex than this, though shouldn't be bad. I'm adding this to the to-do queue on GitHub.

Adding another tab (like a 'language' tab) to the PageEdit screen is going to most likely involve hooking into the ProcessPageEdit::buildForm function and appending another InputfieldWrapper to the form it returns. Probably best for me to setup an example for this–let me know when you are at the point where you want it and I'll get it going.

Thanks,
Ryan



#6 jbroussia

jbroussia

    Full Member

  • Members
  • PipPipPip
  • 72 posts
  • 13

  • LocationFrance

Posted 21 August 2011 - 03:05 PM

Oh in my case there is not need to hurry, the multi-language site I'm planning to do will only have a few pages, for now I can still do manual copies.

Does this clone function clone pages *and* their children ?
And what is "setIsNew" for ?

#7 ryan

ryan

    Reiska

  • Administrators
  • 7,810 posts
  • 6604

  • LocationAtlanta, GA

Posted 21 August 2011 - 04:41 PM

That code snippet above wouldn't clone a tree, just a single page. When this is implemented in the API, we'd let it be optional as to whether to clone the tree (i.e. be recursive). But we'd definitely provide that option, just like the delete() function provides that option (though requires it if the page has children).

That setIsNew function isn't part of the public API, but it would be necessary in this case to tell PW that this is a new page it should create when it comes time to save. Otherwise the cloned copy would still remember that it was originally loaded from the DB. I can't think of another situation where you would ever need to use the setIsNew function, so that probably won't ever make it into the public API.

#8 Oliver

Oliver

    Sr. Member

  • Members
  • PipPipPipPip
  • 134 posts
  • 27

  • LocationBasel, Switzerland

Posted 22 August 2011 - 06:33 AM

Thanks for the input, Ryan. I already experimented with cloning a page and at least got a page transfer from one language tree to another. I guess I’ve to go through the fields one by one and create a copy by setting an new field of the same type and duplicating the data/values/files associated with it. Do I also have to clone any db data–if there is–of role or permission objects related to the page?

As soon as the cloning process works, I would focus on the implementation of the language management in the page edit form. So I let you know when I got there!

#9 ryan

ryan

    Reiska

  • Administrators
  • 7,810 posts
  • 6604

  • LocationAtlanta, GA

Posted 22 August 2011 - 08:39 AM

I'm assuming you are using PW 2.1 for all of this. But let me know if not. You are working with stuff that is somewhat different between PW 2.0 and 2.1, and I would definitely recommend sticking with 2.1 if you are creating new modules since we're just about to the point of making it the current stable version.  

You shouldn't have to clone any of the fields (or database entries), PW will do that for you. I just tested the example here and it did work for me (cloning the page and the fields). Though I should note I did have to add $cloned->setOutputFormatting(false); before I saved it. That would only be necessary if you put the code snippet in a template file, as I did.

The only field I can think of that it would not currently work for is a file or image field... but I'll work on that.

You don't have to clone any role/permission stuff because those are assigned at the template, not the page. So your clone will simply inherit the same roles from the template as the original did.

Do you need the recursive/children cloning like jbroussia mentioned? If so let me know because I will want to get that new $pages->clone function in place this week so that you don't have to wait for this.




#10 Oliver

Oliver

    Sr. Member

  • Members
  • PipPipPipPip
  • 134 posts
  • 27

  • LocationBasel, Switzerland

Posted 22 August 2011 - 08:55 AM

Recursive cloning as an option would be great!

If you post me the example code for extending the page edit form, I could work on the language versions interface while you are working on your "clone" update.

And to answer your question: Yes, I’m using 2.1.

#11 jbroussia

jbroussia

    Full Member

  • Members
  • PipPipPip
  • 72 posts
  • 13

  • LocationFrance

Posted 22 August 2011 - 09:46 AM

If I understand correctly, cloning file or image fields is different because those are stored in the file system instead of the DB !? What about the other fields, do you clone them by creating new copies of the content in the DB, or do you "link" new fields to the original fields ID in the DB ?

#12 ryan

ryan

    Reiska

  • Administrators
  • 7,810 posts
  • 6604

  • LocationAtlanta, GA

Posted 22 August 2011 - 09:53 AM

@jbroussia: PW is writing all new fields when you do $page->save(). Since we set it's ID as 0 and called $page->setIsNew(true), we tricked ProcessWire into thinking it was a brand new page that it had never saved before. So from that point it is creating all new fields when you save it.

Also, when we did "$cloned = clone $page;" it automatically cloned all the fields in memory too, so $page and $clone aren't sharing the same memory even for that moment. This occurs because PW implements PHP5's __clone() method and specifically sets up new fields at at that time. Admittedly, this is one of the first opportunities I've had to actually use it.

You are right that file fields are different because they have corresponding files on the disk. PW needs to know where to place those files, and there's no way for it to know until the page has actually been saved (since the directories are based on the page's ID). So this is one of the details we would have to take care of with a $pages->clone function by saving/creating the page, then copying the files, then saving again.  

#13 ryan

ryan

    Reiska

  • Administrators
  • 7,810 posts
  • 6604

  • LocationAtlanta, GA

Posted 22 August 2011 - 10:01 AM

Here is an example module for adding tabs to a page. I tried to cover a few things you might want to do after you've added the tab too, just as examples. If you want to download the module file (rather than copying/pasting), it is attached at the end of this message.

<?php

class PageTabExample extends WireData implements Module {

    /**
     * Provide information about this module to ProcessWire
     *
     */
    public static function getModuleInfo() {
        return array(
            'title' => 'Page Tab Example',
            'summary' => 'Example of adding tabs to the page editor',
            'version' => 001,
            'permanent' => false,
            'autoload' => true,
            'singular' => true,
            );
    }

    /**
     * Add the hook
     *
     */
    public function init() {
        $this->addHookAfter("ProcessPageEdit::buildForm", $this, 'buildForm');
    }

    /**
     * Hook called after PageEdit::buildForm is called
     *
     */
    public function buildForm(HookEvent $event) {
        $form = $event->return;
        $this->example1($form);
        $this->example2($form);
        $event->return = $form;
    }

    /**
     * Add a simple tab to the page edit form
     *
     * Note that would technically add the tab link after the 'view' link on the form
     * See example2 to see how we get the view link at the end (assuming that's where you want it)
     *
     */
    protected function example1($form) {

        // create the new tab
        $tab = new InputfieldWrapper();
        $tab->attr('id', 'tabExample1');
        $tab->attr('title', 'Example 1');

        // create a general markup field as an example
        $field = $this->modules->get("InputfieldMarkup");
        $field->attr('name', 'fieldExample1');
        $field->label = "Example 1 field";
        $field->attr('value', "<p>This can contain any markup you want</p>");

        // add the field to the tab
        $tab->add($field);

        // add the tab to the form
        $form->add($tab);
    }

    /**
     * Add a tab before the 'view' link
     *
     * Also demonstrates setting or retrieving a value submitted to a field in the tab.
     *
     */
    protected function example2($form) {

        // first we have to find and remove the view link, temporarily
        $view = $form->children('id=ProcessPageEditView')->first();
        $form->remove($view);

        // create the tab
        $tab = new InputfieldWrapper();
        $tab->attr('id', 'tabExample2');
        $tab->attr('title', 'Example 2');

        // now add our new tab and field
        $field = $this->modules->get("InputfieldTextarea");
        $field->attr('name', 'fieldExample2');
        $field->label = 'Example 2 field';
        $field->description =
            "Enter some text. The text you enter won't get saved with the page, " .
            "so you'd have to do something with it in the same hook that displays this.";

        // since this isn't technically part of the page's fields, we have to
        // handle any input submitting to the field if we want it.
        if($this->input->post->fieldExample2 !== null) {
            $field->processInput($this->input->post);
            // we'll store in the temporary session var just an example.
            // using session just for example, you likely would save your value elsewhere.
            $this->session->fieldExample2 = $field->attr('value');
        } else {
            // retrieve the value from the session var.
            $field->attr('value', $this->session->fieldExample2);
        }

        // add the field to the tab
        $tab->add($field);

        // add the tab to the form
        $form->add($tab);

        // now add that view link back to the form at the end
        $form->add($view);
    }
}

Attached Files



#14 Oliver

Oliver

    Sr. Member

  • Members
  • PipPipPipPip
  • 134 posts
  • 27

  • LocationBasel, Switzerland

Posted 22 August 2011 - 05:59 PM

Thanks a lot for the example, Ryan. Works pretty fine. Got the language tab working and just have to finalize the listing of the associated versions and the processing of the form/mapping in the database. Will look into that tomorrow evening.

If we get the cloning done, maybe I can provide a test version of the module to the end of the week.

#15 ryan

ryan

    Reiska

  • Administrators
  • 7,810 posts
  • 6604

  • LocationAtlanta, GA

Posted 22 August 2011 - 09:32 PM

Glad that works! I will move forward with the clone/duplicate API function here.


#16 jbroussia

jbroussia

    Full Member

  • Members
  • PipPipPip
  • 72 posts
  • 13

  • LocationFrance

Posted 23 August 2011 - 02:50 AM

Goooood  ;D

#17 ryan

ryan

    Reiska

  • Administrators
  • 7,810 posts
  • 6604

  • LocationAtlanta, GA

Posted 23 August 2011 - 06:24 PM

Hey guys, I've added the $pages->clone() function to the API and you'll see it in the latest commit (or the one before it). I've tested on a few pages with files and children and so far so good. There's a lot that it has to do, so this is not something I'd use to clone a 1000 page tree, but it should be fine recursively cloning most stuff.

I'd like to do more testing but have run out of time for today, and opted to go ahead and push it to the source tonight so that I could get it to you sooner rather than later. I think it's pretty solid, but just to be safe, I recommend considering that function unstable until we've run it through a lot more scenarios.

Here's how to use it:

<?php

// clone a page or tree of pages
$copy = $pages->clone($page); 

// clone a page or tree of pages, and give it a new parent
$copy = $pages->clone($page, $newParent);

The $copy that is returned is already in the database, ready to go, and does not need to be saved again. Please let me know if you run into any issues with it. Don't use in a production environment until we've had a few more people test it.

Thanks,
Ryan


#18 Oliver

Oliver

    Sr. Member

  • Members
  • PipPipPipPip
  • 134 posts
  • 27

  • LocationBasel, Switzerland

Posted 24 August 2011 - 09:17 AM

Thanks Ryan, you are the best!

I updated the core files with your changes and implemented the cloning. Works fine so far.

Some important steps to make for me, yet:
  • Setting up hooks dealing with manually created pages in language trees, deleting pages from language trees, moving pages in, out of or into trash from language trees to keep the pagemap consistent. The pagemap associates all the different language version of a page with each other.
  • Creating new translation from the "Languages" tab of the page edit dialog. Offering "by cloning current page" and "by creating new empty page". Maybe there also has to be the possibility to map the currently edited page manually with an existing one?
  • Providing an var $languages to make a list of all languages (for language links), the current page’s translations in the different languages as well as the current active language accessible in templates.

As soon as I got the languages tab processing part done, I’ll post a download link here for you guys to have a look. Would love to get some feedback on this.

#19 apeisa

apeisa

    Hero Member

  • Moderators
  • 3,161 posts
  • 1712

  • LocationVihti, Finland

Posted 24 August 2011 - 09:48 AM

This sounds excellent, really looking forward to get my hands on and testing.

#20 Soma

Soma

    Hero Member

  • Moderators
  • 5,057 posts
  • 3824

  • LocationSH, Switzerland

Posted 24 August 2011 - 01:58 PM

Sounds great, this can be very helpful for creating multilanguage sites. Many thank for working on that. I surely would like to help test when a first release candidate is ready.

I'm working here in Switzerland. Having 4 native languages, most projects require multilanguage sites.

@somartist | modules created | support me, flattr my work flattr.com





0 user(s) are reading this topic

0 members, 0 guests, 0 anonymous users