Skip to content

Commit

Permalink
Additional upgrades for support of multi-language page names. Now you…
Browse files Browse the repository at this point in the history
… can define them when adding a page. Previously you could only define them after the page existed.
  • Loading branch information
ryancramerdesign committed Mar 14, 2013
1 parent 3d691a1 commit 45c4f01
Show file tree
Hide file tree
Showing 13 changed files with 184 additions and 54 deletions.
3 changes: 3 additions & 0 deletions htaccess.txt
Expand Up @@ -5,10 +5,13 @@

# -----------------------------------------------------------------------------------------------
# Don't show directory indexes, but do follow symbolic links
# Note: some cloud hosting companies don't allow +FollowSymLinks
# Uncomment +SymLinksifOwnerMatch and comment +FollowSymLinks if you have troubles
# -----------------------------------------------------------------------------------------------

Options -Indexes
Options +FollowSymLinks
# Options +SymLinksifOwnerMatch

# -----------------------------------------------------------------------------------------------
# Let ProcessWire handle 404s
Expand Down
25 changes: 25 additions & 0 deletions wire/core/Page.php
Expand Up @@ -215,6 +215,12 @@ class Page extends WireData {
*/
protected $config = null;

/**
* When true, exceptions won't be thrown when values are set before templates
*
*/
protected $quietMode = false;

/**
* Page-specific settings which are either saved in pages table, or generated at runtime.
*
Expand Down Expand Up @@ -368,12 +374,31 @@ public function set($key, $value) {
self::$instanceIDs[$value] = $this->settings['id'];
break;
default:
if($this->quietMode && !$this->template) return parent::set($key, $value);

$this->setFieldValue($key, $value, $this->isLoaded);

}
return $this;
}

/**
* Set a value to a page without tracking changes and without exceptions
*
* Otherwise same as set()
*
* @param string $key
* @param mixed $value
* @return this
*
*/
public function setQuietly($key, $value) {
$this->quietMode = true;
return parent::setQuietly($key, $value);
$this->quietMode = false;
return $this;
}


/**
* Set the value of a field that is defined in the page's Fieldgroup
Expand Down
@@ -1,4 +1,11 @@
.InputfieldPageNameURL {
font-family: Monaco, Courier, Monaco, monospace;
font-family: Monaco, Courier, monospace;
}

.InputfieldPageName .LanguageSupport {
margin-bottom: 1em;
}
.InputfieldPageName .LanguageSupport .InputfieldPageNameURL {
margin-bottom: 0;
}

Expand Up @@ -40,7 +40,8 @@ var InputfieldPageName = {
},

updatePreview: function($t, value) {
$t.siblings(".InputfieldPageNameURL").children("strong").text((value.length > 0 ? value + '/' : ''))
var $previewPath = $('#' + $t.attr('id') + '_path');
$previewPath.children("strong").text((value.length > 0 ? value + '/' : ''))
}
};

Expand Down
Expand Up @@ -9,7 +9,7 @@ class InputfieldPageName extends InputfieldName implements ConfigurableModule {
public static function getModuleInfo() {
return array(
'title' => 'Page Name',
'version' => 102,
'version' => 103,
'summary' => 'Text input validated as a ProcessWire Page name field',
'permanent' => true,
);
Expand Down Expand Up @@ -99,13 +99,20 @@ class InputfieldPageName extends InputfieldName implements ConfigurableModule {
$this->label = $this->_('Name'); // Field label for 'Name'
$this->set('parentPage', null);
$this->set('sanitizeMethod', 'pageName');
$this->set('languageSupportLabel', '');
$this->description = $this->_("Any combination of letters (a-z), numbers (0-9), dashes or underscores (no spaces)."); // Field description describing what characters are allowed
}

public function ___render() {

$url = $this->parentPage ? $this->parentPage->path : '';
$out = "\n<p class='InputfieldPageNameURL'>$url<strong></strong></p>" . parent::___render();
$out = '';

if($this->languageSupportLabel) $out .= "<div class='LanguageSupport'>" .
"<label for='{$this->id}' class='LanguageSupportLabel detail'>$this->languageSupportLabel</label>";
$out .= "\n<p id='{$this->id}_path' class='InputfieldPageNameURL'>$url<strong></strong></p>";
$out .= parent::___render();
if($this->languageSupportLabel) $out .= "</div>";

// make the replacements part of the JS config
$replacements = empty($this->replacements) ? self::$defaultReplacements : $this->replacements;
Expand Down
Expand Up @@ -21,13 +21,17 @@ $(document).ready(function() {

var titleKeyup = function() {
if(!active) return;
var val = $titleField.val().substring(0, 128);
var val = $(this).val().substring(0, 128);
var id = $(this).attr('id').replace(/Inputfield_title_*/, 'Inputfield_name');
$nameField = $("#" + id);
$nameField.val(val).trigger('blur');
}

