Jump to content


Photo

Module: Languages (multi language content management)


  • Please log in to reply
125 replies to this topic

#21 Oliver

Oliver

    Sr. Member

  • Members
  • PipPipPipPip
  • 133 posts
  • 25

  • LocationBasel, Switzerland

Posted 24 August 2011 - 02:46 PM

Soma, so we are kind of neighbours. I’m living and working in Basel.

Can anyone give me a hint regarding the process hooks? How do I access the created/deleted/trashed page’s id (or page object) in my method dealing with the passed event object?

#22 ryan

ryan

    Hero Member

  • Administrators
  • 5,985 posts
  • 3386

  • LocationAtlanta, GA

Posted 24 August 2011 - 02:47 PM

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.


Sounds like a lot to keep track of. But I think you'll be able to do most (or all) of it by hooking into:

$pages->save()
$pages->delete()
$pages->trash()
$pages->restore()

If you only want to hook these things in the interactive PageEdit, as opposed to all of the API, then you'll want your hooks to check the value of wire('process') (or $this->process) and confirm that it is 'ProcessPageEdit'.

Let me know if you find you need other hooks that we don't already have. In PW, making a function hookable is as easy as prepending 3 underscores to the beginning of it. So I've not attempted to add all possible hooks in the system, instead taking the approach of adding new core hooks as needs arise.

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?


Sounds great! You've really thought this through. I like all that I hear.

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.


In PW, you can make a new API var (available to all templates and modules) by calling:

Wire::setFuel('api_var_name', $your_api_var);

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.


Thanks, looking forward to it!

#23 ryan

ryan

    Hero Member

  • Administrators
  • 5,985 posts
  • 3386

  • LocationAtlanta, GA

Posted 24 August 2011 - 03:04 PM

Can anyone give me a hint regarding the process hooks? How do I access the created/deleted/trashed page’s id (or page object) in my method dealing with the passed event object?


Here are examples of hooks you asked about:

<?php

public function init() {
    $this->pages->addHookBefore('save', $this, 'pagesBeforeSave'); 
    $this->pages->addHookAfter('save', $this, 'pagesAfterSave'); 
    $this->pages->addHookAfter('delete', $this, 'pagesAfterDelete'); 
    $this->pages->addHookAfter('trash', $this, 'pagesAfterTrash'); 
    $this->pages->addHookAfter('restore', $this, 'pagesAfterRestore'); 
}

public function pagesBeforeSave(HookEvent $event) {
    $page = $event->arguments[0]; 
    if(!$page->id) { 
        // set your own var name to check after page saved
        $page->this_is_new = true; 
        // do anything else you want here
    } else {
        // page is an existing page
    }
}

public function pagesAfterSave(HookEvent $event) {
    $page = $event->arguments[0]; 

    if($page->this_is_new) {
        // check the var you set before to determine if it's new 
        // page now has an ID, so do something with it if you want
    }

    if($page->parentPrevious) {
        // page was moved. $page->parent is new parent
        // and $page->parentPrevious is old parent
    }
}

public function pagesAfterDelete(HookEvent $event) {
    $page = $event->arguments[0]; 
    if($page->is(Page::statusDeleted)) {
        // page was deleted
    }
}

public function pagesAfterTrash(HookEvent $event) {
    $page = $event->arguments[0];
    // page was moved to the trash
}

public function pagesAfterRestore(HookEvent $event) {
    $page = $event->arguments[0];
    // page was moved out of the trash
}



#24 Soma

Soma

    Hero Member

  • Moderators
  • 3,421 posts
  • 1944

  • LocationSH, Switzerland

Posted 24 August 2011 - 03:11 PM

what about moving pages in the tree?

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


#25 ryan

ryan

    Hero Member

  • Administrators
  • 5,985 posts
  • 3386

  • LocationAtlanta, GA

Posted 24 August 2011 - 03:14 PM

See the pagesAfterSave function above. If the page was moved then $page->parentPrevious is set. That var holds the old parent, while $page->parent holds the new parent.

#26 Soma

Soma

    Hero Member

  • Moderators
  • 3,421 posts
  • 1944

  • LocationSH, Switzerland

