Jump to content

Leaderboard

Popular Content

Showing content with the highest reputation on 11/25/2016 in all areas

  1. Hi everyone, Here's a new module that lets you control whether multi-language support is enabled at the page / branch level, rather than only per template. http://modules.processwire.com/modules/restrict-multi-language-branch/ https://github.com/adrianbj/RestrictMultiLanguageBranch This is ideal for a site with repeated branches using the same templates where only some need to be multi-language. I think it is confusing to provide multiple language inputs for fields when they are not required - it just bloats the admin interface for site editors. I am hoping to expand this module to allow selection of which languages are supported per page/branch, but I am waiting on @ryan's response to this request: https://github.com/processwire/processwire-requests/issues/54 - to me this would be even more powerful if you have a situation where certain branches need some languages and other branches need different languages. The module config settings shows a summary of the restrictions you have implemented, eg: This shows that we have started with the home page which disables multi-language on itself and all its children/grandchildren (because "This Page Only" is "False". Next we have the /report-cards/ page multi-language enabled, but no inheritance (because "This Page Only" is "True"). The only branch below this to have multi-language enabled is Guanabara Bay and all it's children etc will also be enabled. All other report card branches will be disabled because they will inherit directly from the config settings default status. The Settings tab for each page gives you three options: Inherit, Enabled, Disabled. The screenshots give you an idea of how the Inherit option works and the information it provides based on the status it is inheriting and from where. My goal for this site was to just enable multi-language support for the Guanabara Bay report card branch of the tree, as well as the home page and the /report-cards/ parent. All other branches have multi-language support disabled which makes content entry much cleaner. Hope you guys find a good use for it and I'll be sure to update with the ability to define which languages are available on which pages/branches if Ryan comes up with a core solution for changing the returned $languages. Please let me know if you have any problems / suggestions.
    9 points
  2. Hi all, the renowned German magazine for computer tech, c't, in its most recent issue #25/2016 is presenting several "WorldPress alternatives", and ProcessWire is among them. (My translation.)
    9 points
  3. What's in the new master version 3.0.42, and covering the release of the new ProDevTools modules: ProfilerPro and API Explorer. Plus, a look at some of what we'll be focusing on in 2017! https://processwire.com/blog/posts/processwire-3.0.42-is-out-and-prodevtools-is-released/
    6 points
  4. Thank you for your thoughtful replies. I will surely explore the suggestions you've made. I've found that working with a framework is a good thing, it frees you from sweating over small/routine stuff. But that's me. In any case, I'm loving Processwire although it has been/and is going to be a steep learning curve. I'm a PHP novice. Yet, I can see the freedom and flexibility it affords. Thanks again.
    4 points
  5. Hello and welcome, Well, ProcessWire is perfectly suitable for such a site you described (actually for any website you might happen to build...), but it is not the sort of CMS you can just click together like a WordPress site. Sure, creating your database entities (Pages and Fields) is a piece of cake even for beginners, but you will need to spend time on actually writing code to Wire things together. Learning ProcessWire based development is a sort of linear learning "curve" (linear curve? huh... ) but takes time and patience nonetheless. However, it is also a lot of fun, especially when discussing things here in the Forums
    4 points
  6. Glad to know your issues have been solved, Blending Bootstrap with CMS can become cumbersome and time consuming. At least you probably have learned something about PW. Here's list UI framework, "a lighter version of bootstrap". - Kube - Pure - Skeleton - Concise and many more...You can even build your own framework yourself.
    3 points
  7. Very useful! I use it in combination with opening hours. For this reason I have created a repeater with 7 repeats (one for each day). In this case it prevents the customer from deleting or draging a weekday from the repeater list.
    3 points
  8. Are you sure it's the save that's being slow? I'd first suggest you to measure the timings in that code at various places before we try to improve in the blind. An example on how to measure the timings can be found here:
    3 points
  9. I recommend: http://materializecss.com/ I'm the type who likes css "frameworks”. It is because I do not spend hours working with css, neither do I have the time to fight browser incompatibilities. Those with a decent knowledge of css and supporting technologies (like preprocessors, task runners, etc..) generally do not like Bootstrap like frameworks, especially if they do not work in teams. They tend to like low level “frameworks” (or just simple css grid packages) that give them more freedom, or they do not use any of these at all, or perhaps just their own. However, I think it can be quite useful to learn one of these, because if you are not developing frontends on a daily bases, then they can speed up a lot. Sure, there are situations when one finds the chosen framework to be an obstacle when dealing with a particular problem, but still, they can be really useful. I spent a considerable amount of time to find an easy to use versus versatile and feature rich framework and I think Materialize CSS is the way to go for "experienced beginners" like me. If you are also in this “category”, then I highly recommend it. One can even easily customize it by vanilla css overwrites if you do not want to go the preprocessor way (just yet). Out of the box solutions like Bootstrap, UIKit and others are most useful for teams working on the same project, as these packages define the standard to which everyone can adhere to, so that is why UIKit for the ProcessWire admin would be a good choice, for example. However, solo freelancers working alone have more freedom regarding the path they choose to take...
    3 points
  10. It's Bootstrap-specific in that Bootstrap requires you to add (among many, many other things) an unnecessary data-toggle="dropdown" attribute to the <a> element. It's unnecessary as that element can already be targeted as a direct child of an <li> element with "has_children" class (or whatever other class you might want to use for dropdown menu purposes). That is what @ottogal referred to above: MarkupSimpleNavigation does not equal ProcessWire - it's a third-party module for ProcessWire. PW allows you to generate your markup any way you choose. Personally I find MSN to be a very useful module but it's not compulsory to use it. Some people create their own menu markup from scratch using just the API and basic PHP functions like foreach(). And there are other modules for generating menu markup such as Menu Builder (haven't used it myself). I'm not a good one to answer this as I don't like CSS frameworks - I find them a hindrance rather than a help. I prefer to create my stylesheets for each project from scratch using SCSS. That way there's no bloat and I can build exactly to my design mockups without needing to conform to any framework expectations. I understand UIKit is popular with others though.
    3 points
  11. LimitRepeater Allows restrictions and limits to be placed on Repeater fields. For any Repeater field you can limit the number of items that may be added and also prevent the use of drag-sorting, toggling of published state, and the trashing of items. There is also an option to hide the clone button when the limit is reached. Usage Install the LimitRepeater module. Since v0.2.0 the module settings are configured at Setup > Fields > [your Repeater field]. The settings are contained within the "Restrictions" fieldset on the "Details" tab. Please note that the restrictions limits are applied in Page Edit with CSS/JS so should not be considered tamper-proof. Setting restrictions via a hook Besides setting restrictions in the field settings, you can also apply or modify restrictions by hooking LimitRepeater::checkRestrictions. This allows for more focused restrictions, for example, applying restrictions depending on the template of the page being edited or depending on the role of the user. The checkRestrictions() method receives the following arguments: $field This Repeater field $inputfield This Repeater inputfield $page The page that is open in ProcessPageEdit The method returns an array of restrictions for the Repeater field. An example of a returned array: Example hook Prevent non-superusers from trashing any items in "my_repeater_field": $wire->addHookAfter('LimitRepeater::checkRestrictions', function(HookEvent $event) { $field = $event->arguments('field'); $restrictions = $event->return; if($field->name === 'my_repeater_field' && !$this->user->isSuperuser()) { $restrictions['notrash'] = true; } $event->return = $restrictions; }); Upgrading from < v0.2.0 There are two major changes to be aware of when upgrading from earlier versions of the module. The settings are no longer defined on the module config page, but rather in the field settings of each Repeater field: Setup > Fields > [your Repeater field]. If you visit the module config page you'll find shortcuts to the settings for each Repeater field. In earlier versions you could apply restrictions to a particular role. This is still possible but is now handled by hooking LimitRepeater::checkRestrictions as this is a more flexible and powerful approach. If you were applying restrictions to a particular role or roles you'll need to add hook code to achieve the same effect after you upgrade the module. See the hook information above in this readme. https://github.com/Toutouwai/LimitRepeater http://modules.processwire.com/modules/limit-repeater/
    2 points
  12. Okay, finally I figured out that I was fiddling with the wrong settings, so that is why it "did not work". Thanks to @Juergen who was kind enough to take a look at it! Edit: Black Friday, eh?
    2 points
  13. v105 is uploaded (changelog).
    2 points
  14. Hello @szabesz here are my settings for the repeater field: best regards
    2 points
  15. I just took advantage of my design skills
    2 points
  16. Hi lenoir, There is a version compatible with 3.0, it is in devns branch, it is separated because i didn't find a way to have one codebase compatible with PW 2.x and 3.x (I can tell you more if you want). What happend with your pdf files in 3.0? Why did you have to restore the database? Should I fix something? Cheers, Richard
    2 points
  17. TemplateEngineFactory The main idea of this module is to support the developer separating logic from markup. This is achieved by turning ProcessWire templates into controllers which interact over a new API variable to template engines like Smarty or Twig. The TemplateEngineFactory ships with a default engine "ProcessWire" that uses the internal TemplateFile class to render the templates (some of you may already be familiar with this concept). However, the module is constructed in a way that any template engine can be used, implemented as separate modules. Please check out the readme on GitHub for more details how it works: https://github.com/wanze/TemplateEngineFactory ... or in the modules directory: http://modules.processwire.com/modules/template-engine-factory/ Implementation of Smarty: https://github.com/wanze/TemplateEngineSmarty Implementation of Twig: https://github.com/wanze/TemplateEngineTwig Implementation of Jade (by dreerr, thanks!): https://github.com/dreerr/TemplateEngineJade How does it work? A controller (aka ProcessWire template) can have an associated template file which contains the markup to render. The folder where those templates are stored is configurable for each installed engine. If the Factory finds a template file with the same name as the controller, an instance to access the template is provided with a new API variable (called "view" by default). Over this API variable, you can set the dynamic variables that should be rendered. Hopefully the following example makes things clearer: // In controller file: /site/templates/home.php if ($input->post->form) { // Do some processing, send mail, save data... $session->redirect('./'); } // Pass variable to the template $view->set('foo', 'bar'); $view->set('show_nav', true); $view->set('nav_pages', $pages->get('/')->children()); As you can see, there is no markup echoed out. The corresponding template file is responsible for this task: // In template file: /site/templates/view/home.php <h1><?= $page->title ?></h1> <p>Foo: <?= $foo ?></p> <?php if ($show_nav): ?> <ul> <?php foreach ($nav_pages as $p): ?> <li><a href="<?= $p->url ?>"><?= $p->title ?></a></li> <?php endforeach; ?> </ul> <?php endif; ?> In the example above, "ProcessWire" is used as engine. If Smarty is the active template engine, the corresponding template file could look like this: // In template file: /site/templates/smarty/home.tpl <h1>{$page->title}</h1> <p>Foo: {$foo}</p> {if $show_nav} <ul> {foreach $nav_pages as $p} <li><a href="{$p->url}">{$p->title}</a></li> {/foreach} </ul> {/if} Note that the API variable acts as a gateway which connects you to the activated template engine. We can switch the engine behind without changing the controller logic! (I know that this is probably not a very common need, but it's a cool thing anyway ) For further information, please check out the readmes on GitHub. Please ask questions if anything makes no sense - sometimes it's hard to get my explanations Cheers
    1 point
  18. Hello, I have written a small module which adds UIKit Framework markup to images added with CK-Editor Here are some screenshots: 1) Default view 2) Hover over image shows an overlay and a tooltip info 3) Open in UIKit lightbox How does it work? The module uses Simple HTML DOM Parser for all the manipulations, so this file is also included in the module folder, but it doesnt add any UIKit CSS or Javascript. So you have to load the UIKit framework by yourself. What manipulations take place? Adds UIKit CSS classes to various tags (figure, anchor, image) Adds "no-align" class to tags (figure, image anchor) without alignment, so it could be also styled with CSS Adds an extra surrounding span container to images with no alignment and no figure tag to force block element behaviour Adds UIKit lightbox attribute to linked images Adds image alt attribute as title attribute for linked images on anchor tags / creates fallback if no alt attribute is present Adds UIKit tooltip attribute to all anchors of linked images (tooltip text can be translated in the module translation file) Adds UIKit overlay containers (additional markup) to linked images for overlay effect on hover Example of Markup manipulation: BEFORE <figure class="align_left hidpi"> <a href="/site/assets/files/5556/bora_bora_3.jpg"> <img alt="Rochen" src="/site/assets/files/5556/bora_bora_3.150x0-is-hidpi.jpg" width="150"> </a> <figcaption>Rochen</figcaption> </figure> AFTER <figure class="uk-align-left hidpi"> <a href="/site/assets/files/5556/bora_bora_3.jpg" title="Opens the image in a lightbox view" data-uk-tooltip="" data-uk-lightbox="{group:'my-group'}" class="uk-thumbnail"> <div class="uk-overlay uk-overlay-hover"> <img alt="Rochen" src="/site/assets/files/5556/bora_bora_3.150x0-is-hidpi.jpg" width="150"> <div class="uk-overlay-panel uk-overlay-background uk-overlay-icon"></div> </div> </a> <figcaption class="uk-thumbnail-caption">Rochen</figcaption> </figure> How to install the module? Download the module: TextformatterUIKitImages.zip Github: Download from Github Extract the folder and upload the unzipped folder (including both files - simple_html_dom.php, TextformatterUIKitImages.module) into the site/modules folder. Please let both files into the TextformatterUIKitImages folder - TextformatterUIKitImages (folder) -|- TextformatterUIKitImages.modules (file) -|- simple_html_dom.php (file) Install the module as all other modules and add it f.e. to the body field. Hint: I dont maintain the module, so its only here for people who are using UIKit framework too and want to manipulate the image markup of images added with CKEditor. It can also be used as a starting point for your own image markup manipulation module. Best regards Jürgen
    1 point
  19. Thank you @adrian for so useful module. It would be super great to have opportunity to disable default language for some branches/pages
    1 point
  20. Here are my code snippets for that <?php echo $all_products->renderPager(array( "nextItemLabel" => __("След."), "previousItemLabel" => __("Пред."), "firstNumberItemClass" => "pagination__item--first-num", "lastNumberItemClass" => "pagination__item--last-num", "previousItemClass" => "pagination__item--prev", "nextItemClass" => "pagination__item--next", "firstItemClass" => "pagination__item--first", "lastItemClass" => "pagination__item--last", "currentItemClass" => "pagination__item--active", "listMarkup" => "<div class='pagination'><ul class='pagination__list'>{out}</ul></div>", "itemMarkup" => "<li class='pagination__item {class}'>{out}</li>", "linkMarkup" => "<a class='pagination__item-link' href='{url}'><span>{out}</span></a>" )); ?> and SASS styles ( mixins from foundation ) $module: 'pagination'; .#{$module} { text-align: center; background: $white; border-radius: $global-radius; padding: rem-calc(5); box-shadow: rem-calc(0 1 4 0) rgba(0, 0, 0, 0.1); &__list { @include pagination-container; display: inline-block; margin-bottom: 0; } &__item { display: inline-block; padding: rem-calc(5) rem-calc(10); &--active { a { text-decoration: underline; } } a { @include button-base(); @include button(false, $brand-darker, darken($brand-darker, 5), $white, solid); border-radius: $global-radius - 2; margin-bottom: 0; padding: rem-calc(3 7); } } }
    1 point
  21. You're right it's not working but because the selector should be only '.topnav', these menus are not children of '#topnav'. It seems that jQuery UI doesn't use the border for the calculation. The screencap shows the border increased to 10px:
    1 point
  22. Congratulations on release Ryan! Yesterday I spent some time to browse the API Explorer on demo site and I have to agree that ProcessWire documentation is top notch! Huge improvements over what we used to have and way ahead of most projects.
    1 point
  23. That's odd - I don't see how that can happen. An images field will return an array of images (a 'Pageimages' WireArray) if you have chosen that in the field settings. This approach is okay, but if it were my project I think I'd find that a bit awkward. Hard for editors to make a clear connection between which text field goes with which image. And 10 identical fields seems like something to avoid where possible (maintenance would be a pain for a start). Suggestions... If the text field only needs a normal text input then use the Description field for each image. If the text field needs to be a textarea then try the Image Extra module. If the text field needs to be a CKEditor field then consider creating a Repeater containing an image field limited to 1 image and the CKEditor field. Then you can fix the Repeater to 10 items using Limit Repeater (shameless self-promotion )
    1 point
  24. The short answer is I don't know where the internals of the DB are documented. The long answer? Read on. I found that Repeaters were a bit odd compared with other compound (indirect, recursive, whatever you call them) fields. Most "data" fields in ProcessWire are JSON but repeaters are just a list of page numbers. If you add '[' + data + ']' you can JSON decode it as an array. In JavaScript I ended up doing this: data.split(',').map(text => parseInt(text)); for the same result. It's definitely overkill for what you're doing, but I wrote a deep ProcessWire-specific DB comparison program (in JavaScript) that handles standard field types other than Matrix (because we don't use that). It also handles the Padloper field types that we have implemented so far. If you're comfortable with JavaScript it is available here: https://github.com/bmacnaughton/pw-compare. My goal, when I'm not working on more immediate concerns, is to start outputting LostKobrakai's Migrations for each of the noted differences. The module you'd want to look at is `comparison/pagefields.js`
    1 point
  25. Thanks for the warm welcome! Also, thanks for your comments CLSOURCE and SZABESZ. With regards to the case study, that was one of the reasons that I investigated PW further, particularly the comparison table towards the end of the case study - thanks for this. SZABESZ, thanks for letting me know about the coding side; I'll have more of a look into what is required. I guess over the 6 years or so with WP, I did some basic CSS, html, so it does not faze me to get into the coding side a little. But I'll confess I don't know much! But happy to learn. So, I might go back to the case study (CLSOURCE) and focus a bit more on what is required for the programming side - thanks again.
    1 point
  26. Module updated! There were some cases where the module has produced an error on boolean. This happens if the field where you have added the Textformatter is on the page but it is still empty. Then the manipulation functions fail. I have corrected this with a simple if condition check to prevent this error.
    1 point
  27. Today only: ALL SitePoint Books and Courses$5,000 Value, NOW JUST $99 https://learnable.com/99-deal-unlimited
    1 point
  28. This could be a starting point. I used a customized version for a private car sharing site.
    1 point
  29. @Krlos @Karl_T I fixed the single/ multilanguage issue. Sorry for the delay ... Please update to 2.0.1 and try out.
    1 point
  30. I added 1px border to the submenu so it will always have at least 0.5px overlap. I'll add this to the next version of AOS. #topnav .topnav { border-right: 1px solid transparent; }
    1 point
  31. Not sure it has been mentioned before: If changing the email-name, the saved password is silently removed. Is this intended? I would like a warning here, if there is a case where the saved password gets reset for some reason and needs to be entered again.
    1 point
  32. Hi welcome to PW!. If you need a case study look at this hope it helps
    1 point
  33. @Robin S Thank you! Thank you! Your code worked, like a charm. Really indebted to you and the others who replied to my persistent queries. Really appreciate it. I don't see how this is a Bootstrap-sepecific problem. Am I the only one using Bootstrap with Processwire, and facing this problem? Just for my information, i would like to know what Framework gels best with Processwire? I concede that Bootstrap can be a bit bloated, is there a leaner and meaner framework out there? Thanks
    1 point
  34. Well, the markup you want to output is actually quite complicated. Needlessly complicated too IMHO, but that's Bootstrap's fault. @Soma, author of MarkupSimpleNavigation, said it pretty well: Couldn't agree more. Soma does have a Gist for generating Bootstrap markup with MSN, but it's for Bootstrap 2: https://gist.github.com/somatonic/6258081 Here is one way you could generate markup for your menu: $nav = $modules->get('MarkupSimpleNavigation'); $options = array( 'has_children_class' => 'has_children dropdown', 'levels' => true, 'max_levels' => 2, 'selector' => 'template!=MediaLibrary', 'outer_tpl' => '<nav class="collapse navbar-collapse navbar-right" role="navigation"><div class="main-menu"><ul class="nav navbar-nav">||</ul></div></nav>', 'inner_tpl' => '<div class="dropdown-menu"><ul>||</ul></div>', 'item_current_tpl' => '<a class="current" href="{url}">{title}</a>', ); $nav->addHookAfter('getItemString', function(HookEvent $event) use($options) { $item = $event->arguments('page'); if($item->hasChildren() && count($item->parents) < $options['max_levels']) { $class = $item == $this->page ? ' current' : ''; $event->return = "<a class='dropdown-toggle{$class}' data-toggle='dropdown' aria-expanded='false' aria-controls='navbar' href='{$item->url}'>{$item->title}</a>"; } }); echo $nav->render($options); Don't know about your JS issue, but that won't be PW-related.
    1 point
  35. $animal = 'cat'; $this->addHookAfter('Class::method', function($event) use($animal) { // is there some way to use $animal here? Yes....use 'use' echo $animal; }); Edit...OK, so I am too slow....
    1 point
  36. You can use $this->animal $this->animal = 'cat'; $this->addHookAfter('Page::render', function($event) { bd($this->animal); }); or you can also do this: $animal = 'cat'; $this->addHookAfter('Page::render', function($event) use($animal) { bd($animal); });
    1 point
  37. Thanks for your help @adrian it is working now! Solution: (function ($) { $( document ).ready(function() { $('#Inputfield_city').on( 'blur', function() { var address = $('#Inputfield_street').val() + ', ' + $('#Inputfield_city').val(); $('#Inputfield_location').val(address); $('#Inputfield_location').blur(); }); }); }(jQuery));
    1 point
  38. Any chance your map and address fields are in a fieldsettab? I have a setup like that and just tested with AdminCustomFiles and this works: $(document).on('wiretabclick', function () { Of course that will only work if the tab that the map is on is not the one initially loaded. Sorry, I don't have time to investigate right now, but keep in mind that you'll need to deal with the scenario when the tab is loaded open first, like after a page save. Maybe you are not using tabs, but it certainly shows the issue of that jquery being applied before the form elements exist.
    1 point
  39. Another, maybe better, approach would be to use AdminCustomFiles (http://modules.processwire.com/modules/admin-custom-files/) to add some custom JS to do this on-the-fly. Then use jQuery to grab the contents of your street and city fields, concatenate them, insert them into the map address field and then blur so that google maps triggers a geocode. Maybe something like this: $('#Inputfield_city').on( "blur", function() { var address = $('#Inputfield_street_address').val() + ' ' + $('#Inputfield_city').val(); $('#Inputfield_map').val(address); $('#Inputfield_map').blur(); }); On limited testing here it seems to work just fine. Of course it assumes the address field is named "street_address" and the city field is "city" and the mapmarker field is simply "map". Let me know if that works for you. Don't forget that you can test via your dev console so you know it's working before adding via Admin Custom Files. PS - of course you can always add commas, and even state and country elements to the address that goes into the map address field for geocoding.
    1 point
  40. It looks like executable is not found because it's not within the PATH. Do php -r "print getenv('PATH');" from shell or phpinfo(); from php and check for environment variable PATH. Make sure that jpegoptim (and others) are on the path. You could also check what shell is executed: echo shell_exec("echo $0"); It's possible that it's sh and not bash You may need to use the putenv command or determine whether your path needs to be set in /etc/profile, ~/.profile or ~/.bashrc in order for it to be picked up by the user runing php. Some versions of apache read configuration from /etc/apache2/envvars . You can set environment vars locally within a VirtualHost config using SetEnv. Or it might help if you put putenv('PATH=/your/path'); somewhere in the php, just for the test. You could also set the path of jpegoptim (and other binaries) by modifying optimizeSettings in AutoSmush.module: $this->optimizeSettings = array( 'ignore_errors' => false, //in production could be set to true 'jpegtran_options' => array('-optimize', '-progressive', '-copy', ' all'), 'jpegoptim_options' => array('--preserve', '--all-progressive', '--strip-none', '-T' . self::JPG_QUALITY_THRESHOLD), 'optipng_options' => array('-i0', '-o2', '-quiet', '-preserve'), 'advpng_options' => array('-z', '-3', '-q'), 'jpegoptim_bin' => '/path/to/jpegoptim', <== add this line ); All this is specific to the environment, so I can't give detail instructions on how to make "Local tools" to work. I'm also not a linux user, I tested tools on windows. Let me know how it goes.
    1 point
  41. Perhaps this new config field could be a Selector field ... //in MarkupCookieConsentConfig array( 'type' => 'Selector', 'name' => 'pageSelector', 'label' => __('Banner display limit'), 'description' => __('Define selectors, which limits the display of the banner to selected pages.'), 'columnWidth' => 100 ) // in function renderCookieForm() // we stop here when on admin pages except this modules config page (for demonstration) or out of pageSelector if(($page->template != 'admin' && wire('pages')->find($this->pageSelector)->has($page) == false) || ($page->template == 'admin' && $this->wire('input')->get->name != $this)) return;
    1 point
  42. It might be a nice feature for the api-explorer as well. Have your own code's documentation exported to dash ❤️
    1 point
  43. As an alternative, you can use a site grabber/ripper tool, e.g.: https://www.downloadcloud.com/website-ripper.html Also, you might want to purchase the new ProDevTools module with the API Explorer in it (provided it will be released before you leave): https://processwire.com/blog/posts/processwire-3.0.41-and-a-look-at-api-explorer/
    1 point
  44. Perhaps this new config field could be a field for entering a selector, that would allow more flexibility.
    1 point
  45. @Can Sorry if I didn't speak clearly. I was talking about the banner, not the cookie itself. I don't need an option to modify the path of the consent cookie itself, so I would hardcode it to '/'. (To stay complete leave it as is.) Much more interesting is to limit the display of the banner to a selectable page tree. Therefore an additional pagefield in the config could be useful. Lets call it pageTree. You just need to add one line at the beginning of the renderCookieForm() function. if($page->id != $this->pageTree && $page->parents->has(wire('pages')->get($this->pageTree)) == false) return; An additional checkbox to include or exclude the parent page would complete this option.
    1 point
  46. In PW's multi-language handling for text-based fields, these fields don't hold a regular string (as they do in non-ml setups) but a LanguagesPageFieldValue object. This object stores the field values (strings) for the different languages. When you retrieve the string value of such a field, PHP's standard magic calls _toString on the object. The implementation for _toString is done in the hookable getStringValue method. Unfortunately, while the LanguagesPageFieldValue objects do know their page and field, the information isn't accessible from a hook. But you can always access $this->wire('page') to get the current page in the frontend. Then you can retrieve the absender through $page->absender->first(). Some quick&dirty code for the function that probably illustrates this better (the above sounds more complicated than it really is): /* ... */ $mlObj = $event->object; // LanguagesPageFieldValue object $currPage = $this->wire('page'); if($currPage->template == "doctors") { $absender = $currPage->absender->first(); $absLang = $absender->sprache; $event->return = $mlObj->getLanguageValue($sprache); } /* ... */ As long as you apply use this logic to all language fields on a template, this approach should work fine. Otherwise, it would probably get complicated.
    1 point
  47. InputfieldSelect is a form inputfield like any others, so if you submit a form with the inputfield in it, the selector will be sent. You can then use this selector in the module to select the pages. There's no such thing as a prebuild ajax solution to update without an pagerefresh.
    1 point
  48. Case Study: SignificatoJournal.com: Migrating from MODX Evolution to ProcessWire Contents: * Useful MODX Fields * Custom Template Files * Template Chunks * Field Chunks * Snippets * The Writer Table * The Migration Script * URL Replacement * Image Migration * TinyMCE Code Breaks * Post Migration Data Checks * Link to Script Content I just finished migrating the magazine site that my wife and I run (http://significatojournal.com) from MODX Evolution to ProcessWire. How I did it may be of interest to other MODX users that wish to migrate their sites to ProcessWire. I liked MODX because it was so flexible. My experience with ProcessWire has been that PW is even more flexible than MODX, and it breaks the 5,000 page MODX Evo barrier that was the great bugaboo in Evo. I attempted to use MODX Revolution multiple times but was very unsatisfied with the slowness of the editorial interface. There were also other reasons that I left MODX for PW, that have been addressed by other writers. After using PW’s API to build a large web app in 2013 (which will be a different case study), and now, after having migrated my magazine site to PW, I’m absolutely thrilled with ProcessWire. I could go on and on... There were many things that I liked about MODX Evo, that provided functionality that I wanted to continue to use in ProcessWire. They included: Chunks, snippets, and a combination of built in MODX fields and custom template var “fields” that I created for my former website, including: Useful MODX Fields MODX Fields: longtitle (headline) pub_date introtext (summary) template (id of custom template) menuindex menutitle hidemenu (show in menu) * In PW, I use the three menu fields to create the menu of the site, using Soma’s excellent module “MarkupSimpleNavigation”, with code like this: 'selector' => 'show_in_menu=1' * I use the publish_date field to block display of pages with future publish_dates, as well as to show the actual date of publication set by the editor (not just the date of the saved record). Custom Template Var Fields: subtitle writer_id static_author static_author_attribution newsletter_volume_number headline_thumbnail article_type sitemap_exclude code_blocks1-7 Custom Template Files MODX Evo allows one to assign a custom template file to each article, which I found very useful. Unlike PW, MODX Evo uses a primary static data field set for each article, with custom fields added on as “template vars.” The advantage of the custom template files is that it allows one to use different display templates for different types of articles or section pages. I generally use a four level method of: - home page - multi-section page - section page - article page Given PW’s method of creating virtual data tables, aka template “field sets”, with display template files assigned to each template field set, I had to work out a method to have the same type of flexibility of assigning display template files to each page. For example, an article page might be a regular article page, with writer information, or an “article_plain” page, like a Contact Us page. A section page could be a multi-section page, with a list of sections, or a paginated section page that lists articles. I also had a need for custom section pages, to display articles based on “content_tags”. My solution was to create generic PW template data field sets, that all ran one php template file called “custom_template_controller.php.” The two main PW template field sets are: * article_page_structure_permanent * list_page_structure_permanent Using this method, when a new page is created, I select the PW data template first: and then, once I’m in the main set of fields, I select the custom template file for that page: The custom_template_controller.php is very simple, and simply pulls up the custom template file assigned to the page, and runs it: <?php ################################################################################################### # custom_template_controller.php ################################################################################################### include("./inc_vars.php"); include("./inc_functions.php"); #.................................................................... # block future publish dates; don't block home page (id 1) if ( ( $page->id == '1' ) || ( ! empty( $publish_date ) and $publish_date <= $now ) ) { # page can be displayed } else { wire(session)->redirect("/http404", false); } #.................................................................... include("./$custom_template_file"); ################################################################################################### The "./inc_vars.php" file gets the value of the field: $custom_template_file = $page->custom_template_file->select_value; and also initiates a variety of variables and template “chunks”. Template Chunks MODX Evo allows one to define “chunks” of text that can be replaced in the templates or in data fields, using {{tags}} that are replaced at run time. In PW, I divided the chunks into sets of templates chunks, and a smaller set of field chunks. Because of the way PW uses PHP as its “templating” language (which I REALLY like), I decided to simply place the template “chunks” in a file called "./inc_vars.php", and define them as normal PHP variables. Since that file is loaded before every page, the variables are available to use in the custom template files. Field Chunks For field chunks, I created a PHP function that loops through a set of “chunk” data pages and looks for corresponding tags in the body field, and then replaces them. I placed the “field chunks” branch of data pages under a hidden master page called “elements”, which I also used for custom selects like the custom_template_files. The field chunks use the MODX delimiters of curly brackets {{chunk_name}} and the contents of the chunks are replaced. For example, “{{email.pfb}} is replaced with an image of the email address as the title of a clickable, Javascript encoded mailto: link. In MODX, the field chunk system also allowed one to replace tags in text fields of data coming from template vars (custom fields). I found that my primary need for that was with code that TinyMCE didn’t like, such as data entry forms or special Javascript, so I created seven “code_block” fields, e.g. “code_block1” … “code_block7”. Seven is a bit much, but at the time I created the fields in MODX, I was using many Amazon affiliate tags for books and CDs, in various articles. Snippets MODX Evo also has handy-dandy snippet tags that get replaced at run time. For my purposes, I only need to replace snippets in the code block fields, prior to replacing the code block tags in the body text. For example, I have a form that needs to display a dynamically generated captcha image that gets created by a Perl script. So, in the code_block1 field of the article, which contains the form, I place a snippet tag: [!s_form_get_captcha!] which then gets replaced by the same function that parses the chunk tags. I used the syntax [!...!] from MODX Evo mainly for convenience. Unlike MODX, the ! exclamation marks don’t affect caching of the snippet. To work with the snippet tags, I created an array in the chunk parsing function that attaches the snippet tag to the name of a PHP include file: $snippet_array = array( '[!s_form_get_captcha!]' => '/home/sigj/s_form_get_captcha.php', ); In this case, the PHP file “s_form_get_captcha.php” contains a backtick call to a Perl script which returns the dynamically generated captcha image. But, the PHP file could contain any normal PHP code that has to be generated at run time. Here are the contents of the function that parses chunks and snippets: ################################################################################################### function parse_field_chunks($page_id) { $body = wire(pages)->get("$page_id")->body; $snippet_array = array( '[!s_form_get_captcha!]' => '/home/sigj/s_form_get_captcha.php', ); #.............................................................................. $field_chunk_id_array = wire(pages)->find("parent=1052, include=all"); foreach( $field_chunk_id_array as $chunk_id ) { $chunk_name = '{{' . wire(pages)->get("id=$chunk_id")->name . '}}'; $chunk_value = wire(pages)->get("id=$chunk_id")->chunk; $body = str_replace($chunk_name, $chunk_value, $body); } #.............................................................................. # replace code_block tags with field values for ( $count=1; $count<=7; $count++ ) { $code_block_field = 'code_block' . $count; $code_block_tag = '{{' . $code_block_field . '}}'; $code_block_value = wire(pages)->get("$page_id")->$code_block_field; # now parse code block value for snippet tags # [!snippet_name!] # [!s_form_get_captcha!] foreach ( $snippet_array as $snippet_tag => $snippet_include_file ) { if ( strpos($code_block_value, $snippet_tag) !== false ) { $snippet_value = include("$snippet_include_file"); $code_block_value = str_replace($snippet_tag, $snippet_value, $code_block_value); } } $body = str_replace($code_block_tag, $code_block_value, $body); } return($body); } ################################################################################################### The Writer Table I use the writer_id field as a popup, to pull in an id from a data table of long term writers. When an article page is displayed, code grabs the id and pulls in the writer info, including a photo, attribution and Javascript encoded email address. In MODX, I had to use a custom table. In PW, I simply created a template field set called ‘writer_page_structure_permanent.’ I use a ‘static_author” and “static_author_attribution” field for those times when an author is a one-off writer. My code tests for a writer dropdown ID for ‘Non-Registered’ writer, and if the static fields have something, then that data is displayed. Template Structure Here are some screen shots of my PW template structure, which essentially replicated my previous MODX structure: Templates: List Page Structure Permanent: Article Page Structure Permanent: The Migration Script One of the challenges I faced with my script to migrate the data was the assignment of the correct parent of each article. Luckily, I wanted to keep the exact structure of the section and article tree and the urls. Since I didn’t have tens of thousands of articles, I decided to create an associate array of the sections and articles under the first level of the home page (i.e. starting at level 2), and then use that sorted list to create the ProcessWire parents before each lower level of section or article. I built the script dynamically, testing as I went, so I wouldn’t say that the script is fit for any and all MODX situations. It’s heavily tailored to my installation, and is missing a few elements that I missed until after I had finished with it (thus causing me to fix some things by hand). URL Replacement I had to parse through each article, in both the body, summary and subtitle, to make sure that any internally pointing MODX urls were replaced with the full url. MODX Evo uses the syntax [~ID~] in the “<a href...” tag to dynamically create the full page url at run time. I had to create a routine to replace the ID tags with page urls, e.g. “/columns/some_article_name”. Image Migration I first took the lazy way out and thought that I could simply move the “/assets/” folder from the MODX installation over to the new account. However, when I opened a PW page in edit mode, the links to the /assets/... images were there, but the images weren’t attached to the page, and thus, in edit mode, the image didn’t now show up in the edit box. I therefore added a routine to copy the images to each page. TinyMCE Code Breaks I found that TinyMCE kept trashing my various CSS codes that came over from MODX. I tried adding various tags to the body field’s “valid_elements” field under Input / TinyMCE, but finally just changed valid_elements to: +*[*] It’s a bit radical, I suppose, but my editors are fully trusted. After that, my migrated data was fine. Post Migration Data Checks After I migrated the data for the umpteenth time, in order to get it right, I still needed to do a variety of clean up tasks. I found the Selector Test module by Niklas Lakanen very useful (Thanks, Niklas!), and used it to run checks like: body*=src\=\"{~root_url}assets (which looked for left over links to /assets/ which came from MODX) I also queried the MODX and PW tables directly, using SQLYog, a Windows MySQL client. I ran a script that compared the results of a find command (find . -type f -printf "%f\n") under the MODX assets folder to the files under the PW site/assets/files folder. I found about 70 files that were not copied, some because they were in template files, which my script didn’t parse, and some because the filenames were problematic, like files with spaces, etc. The script took into account the PW code that changes uploaded file names. To do that, I copied the PW “validate_filename” function into my script. For all my checking, I still forgot things, like parsing the subtitle or summary fields for hrefs, which I then had to go and do by hand (since there were very few records like that). I also created a few redirect aliases by hand, instead of trying to handle them via the script. All in all, this migration confirmed once again that website migrations are a Bear. Ugh. I’d rather not do them. Link to Script Content Here’s the link to the script. Note that it didn’t catch everything, and it was heavily tailored to my design. Also note that I’ve removed some of the private data, like writer’s names, etc. sj_modx_pw_migrate_script.php That’s my case study. I hope it may be useful to another “MODX Refugee”. Peter Falkenberg Brown
    1 point
  49. For any PageArray, count($pageArray) returns the number if items present now, but $pageArray->getTotal() returns the number of items total without pagination. So if you specified a "limit=n" in your selector when loading the pages, you can always call upon that $pages->getTotal() to tell you how many matched in total. There is also a $pageArray->getStart() method, which tells you the index of the first item. So if you had specified "limit=10" and you were on page2 in the pagination, then getStart() would return 10. I use these two functions to output a summary of results that tells you were you are. So if you wanted a headline that said this: You'd do something like this: $firms = $pages->find('parent=/firms/, num_staff>100, limit=10'); $start = $firms->getStart() + 1; $end = $firms->getStart() + count($firms); $total = $firms->getTotal(); echo "<h1>Firms with more than 100 staff</h1>"; echo "<h2>Showing firms $start - $end of $total</h2>"; foreach($firms as $firm) { echo "<p>{$firm->title} has {$firm->num_staff} staff</p>"; } // output the pagination links echo $firms->renderPager();
    1 point
×
×
  • Create New...