Jump to content

Search the Community

Showing results for 'runtime'.

  • Search By Tags

    Type tags separated by commas.
  • Search By Author

Content Type


Forums

  • Welcome to ProcessWire
    • News & Announcements
    • Showcase
    • Wishlist & Roadmap
  • Community Support
    • Getting Started
    • Tutorials
    • FAQs
    • General Support
    • API & Templates
    • Modules/Plugins
    • Themes and Profiles
    • Multi-Language Support
    • Security
    • Jobs
  • Off Topic
    • Pub
    • Dev Talk

Product Groups

  • Form Builder
  • ProFields
  • ProCache
  • ProMailer
  • Login Register Pro
  • ProDrafts
  • ListerPro
  • ProDevTools
  • Likes
  • Custom Development

Find results in...

Find results that contain...


Date Created

  • Start

    End


Last Updated

  • Start

    End


Filter by number of...

Joined

  • Start

    End


Group


AIM


MSN


Website URL


ICQ


Yahoo


Jabber


Skype


Location


Interests

  1. Okay understood. About the dev environment, I must say that it's not intended to change your habits. For example, my usual directory structure of my web folder containing the index.php/site/wire is the following: . └── /var/ └── html └── example.com ├── build/ssr/ssr.js <-- run by NodeJS on the server ├── wwwroot <-- directory sent to server hosting │ ├── site │ │ ├── modules │ │ │ └── ViteJS │ │ ├── assets │ │ │ └── static <-- contain assets built by vite │ │ │ ├── layout.abc123.js │ │ │ ├── home.abc123.js │ │ │ ├── about.abc.123.js │ │ │ └── ... │ │ ├── ... │ │ └── templates │ │ ├── _func.php │ │ ├── home.php │ │ └── about.php │ ├── wire │ ├── index.php │ └── .htaccess ├── src <-- development directory & files │ ├── styles │ │ └── app.scss │ └── js │ ├── components │ │ ├── Button.svelte │ │ ├── Hero.svelte │ │ └── ... │ ├── pages │ │ ├── Home.svelte │ │ ├── About.svelte │ │ └── ... │ ├── app.ts │ └── ssr.js ├── .env ├── package.json ├── vite.config.ts ├── tailwind.config.cjs ├── tsconfig.json └── ... The structure can be tweaked as you want. In this example, I'm using TailwindCSS, and Svelte. When developing, the result in your browser will be refreshed when you will save the modified file (it's called HMR for Hot Module Replacement). The same thing will happen when you will modify your ProcessWire templates files. In dev mode, files are served by the vite's local dev server. About your question about the "autobuild", with Vite you have basically three commands. Take a look at the following scripts section of a package.json file: "scripts": { "dev": "vite", "build": "vite build", "preview": "vite preview" }, When you will run `npm run dev` for example, vite will start the development server, the build command will build the production files, and the preview command will start a local server to serve the built production files. You are free to set up any workflow you are interested in, for example like throwing a build on each HMR event (I've given an example about that on stackoverflow, see https://stackoverflow.com/a/69632090/774432). On my side I am mostly working with private servers, but behind the scene the workflow is quite "the same". On my server, I run on closed environment a simple node command `node ../build/ssr/ssr.js` , where eg. on Vercel, you will want to use their Node.js Runtime features through an API. As I said, you can keep your habits in terms of editing content, let them log in ProcessWire backend and filling their body field. The update/preview is just a matter of refreshing or changing page on the site you are working on. The change is dynamic, you are not required to build or commit any change, and as always, you keep the freedom to make only the content you decide to be available - it's a bit , I made you an example on the bottom of the post. I do not use it and I don't remember how it works, but if you can pull data without markup, then all is ✅ even with markup it should work. I will test it. Enregistrement #52.mp4
  2. FieldtypeRuntimeMarkup and InputfieldRuntimeMarkup Modules Directory: http://modules.processwire.com/modules/fieldtype-runtime-markup/ GitHub: https://github.com/kongondo/FieldtypeRuntimeMarkup As of 11 May 2019 ProcessWire versions earlier than 3.x are not supported This module allows for custom markup to be dynamically (PHP) generated and output within a page's edit screen (in Admin). The value for the fieldtype is generated at runtime. No data is saved in the database. The accompanying InputfieldRuntimeMarkup is only used to render/display the markup in the page edit screen. The field's value is accessible from the ProcessWire API in the frontend like any other field, i.e. it has access to $page and $pages. The module was commissioned/sponsored by @Valan. Although there's certainly other ways to achieve what this module does, it offers a dynamic and flexible alternative to generating your own markup in a page's edit screen whilst also allowing access to that markup in the frontend. Thanks Valan! Warning/Consideration Although access to ProcessWire's Fields' admin pages is only available to Superusers, this Fieldtype will evaluate and run the custom PHP Code entered and saved in the field's settings (Details tab). Utmost care should therefore be taken in making sure your code does not perform any CRUD operations!! (unless of course that's intentional) The value for this fieldtype is generated at runtime and thus no data is stored in the database. This means that you cannot directly query a RuntimeMarkup field from $pages->find(). Usage and API Backend Enter your custom PHP snippet in the Details tab of your field (it is RECOMMENDED though that you use wireRenderFile() instead. See example below). Your code can be as simple or as complicated as you want as long as in the end you return a value that is not an array or an object or anything other than a string/integer. FieldtypeRuntimeMarkup has access to $page (the current page being edited/viewed) and $pages. A very simple example. return 'Hello'; Simple example. return $page->title; Simple example with markup. return '<h2>' . $page->title . '</h2>'; Another simple example with markup. $out = '<h1>hello '; $out .= $page->title; $out .= '</h1>'; return $out; A more advanced example. $p = $pages->get('/about-us/')->child('sort=random'); return '<p>' . $p->title . '</p>'; An even more complex example. $str =''; if($page->name == 'about-us') { $p = $page->children->last(); $str = "<h2><a href='{$p->url}'>{$p->title}</a></h2>"; } else { $str = "<h2><a href='{$page->url}'>{$page->title}</a></h2>"; } return $str; Rather than type your code directly in the Details tab of the field, it is highly recommended that you placed all your code in an external file and call that file using the core wireRenderFile() method. Taking this approach means you will be able to edit your code in your favourite text editor. It also means you will be able to type more text without having to scroll. Editing the file is also easier than editing the field. To use this approach, simply do: return wireRenderFile('name-of-file');// file will be in /site/templates/ If using ProcessWire 3.x, you will need to use namespace as follows: return ProcessWire\wireRenderFile('name-of-file'); How to access the value of RuntimeMarkup in the frontend (our field is called 'runtime_markup') Access the field on the current page (just like any other field) echo $page->runtime_markup; Access the field on another page echo $pages->get('/about-us/')->runtime_markup; Screenshots Backend Frontend
  3. Hi @Robin S, autoload module and init() method doesn't work. I disabled FileCompiler, but still not works. Because it's redirected to http404 before the existing template is set inside of the module. public function init() { $this->log->message("ServiceWorkerJS module autoload"); $template = $this->page->template; $this->log->message("TEMPLATE CURRENT: {$template}"); $file = $this->config->paths->siteModules . $this . '/templates/ServiceWorker.php'; $this->log->message("NEW RUNTIME FILE: {$file}"); $template->filename = $file; $this->log->message("CHECK TEMPLATE RUNTIME FILE: " . $template->filename); //$this->log->message("CHECK PAGE URL: " . $this->page->url()); // DISABLED BECAUSE IT FAILS WITH AUTOLOAD! } The log shows the template change runtime works fine, but it's to late to get the correct and expected page. URL is changed to http404. 2018-06-30 09:23:45 admin http://localhost/pw/serviceworker.js ServiceWorkerJS module autoload 2018-06-30 09:23:45 admin http://localhost/pw/serviceworker.js TEMPLATE CURRENT: 2018-06-30 09:23:45 admin http://localhost/pw/serviceworker.js NEW RUNTIME FILE: C:/xampp/htdocs/pw/site/modules/ServiceWorkerJS/templates/ServiceWorker.php 2018-06-30 09:23:45 admin http://localhost/pw/serviceworker.js CHECK TEMPLATE RUNTIME FILE: C:/xampp/htdocs/pw/site/modules/ServiceWorkerJS/templates/ServiceWorker.php 2018-06-30 09:23:47 admin http://localhost/pw/serviceworker.js ServiceWorkerJS module autoload 2018-06-30 09:23:47 admin http://localhost/pw/serviceworker.js TEMPLATE CURRENT: 2018-06-30 09:23:47 admin http://localhost/pw/serviceworker.js NEW RUNTIME FILE: C:/xampp/htdocs/pw/site/modules/ServiceWorkerJS/templates/ServiceWorker.php 2018-06-30 09:23:47 admin http://localhost/pw/serviceworker.js CHECK TEMPLATE RUNTIME FILE: C:/xampp/htdocs/pw/site/modules/ServiceWorkerJS/templates/ServiceWorker.php Autoload module is loaded twice during one page (re-)load, but page is redirected before the autoload module changes the template. FileCompiler is off (namespace in files, site/assets/cache/FileCompiler/site/modules/* is empty). Correct page should be instead of "serviceworker.js" instead of "http404". So how to prevent redirect to http404 with my autoload module? Where change the template to be early enough to prevent the redirect to http404? Example module need to deliver a serviceworker file to the browser from the root directory. Autoloaded with condition of template works fine. So initial the template is set, module is autoloaded, but page is redirected to http404. 'autoload' => 'template=ServiceWorker', There was a module->get() inside of basic-page. Conditional autoload based on template doesn't work. So I changed it to 'autoload' => true,
  4. Ok great, I thought I might be missing something, which can easily happen if you don't use the "regular" approach any more ? For me the runtime approach in general is a huge benefit. Once you start defining things in code all the little details that you would usually avoid because the GUI does not support it suddenly become a no-brainer and very easy to add. All the little customisations that you'd usually need a hook or a module for. They are simply there in your pageclass and you start to develop the backend (page editor) similar to how you'd develop your frontend. <?php public function editForm($form) { if ($f = $form->get('title')) { $f->appendMarkup(" <div>Custom field markup without any hooks...</div> <style>#wrap_Inputfield_title {border: 5px solid red;z-index:1;}</style> <script>console.log('js-demo');</script> "); } } PS: Note that this code will only be added on the page editor of pages having the pageclass where you defined your editForm() method! So if you added that in HomePage.php you'll only get the red border on the "home" page ?
  5. @bernhard At https://github.com/baumrock/RockMigrations#working-with-fieldsets you have e nice description on how to work with fieldsets. This seems to be limited to adding fields to fieldsets at runtime. Is there a way how we can define a migration that puts fields inside a fieldset in template context? Something like 'content-page' => [ 'tags' => 'content', 'fields' => [ 'title' => [ 'label' => 'Page Title', ], 'text' => [ 'label' => 'Subtitle', ], 'content_blocks' => [], 'tab' => [], // this adds the fieldsettab open and close 'fieldname' => [] // how to add this one to the tab fieldset ], It works fine with with $rm->addFieldToTemplate('fieldname', 'content-page' , 'tab', 'tab_END'). Just wondering if it is possible to do this inside the fields array in my example above.
  6. I imagine this may also be achieved without an additional database field using FieldtypeRuntimeMarkup: https://processwire.com/modules/fieldtype-runtime-markup/
  7. Hi all, I have a situation in which a user can upload a file that's any one of a number of formats (pdf, mpg, jpg, etc.). If the file is an image, I want to do some resizing. However, when I call ->size(###,###) on the file field when an image is in it, I get an error. Makes sense. I'm hoping there's an easy way to solve this without needing to create a separate field for image uploads. I tried following @Soma's advice from this thread, but kept getting an error from ImageSizer saying I was passing in invalid format. I know the file in there is an image as it's getting displayed in the browser. So, is there a standard way to convert a File field to an Image field? Thanks.
  8. Condition is/should be attached to store_template pages. So one store could have one entry "Type1" and another store could have multiple types ("Type2", "Type3"). There's no field yet for it, but it could be anything (simple text field, multi select field, page ref...). The condition to search for "Type1" comes from external at runtime, of course. But the more I think about it, the less likely it seems to be a good solution for database level search.
  9. Ah this looks like its exactly what im looking for. ? Can I just ask, did you get that code to work in order to take that screenshot, as I am getting a null error on the last line. Just curious of your field settings for the runtime fieldtype in this instance incase I need to ammend mine to get the same result.
  10. I have had a play and some of the implementation is literally, trivial ?. SSE SSE is so simple it is unbelievable at first. You only need two things: in our case PHP and a modern browser...batteries included! Example adapted from w3schools. More examples at MDN. server-side <?php namespace ProcessWire; header('Content-Type: text/event-stream'); header('Cache-Control: no-cache'); $time = date('r'); echo "data: The server time is: {$time}\n\n"; flush(); client if(typeof(EventSource) !== "undefined") { var source = new EventSource("/some-processwire/url/"); source.onmessage = function(event) { document.getElementById("result").innerHTML += event.data + "<br>"; }; } else { document.getElementById("result").innerHTML = "Sorry, your browser does not support server-sent events..."; } I tested it and it works fine but was getting errors about wrong mime type if I set the headers later, but that is to be expected. Live Preview Refresh - Partial and Full - ajax - htmx I have this working nicely (will try post the demo later today). The premise is: A change happens. The editor client sends the data to the server to save the change (autosave - was just mimicking this part). Server saves and sends back a response including the name of the field that has been saved and the page ID back to the editor client. The editor client communicates with the preview client (a separate window). It sets the page ID and the name of the changed field name in its message to the preview window. The preview window client (htmx) receives the page id and the name of the field that has changed and sends a request to the server to fetch the rendered contents of that field for that page. The server sends back the html for htmx to swap in the DOM. In this simple communication, we have the field name match the html id of the element we are swapping. E.g. <h2 id='headline'>My Headline</h2> This is simple and straightforward and easily achieves partial refresh. However, it will be difficult to handle the more complex cases I mentioned in my previous post. It will also be somewhat dictating how the developer should name their html attributes. In addition, it still leaves the developer's templates with htmx attributes that will not be needed in production. This got me thinking. Live Preview Wrapper - idea #1 In this case, the live preview window would add a wrapper html element around the rendered template file markup. The wrapper html element would have the htmx attributes instead of the template file markup. This idea didn't go very far. It would inevitably lead to broken / incorrect markup in the preview window as the outermost markup of the template file would be <html> tags. In addition, while it solves the problem of unrequired htmx attributes in production, it doesn't solve the problem of selective rendering. It still wouldn't' know what parts of the markup in the preview window to swap. Live Preview Config - idea #2 What if instead of live preview / ProcessWire suggesting things like naming of elements, standard markup, etc. that are needed in a template file in order to get selective markup rendering, we implemented it the other way round? What if the developer instead, via config file(s), passed properties to live preview to implement the htmx attributes needed for a template file / page? The default would be full page refreshes. However, if live preview detected that a partial refresh config was available, it would implement those. This would offer the granularity needed for partial refreshes. If live preview did not receive / detect any configs, the default would kick in. Live preview would remain as agnostic as possible. It could be something like this (assumes use of htmx): A developer places their live preview partial refresh configs in /site/templates/live-previews/basic-page-live-preview.php (or basic-page-live-preview.json). ProcessWire detects that the template file basic-page has a partial refresh config in .../live-previews/basic-page-live-preview.php When ProcessWire loads the preview window, it injects the htmx attributes for each named element in the live preview configs in the corresponding elements in the preview window DOM. Injection is done by a ProcessWire live preview near-agnostic JavaScript script that only runs in the preview window. When the autosave happens in the editor window and the preview window gets informed, htmx kicks in, responding to an event and fires htmx.trigger(elem, event). The element in the markup with the corresponding event listener (hx-trigger='this-element-event-fired') will pick up the event fired by htmx.trigger() and query the server for the changed markup. The attributes set in #3 will ensure only the markup that has a change will fire. Server sends the response back and the element's hx-swap (or even hx-oob) picks it up and swaps the DOM. #6 is still not straightforward though. What will live preview / ProcessWire send back? Here too the config at #1 can help. E.g. it can tell ProcessWire to send <p>My Field Value</p> or just send the value of the field back, i.e. 'My Field Value'. The former is still complicated. Instead of telling ProcessWire I want <p>My Field Value</p>, how about tell it, via the configs at #1 that if requested field is field A, send me back the raw value (e.g. a plain text) but if the request is for field B, use this defined function in basic-page-live-preview.php to send me the rendered partial markup for field B? All ProcessWire would do is pass the value of field B to the said function. This value could be a Pageimages object. The function would iterate the images and send back the 'gallery' markup back to htmx to swap. example live preview config <?php namespace ProcessWire; $livePreviewConfigs = [ [ 'field' => 'headline', 'element_id' => 'my_headline_element_id', 'htmx-attributes' => ['hx-swap' => 'outerHTML', 'hx-trigger' => 'headlineevent'] ], [ 'field' => 'images', 'element_id' => 'gallery', 'handler' => 'renderGallery','htmx-attributes' => ['hx-swap' => 'innerHTML', 'hx-trigger' => 'galleryevent'] ], ]; function renderGallery(Pageimages $images) { // we only need the <li> since we are using innerHTML swap // the target element is <ul id='gallery'></ul> // -------------- $out = ""; foreach ($images as $image) { $thumb = $image->size(200, 200); $out .= "<li><img src='{$thumb->url}' alt='{$image->url}'></li>"; } // --------- // sent back to htmx to swap return $out; } When the preview window loads, ProcessWire sends $livePreviewConfigs to the browser, e.g. ProcessWire.config.LivePreview. The preview window JavaScript would consume it like this: client set htmx attributes @note: JavaScript written in the browser, not tested, could have errors. // get live preview configs const elementsConfigs = ProcessWire.config.LivePreview // loop through configs, find the corresponding element and set their htmx attributes for (const elementConfig of elementsConfigs) { const element = document.getElementById(elementConfig.element_id) if (element) { const htmxAttributes = elementConfig['htmx-attributes'] for (const [prop, value] of Object.entries(htmxAttributes)) { element.setAttribute(prop,value); } } } htmx-ready DOM changes via live preview injection (runtime only in live preview window): <h2 id="my_headline_element_id" hx-swap='outerHTML' hx-trigger='headlineevent' hx-vals='{"field":"headline"}'>My Headline</h2> <!-- gallery --> <ul id="gallery" hx-swap='innerHTML' hx-trigger='galleryevent' hx-vals='{"field":"images"}'> <li><img src='image-1-src' alt='image-1-alt'></li> <li><img src='image-2-src' alt='image-2-alt'></li> </ul> I have partly implement the above (just hard-coded configs for now) and it works nicely. Takeaways ProcessWire's live preview remains as agnostic as possible. Developer give instructions to live preview (via configs) and not the other way round. Live preview ships with minimal defaults, including full page refresh. It seems like a lot of work on the developer's part if they want partial refresh. However, this partly matches the complexity of their template files. Besides, some of the functionality (e.g. renderGallery()) could be something they already use in their template, hence reusable. Developer does not need to have additional markup in their template files in order to see live previews when editing. This means no cleaning up afterwards. Their templates remain clean. Live preview only alters the DOM (of the rendered template) at runtime. Developer can name their markup attributes as they wish. Developer can decide what needs live preview and what doesn't, even on the same page (e.g. hx-select). ProcessWire stays out of the developer's way, as usual.
  11. I also brought this up before as a wishlist request UX improvements · Issue #392 · processwire/processwire-requests (github.com), because I use these features of AoS in EVERY PW instance. But there are also always voices against integrating useful things into the core, to keep it "lean". For example integrating migrations into the core, which I see as crucial, but others say that migrations are not needed for everyone, and so they should stay out of the core. One feature might be useful for one person, but not for the others, so I am always in favor of "providing options" (turning features on or off). I'm also at a point where I find the admin or UIkit theme stale and not really functional. There are a lot of things missing for me. Reactivity like vue.js or alpinejs or react offer. Better usability of dropdowns in the main menu and other things, but I digress. Several times I started to develop a new admin theme based on vuejs, but also noticed how complicated for example the pagetree is built and moreover I lacked the time. Furthermore, with such a big undertaking, it's also always a question of how many people would really use it, or if I (or WE) are just developing it for fun. Because I don't want to develop anything for &>/dev/null. Back to AdminOnSteroids: Right now it's not modular and uses jQuery under the hood. It's not a bad thing because the PW core also relies on jQuery, but would rather suggest a rewrite here where every feature is a javascript module. These modules could be loaded on demand (conditionally based if a specific DOM element exists or not) at runtime. This keeps the core lean, and also the javascript load and execution time and memory consumption low. Maybe such an update (or the integration into the core) could be developed by crowdfunding. I am also in favor of ditching support for Default or Reno theme, because I think that most people would use AdminThemeUikit, but I might be wrong? Upgrading the PHP part, would not be that difficult, if we drop support for PHP versions < 8.
  12. @Ivan Gretskygreat news. I contributed to the module many years ago with leaflet providers and marker cluster integration. It definitely needs some attention and love. Upgrading assets etc. I think that leaflet.js in general is still a viable solution for open source maps in general. Mapbox have changed their subscription model few years ago that is why I still prefer Leaflet over Mapbox. As for geocoding addresses this is still an issue with the open source provider that sits behind InputfieldLeafletMapMarker (Nominatim). They have improved a lot over time. But still failing on more exotic addresses where the Google Maps geocoder is doing a better job. There's quite a few open source geocoding services out there, but most of them use data from OSM's Nominatim. So you can't expect better results than you get with Nominatim. If looking for closed source geocoding APIs, one that might be worth looking into is https://positionstack.com/ . They have a free tier and seem to have a huge address pool. And I'd rather trust my data with an Austrian Company then sending it to Google. Just sayin... It could be used a s a fallback in case Nominatim returns garbage or nothing. It is not. At least with v3.0.3 of InputfieldLeafletMapMarker. You can make seperate inputfields that hold address data work together with the InputfieldLeafletMapMarker. I once wrote a module for a specific use case where I have 3 inputfields, address, postcode, city. The module loads some JS that first hides the default InputfieldLeafletMapMarker inputs, adds a button which takes the values from those fields, geocodes them through Nominatim, adjusts the marker on the map in the InputfieldLeafletMapMarker and fills the (now hidden) fields with the lat lng and name. Although I have done this about 4 years ago or so, it is still working. Here's the module code for reference in case anyone wants to go a similar route. Not a very clean and portable integration but it is doing the job and is here mainly to serve as an example how you could tackle this scenario. EDIT: short screencast to show UI AddressToMap.module.php (just loads the necessary JS) <?php use ProcessWire\HookEvent; class AddressToMap extends WireData implements Module { /** * getModuleInfo is a module required by all modules to tell ProcessWire about them * * @return array * */ public static function getModuleInfo() { return array( // The module'ss title, typically a little more descriptive than the class name 'title' => 'AddressToMap', // version number 'version' => 001, // summary is brief description of what this module is 'summary' => 'Geocode an Address and put a Pin on the Leaflet Map', // singular=true: indicates that only one instance of the module is allowed. // This is usually what you want for modules that attach hooks. 'singular' => true, // autoload=true: indicates the module should be started with ProcessWire. // This is necessary for any modules that attach runtime hooks, otherwise those // hooks won't get attached unless some other code calls the module on it's own. // Note that autoload modules are almost always also 'singular' (seen above). 'autoload' => "template=admin", // Optional font-awesome icon name, minus the 'fa-' part 'icon' => 'map', ); } public function init() { $this->wire->addHookAfter('ProcessPageEdit::loadPage', $this, 'loadAssets'); } public function loadAssets(Hookevent $event) { $id = $event->arguments(0); $page = $this->pages->get($id); if($page->template == 'member') { $this->config->scripts->add($this->config->urls->siteModules . $this->className . "/{$this->className}.js"); } } } AddressToMap.js var AddressToMap = { init: function () { // $(window).on('map:init', function (e) { // map = e.originalEvent.detail.map; // console.log(map); // }); var coder = window.L.Control.Geocoder.nominatim(); // console.log(AddressToMap.geocoder); var address = $('p.InputfieldLeafletMapMarkerAddress').css({ 'display': 'none' }); var setAddress = $('<p></p>').addClass('InputfieldLeafletMapMarkerSetAddress'); var button = $($.parseHTML("<button class='btn btn-primary btn-block' id='setAddress'><i class='fa fa-arrow-down fa-fw'></i>" + ProcessWire.config.strings.addresstomap + "<i class='fa fa-arrow-down fa-fw'></i></button>")); setAddress.append(button); setAddress.insertAfter(address); var inputfieldName = $('.Inputfield.InputfieldLeafletMapMarker').attr('id').replace('wrap_', ''); button.on('click', function (event) { event.preventDefault(); var combAddress = $('#Inputfield_address').val() + ', ' + $('#Inputfield_postcode').val() + ', ' + $('#Inputfield_city').val(); coder.geocode(combAddress, function (results) { if(results[0] !== undefined) { var map = window.leafletmap; map.eachLayer(function(layer){ if(layer.options.draggable === true) { var latlng = L.latLng(results[0].center.lat, results[0].center.lng); layer.setLatLng(latlng); $('#_' + inputfieldName + '_lat').val(results[0].center.lat); $('#_' + inputfieldName + '_lng').val(results[0].center.lng); $('input#' + inputfieldName).val(results[0].name); map.fitBounds(results[0].bbox); } }); } else { alert('Could not geocode this address'); } }); }); } } $(document).ready(function () { AddressToMap.init(); });
  13. @thetuningspoon magicpages are super cool ? the only drawback is that they come with a small performance penalty as I need to load all available templates once on init and then attach the hooks or magic methods on applicable templates: https://github.com/baumrock/RockMigrations/blob/87541c899901f773bf62068a639f070f48a85453/MagicPages.module.php#L43-L51 The init() and ready() method (and the magic methods) are only called once for every template because I create one runtime page for each template on ProcessWire::init The magic methods on the other hand only trigger for the correct pageclass because I have an early exit in the hook if the pageclass does not match: https://github.com/baumrock/RockMigrations/blob/87541c899901f773bf62068a639f070f48a85453/MagicPages.module.php#L113-L119 You might also like this:
  14. Here's my complete work-round for the problem with runtime markup images in a Page Table field. Something that prevents the problem occurring in the first instance would be better though! 1. Add the following condition for images rendered in the runtime markup: if (is_a($image, '\ProcessWire\Pageimage')) $out .= '<img src="' . $image->url . '"/>'; 2. Add the following to admin.js (the 2 functions are lifted from InputfieldPageTable.js): /*******************************************************************************************************************/ /* Work-round to reload and re-enable sorting of Page Table field, which includes runtime markup, after modal edit */ /*******************************************************************************************************************/ $(document).on('pw-modal-closed', function(event) { var wrap = $(this).find('.InputfieldPageTable'); if ($(wrap).attr('id')) $('#' + wrap.attr('id')).load(document.URL + ' #' + wrap.attr('id') + '> *'); // To just load the inner html of wrap - avoiding duplicating it }); $('.InputfieldPageTable').mouseenter(function () { if (!$('tbody').hasClass("ui-sortable")) { var $table = $('tbody').closest('table'); InputfieldPageTableSortable($table); } }); function InputfieldPageTableUpdate($table) { var value = ''; if(!$table.is('tbody')) $table = $table.find('tbody'); $table.find('tr').each(function() { var pageID = $(this).attr('data-id'); if(value.length > 0) value += '|'; value += pageID; }); var $container = $table.parents('.InputfieldPageTableContainer'); var $input = $container.siblings('.InputfieldPageTableSort'); $input.val(value); } function InputfieldPageTableSortable($table) { $table.find('tbody').sortable({ axis: 'y', start: function(event, ui) { var widths = []; var n = 0; $table.find('thead').find('th').each(function() { widths[n] = $(this).width(); n++; }); n = 0; ui.helper.find('td').each(function() { $(this).attr('width', widths[n]); n++; }); }, stop: function(event, ui) { InputfieldPageTableUpdate($(this)); } }); } /*******************************************************************************************************************/
  15. Just noticed the recent GitHub commit: and looks intriguing... Any chance of some sample code to illustrate how/where this might be used? Thanks in advance
  16. Hi, I'm not sure what has gone wrong but after adding a few lines for webp to my .htaccess my site no longer works properly. I reversed it but there may have been another version of .htacess on the site which I've now overwritten. Note: After some checking: It doesn't seem to be rendering the templates. Eg: / = (should use template home.php) /about/ = (uses template page.php) For example putting "die;" in the home.php does nothing on the live server. Basically, it doesn't render the content block and spits out "default content" It is working perfectly on my local server with the same .htaccess file. $config->useMarkupRegions = true; I do not know what to do and I'd be grateful for help. This is my site: https://greglumley.com I've changed "default content" to a more friendly message. And here is my .htaccess ################################################################################################# # START PROCESSWIRE HTACCESS DIRECTIVES # @version 3.0 # @htaccessVersion 301 ################################################################################################# # # Upgrading htaccess (or index) version 300 to 301 # ----------------------------------------------------------------------------------------------- # If you never modified your previous .htaccess file, then you can simply replace it with this # one. If you have modified your .htaccess file, then you will want to copy/paste some updates # to the old one instead: # If your htaccess/index version is 300, upgrade to this version by replacing all of sections #5 # and #15 (Access Restrictions). Also take a look at section #9, which you might also consider # replacing if using HTTPS, though it is not required. (For instance, HSTS might be worthwhile) # # Following that, optionally review the rest of the file to see if there are any other changes # you also want to apply. Sections tagged "(v301)" are new or have significant changes. # # When finished, add a line at the top identical to the "htaccessVersion 301" that you see at # the top of this file. This tells ProcessWire your .htaccess file is up-to-date. # # Resolving 500 errors # ----------------------------------------------------------------------------------------------- # Depending on your server, some htaccess rules may not be compatible and result in a 500 error. # If you experience this, find all instances of the term "(500)" in this file for suggestions on # things you can change to resolve 500 errors. # # Optional features # ----------------------------------------------------------------------------------------------- # Many of the rules in this .htaccess file are optional and commented out by default. While the # defaults are okay for many, you may want to review each section in this .htaccess file for # optional rules that you can enable to increase security, speed or best practices. To quickly # locate all optional rules, search this file for all instances of "(O)". # # If using a load balancer # ----------------------------------------------------------------------------------------------- # If using a load balancer (like those available from AWS) some htaccess rules will need to # change. Search this file for instances of "(L)" for details. # # ----------------------------------------------------------------------------------------------- # 1. Apache Options # # Note: If you experience a (500) error, it may indicate your host does not allow setting one or # more of these options. First try replacing the +FollowSymLinks with +SymLinksifOwnerMatch. # If that does not work, try commenting them all out, then uncommenting one at a time to # determine which one is the source of the 500 error. # ----------------------------------------------------------------------------------------------- # Do not show directory indexes (strongly recommended) Options -Indexes # Do not use multiviews (v301) Options -MultiViews # Do follow symbolic links Options +FollowSymLinks # Options +SymLinksifOwnerMatch # Character encoding: Serve text/html or text/plain as UTF-8 AddDefaultCharset UTF-8 # ----------------------------------------------------------------------------------------------- # 2. ErrorDocument settings: Have ProcessWire handle 404s # # For options and optimizations (O) see: # https://processwire.com/blog/posts/optimizing-404s-in-processwire/ # ----------------------------------------------------------------------------------------------- ErrorDocument 404 /index.php # ----------------------------------------------------------------------------------------------- # 3. Handle request for missing favicon.ico/robots.txt files (no ending quote for Apache 1.3) # ----------------------------------------------------------------------------------------------- <Files favicon.ico> ErrorDocument 404 "The requested file favicon.ico was not found. </Files> <Files robots.txt> ErrorDocument 404 "The requested file robots.txt was not found. </Files> # ----------------------------------------------------------------------------------------------- # 4. Protect from XSS with Apache headers # ----------------------------------------------------------------------------------------------- <IfModule mod_headers.c> # prevent site from being loaded in an iframe on another site # you will need to remove this one if you want to allow external iframes Header always append X-Frame-Options SAMEORIGIN # To prevent cross site scripting (IE8+ proprietary) Header set X-XSS-Protection "1; mode=block" # Optionally (O) prevent mime-based attacks via content sniffing (IE+Chrome) # Header set X-Content-Type-Options "nosniff" </IfModule> # ----------------------------------------------------------------------------------------------- # 5. Prevent access to various types of files (v301) # # Note that some of these rules are duplicated by RewriteRules or other .htaccess files, as we # try to maintain two layers of protection when/where possible. # ----------------------------------------------------------------------------------------------- # 5A. Block access to inc, info, info.json/php, module/php, sh, sql and composer files # ----------------------------------------------------------------------------------------------- <FilesMatch "\.(inc|info|info\.(json|php)|module|module\.php|sh|sql)$|^\..*$|composer\.(json|lock)$"> <IfModule mod_authz_core.c> Require all denied </IfModule> <IfModule !mod_authz_core.c> Order allow,deny </IfModule> </FilesMatch> # 5B. Block bak, conf, dist, ini, log, orig, sh, sql, swo, swp, ~, and more # ----------------------------------------------------------------------------------------------- <FilesMatch "(^#.*#|\.(bak|conf|dist|in[ci]|log|orig|sh|sql|sw[op])|~)$"> <IfModule mod_authz_core.c> Require all denied </IfModule> <IfModule !mod_authz_core.c> Order allow,deny </IfModule> </FilesMatch> # ----------------------------------------------------------------------------------------------- # 6. Override a few PHP settings that can't be changed at runtime (not required) # Note: try commenting out this entire section below if getting Apache (500) errors. # ----------------------------------------------------------------------------------------------- <IfModule mod_php5.c> php_flag magic_quotes_gpc off php_flag magic_quotes_sybase off php_flag register_globals off </IfModule> # ----------------------------------------------------------------------------------------------- # 7. Set default directory index files # ----------------------------------------------------------------------------------------------- DirectoryIndex index.php index.html index.htm # ----------------------------------------------------------------------------------------------- # 8. Enable Apache mod_rewrite (required) # ----------------------------------------------------------------------------------------------- <IfModule mod_rewrite.c> RewriteEngine On # Send WEBP images for JPG or PNG when supported and available. # This means that a request for a JPG or PNG file will instead deliver # WEBP data, but only when the browser supports and understands it. # RewriteCond %{HTTP_ACCEPT} image/webp # RewriteCond %{REQUEST_FILENAME} -f # RewriteCond %{DOCUMENT_ROOT}/$1$2$3/$4.webp -f # RewriteCond expr "! %{QUERY_STRING} -strmatch 'nc=*'" # RewriteRule ^(.*?)(site/assets/files/)([0-9]+)/(.*)\.(jpe?g|png)(.*)$ /$1$2$3/$4.webp [L] # 8A. Optionally (O) set a rewrite base if rewrites are not working properly on your server. # ----------------------------------------------------------------------------------------------- # In addition, if your site directory starts with a "~" you will most likely have to use this. # https://httpd.apache.org/docs/current/mod/mod_rewrite.html#rewritebase # Examples of RewriteBase (root and subdirectories): # RewriteBase / # RewriteBase /pw/ # RewriteBase /~user/ # 8B. Set an environment variable so the installer can detect that mod_rewrite is active. # ----------------------------------------------------------------------------------------------- # Note that some web hosts don't support this. If you get a (500) error, try commenting out this # SetEnv line below. <IfModule mod_env.c> SetEnv HTTP_MOD_REWRITE On </IfModule> # ----------------------------------------------------------------------------------------------- # 9. Optionally Force HTTPS (O) # ----------------------------------------------------------------------------------------------- # Note that on some web hosts you may need to replace %{HTTPS} with %{ENV:HTTPS} in order # for it to work (in sections 9A and 9D below). If on a load balancer or proxy setup, you will # likely need to use 9B rather than 9A, and 9E rather than 9D. # ----------------------------------------------------------------------------------------------- # 9A. To redirect HTTP requests to HTTPS, uncomment the lines below (also see note above): # ----------------------------------------------------------------------------------------------- # RewriteCond %{HTTPS} !=on # RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301] # 9B. If using load balancer/AWS or behind proxy, use the following rather than 9A above: (L) # ----------------------------------------------------------------------------------------------- # RewriteCond %{HTTP:X-Forwarded-Proto} =http # RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301] # 9C. If using cPanel AutoSSL or Let's Encrypt webroot you may need to MOVE one of the below # lines after the first RewriteCond in 9A or 9B to allow certificate validation: # ----------------------------------------------------------------------------------------------- # RewriteCond %{REQUEST_URI} !^/\.well-known/acme-challenge/ # RewriteCond %{REQUEST_URI} !^/\.well-known/cpanel-dcv/[\w-]+$ # RewriteCond %{REQUEST_URI} !^/\.well-known/pki-validation/[A-F0-9]{32}\.txt(?:\ Comodo\ DCV)?$ # 9D. Store current scheme in a 'proto' environment variable for later use # ----------------------------------------------------------------------------------------------- RewriteCond %{HTTPS} =on RewriteRule ^ - [env=proto:https] RewriteCond %{HTTPS} !=on RewriteRule ^ - [env=proto:http] # 9E. If using load balancer/AWS or behind proxy, use lines below rather than 9D: (L) # ----------------------------------------------------------------------------------------------- # RewriteCond %{HTTP:X-Forwarded-Proto} =https # RewriteRule ^ - [env=proto:https] # RewriteCond %{HTTP:X-Forwarded-Proto} =http # RewriteRule ^ - [env=proto:http] # 9F. Tell web browsers to only allow access via HSTS: Strict-Transport-Security (O) (v301) # ----------------------------------------------------------------------------------------------- # This forces client-side SSL redirection. Before enabling be absolutely certain you can # always serve via HTTPS because it becomes non-revokable for the duration of your max-age. # See link below for details and options (note 'max-age=31536000' is 1-year): # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security <IfModule mod_headers.c> # Uncomment one (1) line below & adjust as needed to enable Strict-Transport-Security (HSTS): # Header always set Strict-Transport-Security "max-age=31536000;" # Header always set Strict-Transport-Security "max-age=31536000; includeSubdomains" # Header always set Strict-Transport-Security "max-age=31536000; preload" # Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" </IfModule> # Section 10 intentionally omitted for future use # ----------------------------------------------------------------------------------------------- # 11. Nuisance blocking/firewall # ----------------------------------------------------------------------------------------------- # None of these are enabled by default, but are here for convenience when the need arises. # Review and uncomment as needed. For more complete firewall (and more overhead), the 7G firewall # (or latest version) is worth considering, see: https://perishablepress.com/7g-firewall/ # ----------------------------------------------------------------------------------------------- # 11A. Block via IP addresses # ----------------------------------------------------------------------------------------------- # Note that IP addresses here are examples only and should be replaced with actual IPs. # Block single IP address # Deny from 111.222.333.444 # Block multiple IP addresses # Deny from 111.222.333.444 44.33.22.11 # Block IP address ranges (999.88.*, 99.88.77.*, 1.2.3.*) # Deny from 999.888 99.88.77 1.2.3 # 11B. Block via request URI (matches strings anywhere in request URL) # ----------------------------------------------------------------------------------------------- # RewriteCond %{REQUEST_URI} (bad-word|wp-admin|wp-content) [NC] # RewriteRule .* - [F,L] # 11B. Block via user agent strings (matches strings anywhere in user-agent) # ----------------------------------------------------------------------------------------------- # RewriteCond %{HTTP_USER_AGENT} (bad-bot|mean-bot) [NC] # RewriteRule .* - [F,L] # 11C. Block via remote hosts # ----------------------------------------------------------------------------------------------- # RewriteCond %{REMOTE_HOST} (bad-host|annoying-host) [NC] # RewriteRule .* - [F,L] # 11D. Block via HTTP referrer (matches anywhere in referrer URL) # ----------------------------------------------------------------------------------------------- # RewriteCond %{HTTP_REFERER} (bad-referrer|gross-referrer) [NC] # RewriteRule .* - [F,L] # 11E. Block unneeded request methods (only if you do not need them) # ----------------------------------------------------------------------------------------------- # RewriteCond %{REQUEST_METHOD} ^(connect|debug|delete|move|put|trace|track) [NC] # RewriteRule .* - [F,L] # 11F. Limit file upload size from Apache (i.e. 10240000=10 MB, adjust as needed) # ----------------------------------------------------------------------------------------------- # LimitRequestBody 10240000 # ----------------------------------------------------------------------------------------------- # 12. Access Restrictions: Keep web users out of dirs or files that begin with a period, # but let services like Lets Encrypt use the webroot authentication method. # ----------------------------------------------------------------------------------------------- RewriteRule "(^|/)\.(?!well-known)" - [F] # ----------------------------------------------------------------------------------------------- # 13. Optional domain redirects (O) # # Redirect domain.com to www.domain.com redirect (or www to domain.com redirect). # To use, uncomment either 13A, 13B or 13C. Do not use more than one of them. 13A and 13B # redirect non-www hosts to www.domain.com, 13C redirects www.domain.com host to domain.com. # ----------------------------------------------------------------------------------------------- # 13A. Redirect domain.com and *.domain.com to www.domain.com (see also 13B as alternate): # ----------------------------------------------------------------------------------------------- # RewriteCond %{HTTP_HOST} !^www\. [NC] # RewriteCond %{SERVER_ADDR} !=127.0.0.1 # RewriteCond %{SERVER_ADDR} !=::1 # RewriteRule ^ %{ENV:PROTO}://www.%{HTTP_HOST}%{REQUEST_URI} [L,R=301] # 13B. Alternate www redirect if 13A does not work for your server: # ----------------------------------------------------------------------------------------------- # RewriteCond %{HTTP_HOST} !^www\. [NC] # RewriteRule ^ %{ENV:PROTO}://www.%{HTTP_HOST}%{REQUEST_URI} [L,R=301] # 13C. Redirect www.domain.com to domain.com (do not combine with 13A or 13B): # ----------------------------------------------------------------------------------------------- # RewriteCond %{HTTP_HOST} ^www\.(.+)$ [NC] # RewriteRule ^ %{ENV:PROTO}://%1%{REQUEST_URI} [R=301,L] # ----------------------------------------------------------------------------------------------- # 14. Optionally send URLs with non-ASCII name-format characters to 404 page (optimization). # # This ensures that ProcessWire does not spend time processing URLs that we know ahead of time # are going to result in 404s. Uncomment lines below to enable. (O) # ----------------------------------------------------------------------------------------------- # RewriteCond %{REQUEST_URI} "[^-_.a-zA-Z0-9/~]" # RewriteCond %{REQUEST_FILENAME} !-f # RewriteCond %{REQUEST_FILENAME} !-d # RewriteRule ^(.*)$ index.php?it=/http404/ [L,QSA] # ----------------------------------------------------------------------------------------------- # 15. Access Restrictions (v301) # ----------------------------------------------------------------------------------------------- # 15A. Keep http requests out of specific files and directories # ----------------------------------------------------------------------------------------------- # Prevent all the following rules from blocking images in site install directories RewriteCond %{REQUEST_URI} !(^|/)site-[^/]+/install/[^/]+\.(jpg|jpeg|png|gif|webp|svg)$ # Block access to any htaccess files RewriteCond %{REQUEST_URI} (^|/)(\.htaccess|htaccess\..*)$ [NC,OR] # Block access to various assets directories RewriteCond %{REQUEST_URI} (^|/)(site|site-[^/]+)/assets/(cache|logs|backups|sessions|config|install|tmp)($|/.*$) [NC,OR] # Block access to the /site/install/ directories RewriteCond %{REQUEST_URI} (^|/)(site|site-[^/]+)/install($|/.*$) [NC,OR] # Block dirs in /site/assets/dirs that start with a hyphen (see config.pagefileSecure) RewriteCond %{REQUEST_URI} (^|/)(site|site-[^/]+)/assets.*/-.+/.* [NC,OR] # Block access to /wire/config.php, /site/config.php, /site/config-dev.php, /wire/index.config.php, etc. RewriteCond %{REQUEST_URI} (^|/)(wire|site|site-[^/]+)/(config|index\.config|config-dev)\.php($|/) [NC,OR] # Block access to any PHP-based files in /site/templates-admin/ or /wire/templates-admin/ RewriteCond %{REQUEST_URI} (^|/)(wire|site|site-[^/]+)/templates-admin($|/|/.*\.(php|html?|tpl|inc))($|/) [NC,OR] # Block access to any PHP or markup files in /site/templates/ or /site-*/templates/ RewriteCond %{REQUEST_URI} (^|/)(site|site-[^/]+)/templates($|/|/.*\.(php|html?|tpl|inc))($|/) [NC,OR] # Block access to any files in /site/classes/ or /site-*/classes/ RewriteCond %{REQUEST_URI} (^|/)(site|site-[^/]+)/classes($|/.*) [NC,OR] # Block access to any PHP files within /site/assets/ and further RewriteCond %{REQUEST_URI} (^|/)(site|site-[^/]+)/assets($|/|/.*\.ph(p|ps|tml|p[0-9]))($|/) [NC,OR] # Block access to any PHP, module, inc or info files in core or core modules directories RewriteCond %{REQUEST_URI} (^|/)wire/(core|modules)/.*\.(php|inc|tpl|module|info\.json)($|/) [NC,OR] # Block access to any PHP, tpl or info.json files in /site/modules/ or /site-*/modules/ RewriteCond %{REQUEST_URI} (^|/)(site|site-[^/]+)/modules/.*\.(php|inc|tpl|module|info\.json)$ [NC,OR] # Block access to any software identifying txt, markdown or textile files RewriteCond %{REQUEST_URI} (^|/)(COPYRIGHT|INSTALL|README|htaccess)\.(txt|md|textile)$ [NC,OR] # Block potential arbitrary backup files within site directories for things like config RewriteCond %{REQUEST_URI} (^|/)(site|site-[^/]+)/(config[^/]*/?|[^/]+\.php.*)$ [NC,OR] # Block access throughout to temporary files ending with tilde created by certain editors RewriteCond %{REQUEST_URI} \.(html?|inc|json|lock|module|php|py|rb|sh|sql|tpl|tmpl|twig)~$ [NC,OR] # Block access to names of potential backup file extensions within wire or site directories RewriteCond %{REQUEST_URI} (^|/)(wire/|site[-/]).+\.(bak|old|sql|sw[op]|(bak|php|sql)[./]+.*)[\d.]*$ [NC,OR] # Block all http access to the default/uninstalled site-default directory RewriteCond %{REQUEST_URI} (^|/)site-default/ # If any conditions above match, issue a 403 forbidden RewriteRule ^.*$ - [F,L] # 15B. Block archive file types commonly used for backup purposes (O) # ----------------------------------------------------------------------------------------------- # This blocks requests for zip, rar, tar, gz, and tgz files that are sometimes left on servers # as backup files, and thus can be problematic for security. This rule blocks those files # unless they are located within the /site/assets/files/ directory. This is not enabled by # default since there are many legitimate use cases for these files, so uncomment the lines # below if you want to enable this. # RewriteCond %{REQUEST_URI} \.(zip|rar|tar|gz|tgz)$ [NC] # RewriteCond %{REQUEST_URI} !(^|/)(site|site-[^/]+)/assets/files/\d+/ [NC] # RewriteRule ^.*$ - [F,L] # PW-PAGENAME # ----------------------------------------------------------------------------------------------- # 16A. Ensure that the URL follows the name-format specification required by PW # See also directive 16b below, you should choose and use either 16a or 16b. # ----------------------------------------------------------------------------------------------- RewriteCond %{REQUEST_URI} "^/~?[-_.a-zA-Z0-9/]*$" # ----------------------------------------------------------------------------------------------- # 16B. Alternative name-format specification for UTF8 page name support. (O) # If used, comment out section 16a above and uncomment the directive below. If you have updated # your $config->pageNameWhitelist make the characters below consistent with that. # ----------------------------------------------------------------------------------------------- # RewriteCond %{REQUEST_URI} "^/~?[-_./a-zA-Z0-9æåäßöüđжхцчшщюяàáâèéëêěìíïîõòóôøùúûůñçčćďĺľńňŕřšťýžабвгдеёзийклмнопрстуфыэęąśłżź]*$" # END-PW-PAGENAME # ----------------------------------------------------------------------------------------------- # 17. If the request is for a file or directory that physically exists on the server, # then don't give control to ProcessWire, and instead load the file # ----------------------------------------------------------------------------------------------- RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteCond %{REQUEST_FILENAME} !(favicon\.ico|robots\.txt) # ----------------------------------------------------------------------------------------------- # 18. Optionally (O) prevent PW from attempting to serve images or anything in /site/assets/. # Both of these lines are optional, but can help to reduce server load. However, they # are not compatible with the $config->pagefileSecure option (if enabled) and they # may produce an Apache 404 rather than your regular 404. You may uncomment the two lines # below if you don't need to use the $config->pagefileSecure option. After uncommenting, test # a URL like domain.com/site/assets/files/test.jpg to make sure you are getting a 404 and not # your homepage. If getting your homepage, then either: do not use this option, or comment out # section #2 above that makes ProcessWire the 404 handler. # ----------------------------------------------------------------------------------------------- # RewriteCond %{REQUEST_URI} !\.(jpg|jpeg|gif|png|ico|webp|svg)$ [NC] # RewriteCond %{REQUEST_FILENAME} !(^|/)site/assets/ # ----------------------------------------------------------------------------------------------- # 19. Pass control to ProcessWire if all the above directives allow us to this point. # For regular VirtualHosts (most installs) # ----------------------------------------------------------------------------------------------- RewriteRule ^(.*)$ index.php?it=$1 [L,QSA] # ----------------------------------------------------------------------------------------------- # 20. If using VirtualDocumentRoot (500): comment out the one above and use this one instead # ----------------------------------------------------------------------------------------------- # RewriteRule ^(.*)$ /index.php?it=$1 [L,QSA] </IfModule> ################################################################################################# # END PROCESSWIRE HTACCESS DIRECTIVES #################################################################################################
  17. I couldn't find much about the pagefileSecure option. Is this just a runtime setting I can switch on or off anytime? Or is it only for files uploaded later? Is there anything changing in the files/assets folder?
  18. ProcessWire 3.0.198 contains a mixture of new features and issue resolutions. Below are details on a few of the new features: Support was added for runtime page cache groups. This enables pages to be cached as a group—and just as importantly—uncached as a group. While it can be used by anybody, it was added primarily to add efficiency to $pages->findMany(), so that it can cache supporting pages (like parents and page references). Previously, it would have to load a fresh copy of each supporting page used by findMany() results (for every returned page) since findMany() used no in-memory caching. If it did cache in memory, then you could potentially run out of memory on large result sets, so that strategy was avoided. Consider the case of iterating over all pages returned by findMany() and outputting their URLs... that triggers load of all parent pages for each page in the result set. And without a memory cache, it meant it would have to do it for each page in the results. Following this week's updates, now it can cache these supporting pages for each chunk of 250 pages, offering potentially significant performance improvement in many cases, without creating excess memory usage or memory leaks. When the chunk of 250 result pages is released from memory (to make room for the next chunk), all the supporting pages (parents, page references, etc.) are also released from memory, but not before. Now findMany() can offer both memory efficiency and great performance. For as long as I can remember, ProcessWire has had an apparently undocumented feature for dependent select fields that enables you to have two page reference fields using selects (single, multiple or AsmSelect) where the selected value in one select changes the selectable options in the other select (Ajax powered). Think of "categories" and "subcategories" selects, for example, where your selection for "categories" changes what options are selectable in "subcategories". Part of the reason it's undocumented is that it is one of those features that is a little bit fragile, and didn't work in some instances, such as within Repeater items. That changed this week, as the feature has been updated so that it can also work in Repeater items too. The $pages->findRaw() method was updated with a new "nulls" option (per Adrian's request in #1553). If you enable this new "nulls" option, it will add placeholders in the return value with null values for any fields you requested that were not present on each matching page. This reflects how PW's database works, in that if a field has no value, PW removes it from the database entirely. Without the nulls option (the existing behavior), it retains the more compact return values, which omit non present values completely. For example, consider the following: $items = $pages->findRaw("template=user, fields=email|first_name|last_name"); If a row in the result set had no "first_name" or "last_name" populated, then it would not appear in the that row of the return value at all... [ "email": "ryan@processwire.com" ] By specifying the "nulls" option, it will still include placeholders for field values not present in the database, and these will have a value of null: $items = $pages->findRaw("template=user, nulls=1, fields=email|first_name|last_name"); [ "email": "ryan@processwire.com", "first_name": null, "last_name": null ] By the way, if you don't specify which fields you want to get (which is the same as saying "get all") then adding the nulls option makes it provide placeholders for all fields used by the page's template. As you might expect, without the nulls option, it includes only populated fields. Also included in 3.0.198 are 7 issue fixes, most of which are visible in the dev branch commits log. That's all for this week. Thanks for reading this update and have a great weekend!
  19. Thx, it has already been running all the time ? Just tried without it and same issue. What about adding a $config->noTracy = true config setting that makes sure that tracy is not loaded if the flag is true? I could maybe set that on runtime and it could also be nice to have on production servers maybe?
  20. Thank you very much for your inspiring, and detailed answer @monollonom I am going to make some tests very soon, also I didn’t know the existence of inputfield-runtime-only it solves a blurry area in my view of the process!
  21. I don't know if anyone pm'd you already but it can be quite simple if you only need to generate a static svg from a text input. I found this library https://github.com/kartsims/easysvg which would allow you to do it all server-wise if you have .svg font files at hand. My approach would be to have a file input for the font file, a text input for the text you want to generate the svg from, a textarea (closed by default and non-editable) to hold the svg code, and something like https://processwire.com/modules/inputfield-runtime-only/ to echo the textarea's content (the svg) as a preview. Basically a hook in `ready.php`: require "/path/to/easySVG.php"; wire()->addHookBefore("Pages::saveReady", function(HookEvent $event) { /** @var Page $page */ $page = $event->arguments(0); if($page->template->name !== "font") return; // or whatever if(!$page->fontfile && !$page->text) return; // file and text inputs // copy/pasting from easySVG example $svg = new EasySVG(); $svg->setFontSVG($page->fontfile->url); $svg->setFontSize(100); $svg->setFontColor('#000000'); $svg->setLineHeight(1.2); $svg->setLetterSpacing(.1); $svg->setUseKerning(true); $svg->addText($page->text); list($textWidth, $textHeight) = $svg->textDimensions($page->text); $svg->addAttribute("width", $textWidth."px"); $svg->addAttribute("height", $textHeight."px"); $page->svgcode = $svg->asXML(); // textarea input $event->arguments(0, $page); }); And in your `svgpreview` field/file (check RuntimeOnly doc) or template file: echo $page->getUnformatted("svgcode"); (I used getUnformatted in case there are textformatters but it would best if there's none in the field's settings) It's not tested and made on top of my head but I think this might work. (nice to see Velvetyne here btw, I like your work and a good friend of mine made La Savate ?)
  22. Get rid of writing the $action = $input->post->option('bookmark', [ 'save', 'remove'] ); block in a loop, you do not need that. If you look at my example (made some corrections for consistency), you are capturing the action made by the user in the `$input->bookmark->action` and the id in `$input->bookmark->id` (form data built in the $.post() ajax jquery code): (I don't know what is the `$input->post->option('a', ['b', 'c'])`) thing, but we dont care) bd($input->post); // debug with tracy /** contain: * action: 'save' * bookmark: '1024' */ Then just handle the request : <?php /** threat ajax (pw) => https://processwire.com/api/ref/config/#pwapi-methods-runtime * Better to write a dedicated template which will receive the request. * That is a good candidate and exercise to try the module AppApi ? */ if($config->ajax && $input->post->bookmark) { // bd($input->post->bookmark); // debug with tracy, uncomment if you have it /** contain: * action: 'save' or 'remove' * bookmark: '1024' */ $bookmarkid = $sanitizer->int($input->post->bookmark); $action = $sanitizer->name($input->post->action); switch($action) { // which action ? case 'save': // save logic $message = 'Borat saved bookmark "'. $bookmarkid .'" ?'; $success = true; break; case 'remove': // remove logic $message = 'Borat removed bookmark "'. $bookmarkid .'" ❌'; $success = true; break; default: $message = 'error'; $success = false; } // build the response data array that will be returned in `data` from `.done(function(data) {` $json = array( 'id' => $bookmarkid, 'action' => $action, 'message' => $message, 'success' => $success ); // convert data and send as JSON header('Content-Type: text/json; charset=utf-8'); // forgot this line echo json_encode($json); return; } ?> <!-- the only one loop --> <!-- you dont really need a form --> <!-- <form action='<?=$page->url?>' method='post'> --> <?php // dummy bookmarks loop logic for the show $bookmarks = $pages->find("template=dummypage, limit=5, sort=sort"); foreach ($bookmarks as $bookmark): ?> <?php if($user->isLoggedin()): ?> <!-- show button to save and remove this page to bookmarks --> <!-- of course, make your own logic to show one or other button --> <!-- add a data attribut to each button --> <button class="button bookmark" name='bookmark' value='save' data-id='<?= $bookmark->id ?>'>Save <?= $bookmark->id ?></button> <button class="button bookmark" name='bookmark' value='remove' data-id='<?= $bookmark->id ?>'>Remove <?= $bookmark->id ?></button> <?php endif; ?> <?php endforeach; ?> <!-- </form> --> <script> jQuery(document).ready(function($) { // when clicked, send ajax request to server $('button.bookmark').on('click', function(e /* add event arg */) { var btn = $(this).hasClass('clicked') ? false : $(this).addClass('clicked'); if (!btn) { console.warn(`⚠️ Bookmark already saved to profil`); return false; } $.post('<?= $page->url /* send to the page which have the logic and return json answer */ ?>', { action: $(this).val(), bookmark: btn.data('id') }) .done(function(data) { console.log(data, "? response from ajax req"); // $(this).removeClass('clicked'); // remove `clicked` class, can be added on the foreach loop if the bookmark is already saved and then locked, or whatever.. console.info(`✅ Bookmark id => ${data.id} ${data.action} `); }); //e.preventDefault(); // you didn't need a <form>, and if you really want it, add this line to prevent default behavior and stop the redirection }); }); </script> I will make you an example to save the page, but if I were you, I would just save a "page (ID)" in a page-reference field in the profile.
  23. I don't really get what is your issue, the click that still redirect ? If yes, just add `e.preventDefault()` to your js script to prevent default behavior as your are submiting a form. <?php /** threat ajax (pw) => https://processwire.com/api/ref/config/#pwapi-methods-runtime * Better to write a dedicated template which will receive the request. * That is a good candidate and exercise to try the module AppApi ? */ if($config->ajax && $input->post) { bd($input->post->bookmark); // debug with tracy /** contain: * action: 'add' * bookmark: '1024' */ /** * you can write some logic here, eg., to grab the post data sent by the ajax request */ $bookmarkid = $sanitizer->int($input->post->bookmark); // eg. you can get a page from that $bookmarkedPage = $pages->get($bookmarkid); // send response data back $message = 'Borat added bookmark "'. $bookmarkedPage->title .'" ?'; $success = true; // build the response data array $json = array( 'message' => $message, 'success' => $success ); // convert data and send as JSON header('Content-Type: text/json; charset=utf-8'); // forgot this line echo json_encode($json); return; } ?> <?php // dummy bookmarks loop logic for the show // foreach($bookmarks as $bookmark) : $bookmark = $pages->get(1024); // forgot this line ?> <!-- you dont really need a form --> <!-- <form action='<?=$page->url?>' method='post'> --> <?php if($user->isLoggedin()): ?> <!-- show button to add this page to bookmarks --> <!-- add a data attribut to each button --> <button class="button bookmark" name='bookmark' value='add' data-id='<?= $bookmark->id ?>'>Save bm #1</button> <?php endif; ?> <?php //endforeach; ?> <!-- </form> --> <script> jQuery(document).ready(function($) { // when clicked, send ajax request to server $('button.bookmark').on('click', function(e /* add event arg */) { var btn = $(this).hasClass('clicked') ? false : $(this).addClass('clicked'); if (!btn) { console.warn(`⚠️ Bookmark already saved to profil`); return false; } $.post('<?= $page->url /* send to the page which have the logic and return json answer */ ?>', { action: $(this).val(), bookmark: btn.data('id') }) .done(function(data) { console.log(data, "? response from ajax req"); // $(this).removeClass('clicked'); // remove `clicked` class, can be added on the foreach loop if the bookmark is already saved and then locked, or whatever.. console.info(`✅ Bookmark id => ${btn.data('id')} saved to user profil`); }); //e.preventDefault(); // you didn't need a <form>, and if you really want it, uncomment this line to prevent default behavior and stop the redirection }); }); </script>
  24. Thanks for the answer @szabesz! I've read the RuntimeMarkup module thread, and @kongondo also recommends to put the actual code in a file and just wireRenderFile() it. So that must be the main difference. I also thought about the easy inclusion of js and css in Runtime only as a substantial difference. By the way, I do not read any real sarcasm in @bernhard's post. I guess it is just a his style) He is always asking himself questions and answers them. Sort of inner dialog)
  25. Building your very own custom Fieldtypes in ProcessWire seems to be hard, but it actually is not, once you get the concept. In this post I'll share a simple and as minimalistic module as it can get that everybody can use as a boilerplate for his own Fieldtypes. A Fieldtype that does not store any information in the database A Fieldtype that stores information in the database (to be done) Make your Fieldtype configurable 1. A Fieldtype that does not store any information in the database Some of you might know the excellent RuntimeMarkup module by @kongondo. I've used it a lot in the past, but I get more and more into developing custom fieldtypes rather than hacking the runtime module to my needs. That has several advantages, but it has also one major drawback: It is always a pain to setup a new Fieldtype, because there are lots of methods (see the base class /wire/core/Fieldtype.php) and you have to know which methods you need, which ones you have to remove and adopt etc.; Not any more! Ryan has developed a showcase module (the Events Fieldtype) to learn from, but when I first peaked into it, it felt quite complex and I thought it would be a lot of effort to learn Fieldtype development). There are also some easy and small Fieldtypes to learn from, like the FieldtypeCheckbox. But all of them require a good understanding of what is going on, you need to modify existing config inputfields that might have been added to that fieldtype and afterall it's not as easy as it could be. With my new approach I plan to use a boilerplate fieldtype to start from (in OOP terms to "extend" from) and only change the parts i need... More on that later. Here is the boilerplate module with some debugging info to illustrate the internal flow on the tracy console: See the module in action in the tracy console ( @adrian has done such an amazing job with that module!!!): The code should be self-explaining. What is interesting, though, is that after the ### getUnformatted ### dump there is only one call to "___formatValue". All the other calls (loadPageField, wakeupValue and sanitizeValue) are not executed because the value is already stored in memory. Only the formatting part was not done at that point. With that concept it is very easy to create your very own custom Fieldtypes: <?php namespace ProcessWire; /** * Demo Fieldtype Extending the Boilerplate Runtime Fieldtype * * @author Bernhard Baumrock, 03.10.2018 * @license Licensed under MIT * @link https://www.baumrock.com */ class FieldtypeDemo extends FieldtypeMarkup { public static function getModuleInfo() { return [ 'title' => 'Demo', 'version' => '0.0.1', 'summary' => 'Demo Fieldtype', 'icon' => 'code', ]; } /** * convert the wakeupValue to the given format * eg: convert a page object to a string */ public function ___formatValue(Page $page, Field $field, $value) { $value = parent::___formatValue($page, $field, $value) . " - but even better!"; d($value, '___formatValue --> convert to whatever format you need'); return $value; } } Notice the change "but even better" in the last two dumps. I think it can't get any easier, can it?! ? I'll continue testing and improving this module, so any comments are welcome! 2. A Fieldtype that stores information in the database See this module for an easy example of how to extend FieldtypeText:
×
×
  • Create New...