Posted 24 August 2011 - 03:16 PM

Soma, so we are kind of neighbours. I’m living and working in Basel.


Hey cool, I'm from Schaffhausen and do work in Zurich. So yes, we're neighbours. Nice to meet you here on a "US" forum! :)

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


#27 ryan

ryan

    Hero Member

  • Administrators
  • 5,985 posts
  • 3386

  • LocationAtlanta, GA

Posted 24 August 2011 - 03:41 PM

Based on where the majority of our traffic comes from, I think it might be considered a European forum. :)

#28 Oliver

Oliver

    Sr. Member

  • Members
  • PipPipPipPip
  • 133 posts
  • 25

  • LocationBasel, Switzerland

Posted 25 August 2011 - 05:28 AM

BTW: I saw some code parts checking on a NoMove property of templates to keep them being moved within the page tree. But I didn’t find a way to actually create a not movable template. Would be perfect to lock the language trees’ position in the hierarchy. Any idea?

#29 Pete

Pete

    Administrator

  • Administrators
  • 1,802 posts
  • 727

  • LocationChester, England

Posted 25 August 2011 - 06:00 AM

If I'm understanding you right, you could create a template called "language_root" and set it's "Allowed template(s) for parents" status to "Home" (or whatever the template of the homepage is on your particular site). That setting is under the Family tab when editing a template anyway.

Whilst it doesn't prevent someone from reordering the pages at that level, I think that would lock those pages in place though I've not actually tested it.

#30 ryan

ryan

    Hero Member

  • Administrators
  • 5,985 posts
  • 3386

  • LocationAtlanta, GA

Posted 25 August 2011 - 08:00 AM

Pete is right about that approach. Another option is that you can edit /site/config.php and change $config->advanced = true; Following that, when you edit templates, you'll see a 'system' tab that provides several other more advanced options like the 'noMove' option. These are intended primarily for PW core and module development.

#31 Oliver

Oliver

    Sr. Member

  • Members
  • PipPipPipPip
  • 133 posts
  • 25

  • LocationBasel, Switzerland

Posted 25 August 2011 - 08:49 AM

As I create the template as a clone of the home template on installation of the module, couldn’t I set the noMove using the templates attr() method?
At least I’ve set the parentTemplates to only accept the root page’s template.

#32 ryan

ryan

    Hero Member

  • Administrators
  • 5,985 posts
  • 3386

  • LocationAtlanta, GA

Posted 25 August 2011 - 09:00 AM

That's correct. While there isn't an attr() method on the Template object, you could set it from the API like this:

$template->noMove = 1;

or

$template->set('noMove', 1);

(the first example routes through the set() method behind the scenes)

#33 Oliver

Oliver

    Sr. Member

  • Members
  • PipPipPipPip
  • 133 posts
  • 25

  • LocationBasel, Switzerland

Posted 25 August 2011 - 02:34 PM

Yeah, sorry, mixed that up. Of course "set"! ;)
Was a bit too much dealing with InputField attributes today. Anyway, got it working. Thanks.

#34 ryan

ryan

    Hero Member

  • Administrators
  • 5,985 posts
  • 3386

  • LocationAtlanta, GA

Posted 26 August 2011 - 11:44 AM

Like with jQuery, the attr() method is just way way to say "I only want to deal with HTML form attributes". Anything you set with attr() is going to end up as an HTML attribute in the form field, and likewise anything you get with it is from an HTML attribute.

Inputfields also have the usual set(), get() and direct reference ($a->b) means of access just like everywhere else in PW. You can also use them to get/set attributes just like with attr() and they are largely interchangeable… except for setting a new HTML attribute. If the attribute doesn't already exist, and you set it like $inputfield->something = '...', there's no way for ProcessWire to know that you want "something" that to be an HTML attribute. But if you set it with $inputfield->attr('something', '...'), then PW knows you intend that to be an HTML attribute.

My recommendation is to use the attr() method when setting and getting HTML attributes on an Inputfield. While it's not always necessary to do so, it does make it clear in your code when you are dealing with HTML attributes versus other properties in an Inputfield.

