• Content count

  • Joined

  • Last visited

Community Reputation

13 Good

About ZGD

  • Rank
    Full Member

Recent Profile Visitors

1,107 profile views
  1. Thanks @abdus, I should have mentioned I'm searching pages not templates here. So I don't think using $result->sort('label') on the results PageArray would work for this situation?
  2. I usually stick to a convention for my template names so each is a single word. However the template label may be more descriptive, for example I have a template called post with the label Blog Post. I am implementing a search function and want to group results into types, so all Blog Posts appear together. Using the selector sort=template works for grouping in order by template name, but I would like the groups to appear alphabetically by label. TL;DR: Is there a way I can use selectors to sort by template label, something like sort=template.label?
  3. Replying to myself here in case anyone else was looking for a similar migration. This seems to work for setting up repeater fields via a single migration file. Not sure if the method I've used for generating the repeater template is sensible but it will do for now: <?php class Migration_2017_10_01_10_10_10 extends FieldMigration { public static $description = "<b>+FIELD</b> Custom Matrix (custom_matrix)"; protected function getFieldName(){ return 'custom_matrix'; } protected function getFieldType(){ return 'FieldtypeRepeaterMatrix'; } protected function getMatrixTemplateName() { return FieldtypeRepeater::templateNamePrefix . $this->getFieldName(); } protected function getMatrixTypes() { return [ [ 'name' => 'foo', 'label' => 'Foo', 'head' => '{matrix_label}', 'fields' => [ 'title' => [ 'label' => 'Title (Optional)', 'required' => 0, ], 'markdown', ], ], [ 'name' => 'bar', 'label' => 'Bar', 'head' => '{matrix_label}', 'fields' => [ 'title' => [ 'label' => 'Title (Required)', 'required' => 0, ], 'image', ], ], ]; } protected function addFieldToRepeaterList($matrixField, $field) { $repeaterFields = $matrixField->repeaterFields; if (!is_array($repeaterFields)) $repeaterFields = []; if (!in_array($field->id, $repeaterFields)) { $repeaterFields[] = $field->id; } $matrixField->repeaterFields = $repeaterFields; } protected function addMatrixType($matrixField, $index, $config) { $templateName = $this->getMatrixTemplateName(); $prefix = 'matrix' . ($index + 1) . '_'; $matrixField[$prefix.'name'] = $config['name']; $matrixField[$prefix.'label'] = $config['label']; $matrixField[$prefix.'head'] = $config['head']; $matrixField[$prefix.'sort'] = $index; $fields = []; foreach ($config['fields'] as $key => $property) { $field = (!is_numeric($key)) ? $this->fields->get($key) : $this->fields->get($property); $fields[] = $field->id; $this->addFieldToRepeaterList($matrixField, $field); $this->insertIntoTemplate($templateName, $field); if (!is_numeric($key)) { $this->editInTemplateContext($templateName, $field, function(Field $f) use ($property) { foreach ($property as $prop => $val) { $f->$prop = $val; } }); } } $matrixField[$prefix.'fields'] = $fields; } protected function fieldSetup(Field $f){ $f->label = 'Custom Matrix'; $f->tags = 'matrix'; $f->required = 1; $f->icon = 'fa-bars'; // Do this to generate a template file for the repeater // @TODO Find a better way to do this $fieldtype = $f->type; $numOldReady = $fieldtype->countOldReadyPages($f); // Setup matrix stuff $matrixTypes = $this->getMatrixTypes(); foreach ($matrixTypes as $i => $matrixData) { $this->addMatrixType($f, $i, $matrixData); } // Insert into template $template = 'my-template'; $this->insertIntoTemplate($template, $f); } }
  4. I'm making extensive use of migrations for a project and had a question regarding writing migrations for matrix fields. When creating a new FieldtypeRepeaterMatrix field, I usually need to create two migrations. The first actually creates the blank matrix field, and then the second populates it with types/fields. This is because the template associated with the matrix is not actually created when the initial save() method is called when creating the field. Does anyone know if it is possible to force PW to create this template via the API? Wanted to also say how useful this module is. Would love to see migrations built into the core one day as it's totally changed how we use PW. I've been thinking about submitting a pull request with some new migration classes I've been building, would be great to have some reusable templates for complex situations like this. EDIT: I just realized I could probably just create the template as I would any other template on the initial field setup. But if there is an alternative save() method that would let PW handle the template creation that would still seem easier!
  5. Just noticed that FieldtypeFieldsetPage doesn't seem to be compatible with RepeaterMatrix, has anyone tried using either of the new FieldtypeFieldset modules within a RepeaterMatrix yet? EDIT I'd created the FieldtypeFieldsetPage fields using the API in a migration file, seems that re-saving them via the admin GUI solved the problem.
  6. I'm currently working on quite a large project and want to take advantage of some of the new features that have been introduced to the dev branch over the last couple of weeks, namely FieldsetPage and FieldsetGroup. We're using a large number of matrix repeaters with slight variations, and taking advantage of these new features would help us a great deal. The project is about a month away from launch, and unfortunately I know that the client will take issue with using the development build when we move to production. So my question is, at what point is the development branch generally considered mature enough to merge with the master? Is there a release schedule as part of the roadmap or is this more up to Ryan's discretion? Totally understand if it is the latter, just wanted to get some more information so we can weigh up our options over the next few weeks.
  7. Not sure if this has been discussed before, but is it possible to use a different method for storing image variations with PW? For example, would it be possible that instead of storing variations like this: /image.jpg /image.2000x0.jpg /image.0x900.jpg Variations were instead stored like this: /image.jpg /2000x0/image.jpg /0x900/image.jpg Normally the default method is fine, but we are working on a project where the image filenames may need to remain unchanged, but variations still produced. If anyone has any information about this it would be much appreciated! EDIT: Having looked at the size method in the Pageimage class, it looks like this isn't something that could be changed easily.
  8. Figured out a slight workaround today using various hooks, it's probably not ideal but seems to work OK. It adds two new fields to every template, the first gives the option to hide view links from pages using that template, the second adds the ability to edit the URL. The following is in my ready.php file: <?php // Helper function to rewrite URLs with dynamic properties // For example {name} will be replaced with the page name property function rewriteURL($page, $url) { $appUrl = wire('pages')->get(1)->link; // Base URL set in home template $regex = '/(?:{([^}]+?)})|(.+?(?={[^}]+?}|$))/'; $out = ''; preg_match_all($regex, $url, $matches, PREG_SET_ORDER, 0); foreach($matches as $match) { if ($match[1]) $out .= $page[$match[1]]; else if ($match[2]) $out .= $match[2]; } return $appUrl.$out; // e.g. http://www.example.com/some-path } // Rewrite page list view URLs $wire->addHook('ProcessPageListActions::getActions', function($event) { $page = $event->arguments(0); $template = $page->template; if ($template->hideViewLink) { $actions = $event->return; unset($actions['view']); $event->return = $actions; } elseif ($template->rewriteViewURL) { $actions = $event->return; if (array_key_exists('view', $actions)) { $actions['view'] = array( 'cn' => $actions['view']['cn'], 'name' => $actions['view']['name'], 'url' => rewriteURL($page, $template->rewriteViewURL), ); $event->return = $actions; } } }); // Rewrite page edit view URLs $wire->addHookBefore('ProcessPageEdit::buildFormView', function($event) { $page = $event->object->getPage(); $template = $page->template; if ($template->rewriteViewURL) { $updatedURL = rewriteURL($page, $template->rewriteViewURL); $event->arguments = [ $updatedURL ]; } }); // Edits the edit page 'View' tab if required // Removes dropdown items $wire->addHookAfter('ProcessPageEdit::getTabs', function($event) { $template = $event->object->getPage()->template; $tabs = $event->return; $editTab = array_key_exists('ProcessPageEditView', $tabs); if ($editTab) { if ($template->hideViewLink) { $tabs['ProcessPageEditView'] = false; } else { $tabs['ProcessPageEditView'] = preg_replace('/<span.*(?=<\/a>)/', '', $tabs['ProcessPageEditView']); } $event->return = $tabs; } }); // Adds extra fields to templates $wire->addHookAfter("ProcessTemplate::buildEditForm", function(HookEvent $event) { $template = $event->arguments[0]; $form = $event->return; $f = $this->modules->get('InputfieldText'); $f->attr('id+name', 'rewriteViewURL'); $f->value = $template->rewriteViewURL; $f->label = $this->_('Rewrite View URL'); $f->description = $this->_('You can access page properties by wrapping them in curly braces.'); $f->notes = $this->_('e.g. {name} will be replaced with the page name'); $form->insertAfter($f, $form->templateLabel); $f = $this->modules->get('InputfieldRadios'); $f->attr('id+name', 'hideViewLink'); $f->addOption(1, 'Yes'); $f->addOption(NULL, 'No'); $f->value = $template->hideViewLink; $f->label = $this->_('Hide View Link'); $f->description = $this->_('If this template should hide view links.'); $form->insertAfter($f, $form->templateLabel); $event->return = $form; }); // Save the extra template fields $wire->addHookBefore("ProcessTemplate::executeSave", function() { $template = $this->templates->get($this->input->post->id); $template->set('hideViewLink', $this->input->post->hideViewLink); $template->set('rewriteViewURL', $this->input->post->rewriteViewURL); }); Also need some custom CSS rules to hide the view link in the dropdown Save button. .pw-button-dropdown .ui-menu-item { display: block } .pw-button-dropdown .ui-menu-item a[data-pw-dropdown-value="view"] { display: none; } Would still be interested in a cleaner solution though!
  9. Bumping this to see if anyone else found a viable solution. We need to be able to redirect users to an app served by a different server as the PW instance just outputs JSON data. Seems like there is no hook available to simply alter the view URL. Would be super useful.
  10. Basically yes, I'm trying to access an unpublished page via an asynchronous request from another server, so it's effectively the same as trying to access it from a browser. My JS app running on the node.js server makes an AJAX request to the ProcessWire specific URL (an unpublished page), but as that session is not authenticated, PW responds with 404.
  11. This is similar to what I'm looking for but I don't think it would work in this case. I would essentially do... $page = $pages->get($page); ... in my template. But if I understand correctly ProcessWire will redirect to the 404 page so I can't actually run this code in my template. I guess I would need to somehow hook into the page render method and prevent this 404 redirect?
  12. TL;DR: Is it possible to render unpublished pages even if the request comes from an unauthenticated user? I'm currently building a website that consists of a two parts: A front-end 'app' built using Vue.js served by a node.js/express server (necessary for server-side rendering). A ProcessWire back-end that serves up a JSON API, this is running on an Apache server. I've added hooks so the "View" page links/tabs in ProcessWire direct to the front-end app's URLs, rather than just showing a page of the raw JSON data exposed by ProcessWire. So for example if my PW page lives at https://api.example.com/pages/foo, clicking View directs to a URL pointing to the front-end app like https://example.com/foo. The problem I now have is that I need to be able to view unpublished pages. As sessions are not shared between ProcessWire and the front-end as they run on different servers, this means if I redirect to the app from PW as above, the app then makes a request to PW and obviously returns a 404. PW rightly sees the request from the app as coming from an unauthenticated user. My proposed solution is that I edit the page preview hook to include a query in the URL like so https://example.com/foo?session=a1b2c3d4e5. The front-end server then passes this query string to any requests it makes to the PW API (e.g. https://api.example.com/pages/foo?session=a1b2c3d4e5), which in turn checks it against a list of valid session variables (even better if I could somehow check this against a session_id of logged in users). If the query was valid, then PW should render the page even if it is unpublished. Is this possible?
  13. Managed to resolve this, code below for any that's interested. My translations class: <?php namespace Translations; class Translation { private function __construct() {} protected static function translations() { return []; } private static function initialize() { $languages = \ProcessWire\wire('languages'); $user = \ProcessWire\wire('user'); $translated = []; foreach($languages as $lang) { $user->language = $lang; foreach(static::translations() as $k => $t) { $translated[$k][$lang->language_code] = $t; } } return $translated; } public static function translate() { return static::initialize(); } } include_once __DIR__ . '/greetings.php'; include_once __DIR__ . '/questions.php'; Now I can create as many different translation files as I like, for example greetings.php <?php namespace Translations; use function ProcessWire\__ as __; class Greetings extends Translation { protected static function translations() { return [ 'hello' => __('Hello'), // A casual greeting message 'goodbye' => __('GoodBye'), // A casual departing message ]; } } and questions.php <?php namespace Translations; use function ProcessWire\__ as __; class Questions extends Translation { protected static function translations() { return [ 'howareyou' => __('How are You?'), 'howmuch' => __('How Much?'), ]; } } So in my template I can now write: <?php use Translations\Greetings; use Translations\Questions; $greetings = Greetings::translate(); $questions = Questions::translate(); // ... output here
  14. I'm using PW to build an API which returns data in multiple languages at once. For example I might have the words "Hello" and "Goodbye" that need to be translated into several languages. The API (which returns data as JSON) needs to return something like the following... { "hello": { "de": "Hallo", "en": "Hello", "fr": "Bonjour", "it": "Ciao", "tc": "你好" }, "goodbye": { "de": "Auf Wiedersehen", "en": "Goodbye", "fr": "Au Revoir", "it": "Addio", "tc": "再見" } } Ideally I'd like to be able to use PW's template string translating feature, as there will be potentially hundreds of different translations and creating many fields seems the wrong way to go. Each translation will also need to have a note or description alongside it. I was originally thinking something like the example below may be possible, but it seems unlikely. <?php function _t($translation) { // logic to return all translations here... return $translation; } $translations = [ 'hello' => _t(__('Hello')), // A casual greeting message 'goodbye' => _t(__('Goodbye')) // A casual departing message ]; echo json_encode($translations); Can anyone suggest a good method for returning all translations of a translatable string? Is it even possible to access multiple translations of static strings within a template? Alternatively, if anyone has solved a similar problem before, I'm open to suggestions. Thanks!
  15. I'm also seeing this, did you find a workaround? EDIT: Prepended the ProcessWire namespace to the wirePopulateStringTags method which seems to have fixed the problem for now.