$titleField.keyup(titleKeyup);
// $titleField.keyup(titleKeyup);
$titleField.bind('keyup change', titleKeyup);

$nameField.focus(function() {
// $nameField.focus(function() {
$('.InputfieldPageName input').focus(function() {
// if they happen to change the name field on their own, then disable
if($(this).val().length) active = false;
});
Expand Down
Expand Up @@ -9,7 +9,7 @@ class InputfieldPageTitle extends InputfieldText {
return array(
'title' => __('Page Title', __FILE__), // Module Title
'summary' => __('Handles input of Page Title and auto-generation of Page Name (when name is blank)', __FILE__), // Module Summary
'version' => 100,
'version' => 101,
'permanent' => true,
);
}
Expand Down
9 changes: 6 additions & 3 deletions wire/modules/Inputfield/InputfieldTinyMCE/content.css
Expand Up @@ -10,17 +10,20 @@ pre {
font-family: "Courier New", Courier, monospace;
}

.align_left {
.align_left,
.align-left {
float: left;
margin: 0 15px 10px 0;
}

.align_right {
.align_right,
.align-right {
float: right;
margin: 0 0 10px 15px;
}

.align_center {
.align_center,
.align-center {
display: block;
margin-left: auto;
margin-right: auto;
Expand Down
2 changes: 2 additions & 0 deletions wire/modules/LanguageSupport/LanguageSupportFields.module
Expand Up @@ -363,13 +363,15 @@ class LanguageSupportFields extends WireData implements Module {
$values[$key] = $value->getLanguageValue($language->id);
}

/*
if(!strlen($values['data'])) foreach($values as $k => $v) {
// prevent the possibility of the default language having
// a blank value while some other language has a populated value
if(!strlen($v)) continue;
$values['data'] = $v;
break;
}
*/

$event->return = $values;
}
Expand Down
137 changes: 101 additions & 36 deletions wire/modules/LanguageSupport/LanguageSupportPageNames.module
Expand Up @@ -20,7 +20,7 @@ class LanguageSupportPageNames extends WireData implements Module {
static public function getModuleInfo() {
return array(
'title' => 'Languages Support - Page Names',
'version' => 2,
'version' => 3,
'summary' => 'Required to use multi-language page names. In development, NOT for production use.',
'author' => 'Ryan Cramer',
'autoload' => true,
Expand Down Expand Up @@ -61,17 +61,19 @@ class LanguageSupportPageNames extends WireData implements Module {
public function ready() {

$this->addHookAfter('Page::path', $this, 'hookPagePath');
$this->addHook('Page::localName', $this, 'hookPageLocalName');
$this->addHook('Page::localUrl', $this, 'hookPageLocalUrl');
$this->addHook('Page::localPath', $this, 'hookPageLocalPath');

// verify that page path doesn't have mixed languages where it shouldn't
$redirectURL = $this->verifyPath($this->requestPath);
if($redirectURL) return wire('session')->redirect($redirectURL);

$page = wire('page');

if($page->template == 'admin' && $page->process == 'ProcessPageEdit') {
if($page->template == 'admin' && ($page->process == 'ProcessPageEdit' || $page->process == 'ProcessPageAdd')) {
// when in admin, add inputs for each language's page name
$page->addHookBefore('ProcessPageEdit::execute', $this, 'hookProcessPageEditExecute');
$this->addHookBefore('InputfieldPageName::render', $this, 'hookInputfieldPageNameRenderBefore');
$this->addHookAfter('InputfieldPageName::render', $this, 'hookInputfieldPageNameRenderAfter');
$this->addHookAfter('InputfieldPageName::processInput', $this, 'hookInputfieldPageNameProcess');
}
Expand Down Expand Up @@ -232,55 +234,47 @@ class LanguageSupportPageNames extends WireData implements Module {
}
}

/**
* Ensure that PageName starts with default language, so that the presented URL is not confusing to user
*
*/
public function hookInputfieldPageNameRenderBefore(HookEvent $event) {
$user = wire('user');
$language = $user->language;
if($language->isDefault()) return;
$options = $event->options;
$options['savedLanguage'] = $language;
$event->options = $options;
$user->language = wire('languages')->get('default');
}

/**
* Hook into the page name render for when in ProcessPageEdit
*
* Adds additional inputs for each language
*
* @todo Just move this to the InputfieldPageName module rather than using hooks
*
*/
public function hookInputfieldPageNameRenderAfter(HookEvent $event) {

// restore language that was saved in the 'before' hook
if(isset($event->options['savedLanguage'])) wire('user')->language = $event->options['savedLanguage'];

$inputfield = $event->object;
$page = $this->process->getPage();
$name = $inputfield->attr('name');
$out = $event->return;
$language = wire('languages')->get('default');
if($inputfield->languageSupportLabel) return; // prevent recursion

$user = wire('user');
$page = $this->process == 'ProcessPageEdit' ? $this->process->getPage() : new NullPage();
$savedLanguage = $user->language;
$savedName = $inputfield->attr('name');
$savedID = $inputfield->attr('id');
$out = '';

// add a label for default language
$label = $language->get("title|name");
$out = str_replace("<input", "<label for='Inputfield_name' class='LanguageSupportLabel detail'>$label</label><input", $out);
$language = wire('languages')->get('default');
$user->language = $language;
$inputfield->languageSupportLabel = $language->get('title|name');
$out .= $inputfield->render();

// add labels and inputs for other languages
foreach(wire('languages') as $language) {
if($language->isDefault()) continue;
$value = wire('sanitizer')->entities($page->get("name$language"));
$id = "$name$language";
$user->language = $language;
$value = $page->get("name$language");
$id = "$savedID$language";
$name = "$savedName$language";
$label = $language->get('title|name');
$out .= "<div class='LanguageSupport'>" .
"<label for='$id' class='LanguageSupportLabel detail'>$label</label>" .
"<input class='InputfieldMaxWidth' maxlength='128' type='text' id='$id' name='$id' value='$value' />" .
"</div>";
$inputfield->languageSupportLabel = $label;
$inputfield->attr('id', $id);
$inputfield->attr('name', $name);
$inputfield->attr('value', $value);
$out .= $inputfield->render();
}

// restore language that was saved in the 'before' hook
$user->language = $savedLanguage;

$event->return = $out;
}

Expand All @@ -300,7 +294,9 @@ class LanguageSupportPageNames extends WireData implements Module {
if($language->isDefault()) continue;
$name = $inputfield->attr('name') . $language;
$value = wire('sanitizer')->pageName($input->$name);
$page->set("name$language", $value);
$key = "name$language";
if($page->id) $page->set($key, $value);
else $page->setQuietly($key, $value); // avoid non-template exception when new page
}
}

Expand All @@ -315,6 +311,75 @@ class LanguageSupportPageNames extends WireData implements Module {
$event->return = $this->getPagePath($page, $language);
}

/**
* Add a Page::localName function with optional $language as argument
*
* @param Language|string|int Optional language
* @return string Localized language name or blank if not set
*
*/
public function hookPageLocalName(HookEvent $event) {
$page = $event->object;
$language = $this->getLanguage($event->arguments(0));
$nameField = $language->isDefault() ? "name" : "name$language";
$value = $page->get($nameField);
if(is_null($value)) $value = '';
$event->return = $value;
}

/**
* Add a Page::localPath function with optional $language as argument
*
* @param Language|string|int Optional language
* @return string Localized language name or blank if not set
*
*/
public function hookPageLocalPath(HookEvent $event) {
$page = $event->object;
$language = $this->getLanguage($event->arguments(0));
$event->return = $this->getPagePath($page, $language);
}

/**
* Add a Page::localUrl function with optional $language as argument
*
* @param Language|string|int Optional language
* @return string Localized language name or blank if not set
*
*/
public function hookPageLocalUrl(HookEvent $event) {
$page = $event->object;
$language = $this->getLanguage($event->arguments(0));
$event->return = wire('config')->urls->root . ltrim($this->getPagePath($page, $language), '/');
}

/**
* Given an object, integer or string, return the Language object instance
*
* @param int|string|Language
* @return Language
*
*/
protected function getLanguage($language) {

if(is_object($language)) {
if($language instanceof Language) return $language;
$language = '';
}

if(is_string($language) || is_int($language)) {
if(ctype_digit("$language")) $language = (int) $language;
else $language = wire('sanitizer')->pageName($language);
$language = wire("languages")->get($language);
}

if(!$language || !$language->id || !$language instanceof Language) {
$language = wire('languages')->get('default');
}

return $language;
}

/**
* Update pages table for new column when a language is added
*
Expand Down

0 comments on commit 45c4f01

Please sign in to comment.