Btw, if you ever see reference to setAttribute() and getAttribute() with PW Inputfields, these are just verbose ways of saying: attr(key, value) and attr(key). The verbose versions are just there for internal use and hooks. So it's preferable to use attr() in most API code.


#35 Oliver

Oliver

    Sr. Member

  • Members
  • PipPipPipPip
  • 133 posts
  • 25

  • LocationBasel, Switzerland

Posted 28 August 2011 - 12:09 PM

The further I get, the more complex it gets. Have still to deal with some issues, before I can give you a test version covering the most important functionalities.

#36 Pete

Pete

    Administrator

  • Administrators
  • 1,802 posts
  • 727

  • LocationChester, England

Posted 28 August 2011 - 12:10 PM

Keep up the good work - I'm looking forward to giving this one a test :)

#37 Soma

Soma

    Hero Member

  • Moderators
  • 3,421 posts
  • 1944

  • LocationSH, Switzerland

Posted 29 August 2011 - 08:38 AM

The further I get, the more complex it gets. Have still to deal with some issues, before I can give you a test version covering the most important functionalities.


I can imagine. Do you need any support on this?

I think this will be one of the more important module for me/us.
If there would be no easy way to handle a multi language site with language SEO urls, it would be a dealbreaker.

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


#38 Oliver

Oliver

    Sr. Member

  • Members
  • PipPipPipPip
  • 133 posts
  • 25

  • LocationBasel, Switzerland

Posted 29 August 2011 - 09:11 AM

Thanks for the offer, Soma. Would help, if you tell about the functionalities you’d expect such a module to have. Maybe it gives me an hint for the one or other question I still have to get an answer for. ;)

The real complex thing still is to keep the page mapping (associating the different language's translations of a page with each other) consistent, while allowing pages to be moved, trashed or deleted and also to providing the possibility to have pages in a language tree that aren’t sync’ed with other languages.

I implemented a sync option to automatically sync a language with the defined default language. So creation and deletion of pages in default language’s page tree will be repoduced within the synced language’s page tree all the way.

Alternatively, you can deal with the translations manually by creating them from the language tab in the page edit form (as already said: by cloning as well as by creating an empty new page).

What would you guys suggest should happen, when a page is moved outside the language’s page tree or is trashed? On sync, I guess, the associated pages have to be deleted or–in case of being trashed–also be trashed.

When a mapped page is moved within the language tree, the associated translations will be moved to the parent within their particular page tree, that is mapped to the new parent of the current languages page tree. But if you can have pages without a translation in the other language trees, what should happen on a page being moved to such unmapped page as new parent? Should this parent be replicated to the other language trees as new parent for the as well moved translations? Or should it be forbidden to move to unmapped parents?

Maybe I just should reduce the functionalities to syncing the page trees 1:1 first.

Btw: What information on the current language is needed within the template? ISO-Code? LCID? Or what else? Let me know.

#39 ryan

ryan

    Hero Member

  • Administrators
  • 5,985 posts
  • 3386

  • LocationAtlanta, GA

Posted 29 August 2011 - 10:54 AM

While I've not built a multi-language site before, I kind of think that mapping sounds very convenient for creating pages.  But I wonder if you really need to do any more than that in your version 1.0? Seems like just that one feature (creating pages) would cover the biggest need that most have? Following that, people might even prefer to manage other aspects in the trees (I'm guessing). So some of the more advanced mapping stuff might be fun to save as options for a version 2.0?

The way I see it, on 80% of sites, clients will just be creating and editing pages. Whereas, the big moves, deletes and other big operations are something that more often falls in the court of the developers, and usually during a site's initial development. (just speaking in very general terms). So you might be able to cover the needs of 80% of people before adding the more complex mapping features?


#40 Oliver

Oliver

    Sr. Member

  • Members
  • PipPipPipPip
  • 133 posts
  • 25

  • LocationBasel, Switzerland

Posted 30 August 2011 - 10:49 AM

Ryan, just a GU thing: When the page has a template with noMove set, shouldn’t the "move" link in the page list be hidden? On my endless ways through the core files I believe I saw some code related to that. So far it doesn’t seem to work properly.




0 user(s) are reading this topic

0 members, 0 guests, 0 anonymous users