Jump to content

Leaderboard

Popular Content

Showing content with the highest reputation on 11/14/2023 in all areas

  1. It's not exactly the same technique, but I have just got into this rabbit hole and after a bit of blood sweat and tears and help from a pal, this is working for me while using ProCache and using the nonce attribute on scripts. This configuration assumes you have mod_substitute and mod_cspnonce installed. <If "%{THE_REQUEST} !~ m# /processwire/?#"> Options +Includes AddOutputFilterByType SUBSTITUTE;INCLUDES text/html Substitute "s|--CSP-NONCE--|<!--#echo var=\"CSP_NONCE\" -->|i" # Customize to your needs Header add Content-Security-Policy "default-src 'self' 'nonce-%{CSP_NONCE}e'; </If> Place this at the end of the ProcessWire htaccess directives. This should swap on the fly the Apache response and replace any --CSP-NONCE-- script. Then on Apache you can also set the nonce headers like this: I do not use this because my server setup uses nginx as reverse proxy. For example: <script nonce="--CSP-NONCE--" src="https://totally-safe-website.com"></script> Will end up as: <!-- nonce swapped on every request! --> <script nonce="0O4I3O5nNFG/MVpqormzyIuH" src="https://totally-safe-website.com"></script> @ryan fyi In theory, this would be A LOT simpler in Apache 2.5, since you could put an expression within the Substitute directive instead of the server side includes to substitute the "--CSP-NONCE--" script, but right now I'm limited to Apache 2.4 in the setup where I need this since I don't have control of the stack versions. So this should work in Apache 2.4+
    3 points
  2. Hello, Recently, while debugging something, I found I had unsolved PHP warnings. Because I'm using Twig, PHP warnings are not displayed at top of page. Not being aware that we have warnings in our code is not acceptable. ?‍♂️ So I added this code in site/init.php: set_error_handler( function (int $errorNumber, string $message, string $file, int $lineNumber): bool { wire()->log->save('php-warnings', "Fichier \"$file\", ligne $lineNumber => $message"); // Return true so PHP won't process these warnings and display them at top of page. return true; }, E_WARNING); The log: Do you think it should be integrated in PW base installation, like errors and exceptions? If you have another way of managing this warnings, don't hesitate to share. ?
    3 points
  3. This week we'll take a look at a newly released module that enables you to lock page fields or properties from editing on a page-by-page basis. Originally planned as a core feature, I found it worked just as well built as a focused module, so here it is. This post serves as both an introduction to, as well as documentation for this new module— https://processwire.com/blog/posts/page-edit-lock-fields/
    2 points
  4. Oh, what's this? Another completely different site borked itself? Performed an update using an automated tool through the web host where the core and all plugins are updated at the same time. Is there a right way to update WordPress? The world may never know... This update has has rendered the Admin unusable and inaccessible. Time for a server rollback! Okay! We're back... oh boy... Did you know? Deactivating plugins in the wrong order can make the plugins page hang on a redirect loop every time you try and access it. Time for a server rollback! (Before someone says "that was a caching issue", no it wasn't) Okay! We're back... oh boy... Did you know? In WordPress, a commercial theme or plugin you've purchased and installed may install other commercial plugins that require their own license. When that license expires, you may have to pay for a separate license for the plugin that the plugin/theme you already purchased installed when it comes time to upgrade. PROTIP: the commercial plugins that were installed by the theme or plugin you purchased are not easily recognized as a dependency or indicate what plugin/theme installed it- so, when working on WordPress, keep a shovel handy in case you have to start digging!
    2 points
  5. Also, your cache function only works as expected if the $nameid variable is the same every time you use a cached version of the query. To get around this, you should make the cache key depend on it, e.g.: $name = $cache->get("cachedname-$nameid", "+2 minutes", function($pages) use($nameid) { return $pages->find("template=names, parent=1195, id=$nameid, status=published, limit=3"); });
    2 points
  6. This tutorial is about making the Latte template engine available in ProcessWire template files via the use of ProcessWires Custom Page Classes. Based on some inspiring posts from @bernhard, I wanted to use custom page classes and Latte templates in my own projects. I know there are modules like RockFrontend and others available to integrate template engines like Latte or Twig . However I wanted to start with a small, flexible and reusable code base as playground for my next projects without module dependencies as starting point. Please note: The template part of this tutorial assumes you are using the Site profile Default profile (standard/intermediate edition), which can be downloaded from the ProcessWire Github repository. Here a technique called delayed output strategy is used. I also posted an alternative Latte implementation some posts below, using a normal class file for implementing Latte via your template _init.php file. Step 1: Enable ProcessWire Custom Page Classes PageClasses were added with ProcessWire 3.0.152. To use page classes open site/config.php and enable the feature if needed via $config->usePageClasses = true;. After that create the folder site/classes in your site installation and add the PHP file DefaultPage.php inside with the following content for now. <?php namespace ProcessWire; class DefaultPage extends Page {} The DefaultPage class is used whenever a ProcessWire template like site/templates/your-template.php has no page class associated with it. In other words, if no site/classes/YourTemplatePage.php exists, the default page class site/classes/DefaultPage.php is used as fallback. Step 2: Install PHP Dependency Manager Composer As we are using the Latte Composer package, we first need to install Composer on our computer if not yet available. Visit the Composer webiste and install Composer on your system. On Windows just grab the Composer-Setup.exe file and execute it. Once Composer is installed, open a new terminal and type the command composer. You should see some Composer output in your console. If so, proceed with Step 3. If an error occurs, please read and follow the Docs on the Composer website to get Composer installed on your system before proceeding with this tutorial. Step 3: Install Latte Composer Package In the next step, we install the Latte template engine files as Composer package in our site folder. Open a terminal and switch into your local site/classes folder created in Step 1. From inside the site/classes folder type the following command into your terminal composer require latte/latte. Please note: If your installation has a composer.json file in the root, you will get asked if you want to use the composer.json in the root. As this tutorial assumes to install the composer files inside our site/classes folder, you MUST answer no. Otherwise the Latte files will be installed inside /root/vendor not /root/site/classes/vendor as expected in this tutorial. Once done, your site classes folder should contain a vendor folder with the Latte composer files, the two Composer files composer.json and composer.lock and our DefaultPage.php file created in Step 1. site/ classes/ vendor/ bin/ composer/ latte/ autoload.php composer.json composer.lock DefaultPage.php (implements Latte logic inherited by other PageClasses) EventsPage.php (implements logic for /templates/events.php) // Note this part is setup and explained later in this tutorial templates/ views/ events.latte your-template.php (associated with fallback classes/DefaultPage.php) events.php (associated with classes/EventsPage.php) Please note: The files located under site/templates are explained later in this tutorial. I just thought it would be a good idea to show the relevant file structure at one spot. Step 4: Make Latte available from PageClasses Now we need to make Latte available in our custom PageClasses. Open site/classes/DefaultPage.php and change the code as follows: <?php namespace ProcessWire; /** * The DefaultPage class implements methods to render Latte templates as string. This page class serves as * fallback for PW templates inside site/templates not implementing their own page class inside site/classes. */ class DefaultPage extends Page { protected static \Latte\Engine|null $latte = null; private array $latteConfig = []; // Constructor. public function __construct(Template $tpl = null) { parent::__construct($tpl); $this->setupLatteEngine(); $this->setLatteConfig(); } // Set or update Latte configuration. public function setLatteConfig(array $config = []): void { if (!isset(self::$latte)) return; // Remove keys from user config not available in Latte defaults, except for 'pwAPI'. $config = array_intersect_key($config, $this->getLatteDefaultConfig()); // Merge user config with Latte defaults. $this->latteConfig = array_merge($this->getLatteDefaultConfig(), $config); // Apply updated configuration to actual Latte instance. self::$latte->setAutoRefresh($this->latteConfig['autoRefresh']); self::$latte->setTempDirectory($this->latteConfig['cacheFolder']); } // Render given Latte template as string, injecting ProcessWire API and $data. public function renderTemplate(string $template, array $data = []): string { if (!isset(self::$latte) || !$this->latteConfig) return ''; // Ensure Latte template exists within specified template folder. $latteTemplateFolder = realpath($this->latteConfig['latteTemplateFolder']); $templatePath = realpath($latteTemplateFolder . '/' . $template); if ($templatePath === false || stripos($templatePath, $latteTemplateFolder) !== 0) { throw new \Exception("Latte template '$template' not found or outside 'latteTemplateFolder'."); } // Render Latte template and inject ProcessWire API and $data. return self::$latte->renderToString($templatePath, array_merge($data, $this->latteConfig['pwAPI'])); } // Returns associative array with Latte default configuration. protected function getLatteDefaultConfig(): array { return [ 'autoRefresh' => true, 'cacheFolder' => $this->wire('config')->paths->cache . 'Latte/', 'latteTemplateFolder' => $this->wire('config')->paths->templates, 'pwAPI' => [ 'page' => $this, 'pages' => $this->wire('pages'), 'config' => $this->wire('config'), ], ]; } // Initiate Latte template engine. private function setupLatteEngine(): void { if (isset(self::$latte)) return; require_once "vendor/autoload.php"; self::$latte = new \Latte\Engine; } } The DefaultPage class assumes the Latte template files to be stored inside path site/templates/. By default we can access the PW-API $page, $pages and $config inside our Latte templates. You can add more ProcessWire APIs like $sanitizer or template specific data via the setLatteConfig() method or via the optional associative array $data of the renderTemplate() method. Assume we have another PageClass in site/classes/EventsPage.php, which implements the complex method hello(). To make the Latte render methods available in the EventsPage class, we need to extend from our DefaultPage class instead of the ProcessWire Page class. This way we can create as many page classes with logic for specific templates as needed, while keeping code structured and organized. <?php namespace ProcessWire; class EventsPage extends DefaultPage { public function hello() { return "Hello World!"; } } Step 5: Add Latte template (acting as View) Next we add a Latte template file to site/templates/views/events.latte with the following content. <div> <h2>{$page->title|capitalize}</h2> <p>PageId: {$page->id}</p> <p n:if="$config->useFunctionsAPI"> Using Functions API and Latte is fun. </p> {* This will be translated based on the default PW language or users language preference *} <p>{$lang['Please select a date']}</p> <ul n:if="$page"> <li n:foreach="$page->getFields() as $field"> $page->{$field}: {$page->$field} </li> </ul> </div> Step 6: Render Latte template from ProcessWire templates (acting as Controller) The code below shows how custom methods and the Latte render method can be invoked within normal ProcessWire templates like site/templates/events.php. The first code line appends the output from the method hello() implemented in the associated PageClass site/classes/EventsPage.php to our template $output variable. Please note: In this example the ProcessWire template file uses a technique called Delayed Output Strategy. You can read more about this strategy in the linked ProcessWire blog post from Ryan. <?php namespace ProcessWire; /** * Template: "events.php" * Using Processwire delayed output strategy * https://processwire.com/docs/tutorials/how-to-structure-your-template-files/ */ // Append output from method hello() implemented in `site/classes/EventPage.php`. $content .= $page->hello(); // Add needed translations here. You can access them via $lang inside your Latte template. // The translation for 'Please select a date' can be added via the ProcessWire backend. // For details see https://processwire.com/docs/multi-language-support/code-i18n/. $data = ['lang' => [ 'Please select a date' => __('Please select a date!'), ], ]; // Append output of rendered Latte template located in `site/templates/views/events.latte`. $content .= $page->renderTemplate('views/events.latte', $data); At the bottom of our template file site/templates/events.php we call the render method implemented in site/classes/DefaultPage.php to render the specified Latte template in site/templates/views/events.latte. The ProcessWire API $page, $pages, $config is available in all Latte templates by default. As the EventsPage class extends the DefaultPage class, it inherents it's render methods This way we need to implement the Latte template code once, but can use it in all our custom PageClasses and default ProcessWire template files located in /site/templates. Note: If you are using the output strategy Markup regions, the Events template file would look as follows: <?php namespace ProcessWire; /** * Template: "events.php" * Using Processwire delayed output strategy * https://processwire.com/docs/tutorials/how-to-structure-your-template-files/ */ // Append output from method hello() implemented in `site/classes/EventPage.php`. // Add needed translations here. You can access them via $lang inside your Latte template. // The translation for 'Please select a date' can be added via the ProcessWire backend. // For details see https://processwire.com/docs/multi-language-support/code-i18n/. $data = [ 'lang' => [ 'Please select a date' => __('Please select a date!'), ], ]; ?> <div id="content"> <?= $page->hello(); ?> <?= $page->renderTemplate('views/events.latte', $data); ?> </div> Have fun. cwsoft Please note: Edited the initial post to include the code suggestions from member @d'Hinnisdaël some posts below. Thanks for your suggestions.
    1 point
  7. Hey @bernhard, Not urgent, but I discovered a slight glitch when trying to delete a block from the dropdown on the module config page, which appears to be down to the backslash notation in Windows (I develop locally on Windows). The getFiles() method of Block.php doesn't find any matching files on a Windows setup. The path returned by the dirname($file) call is returning something like this: 'D:\laragon\www\sitename\site\templates\RockPageBuilder\blocks_general\IanTest/IanTest' whereas the wire->files->find->($dir) returns file paths corrected for backslashes like this: 'D:/laragon/www/sitename/site/templates/RockPageBuilder/blocks_general/IanTest/IanTest.view.php' If I correct the backslashes as follows on line 340, it works just fine: $dir = str_replace("\\", "/", dirname($file)); No rush - deleting the block files manually and doing a modules refresh works in the meantime. Cheers, Ian.
    1 point
  8. Some reporter had the incredible chance to capture a team of developers successfully completing a full WordPress update. A very rare and intense footage: (reading your messages, this is how I imagine WordPress ? )
    1 point
  9. Well, best practice would be to make sure that your api request times out before your PHP script does (WireHttp::setTimeout / curl_setopt(CURLOPT_TIMEOUT, xx) / curl_setopt(CURLOPT_CONNECTTIMEOUT, yy) vs. max_execution_time & site_time_limit). You can of course set the header through .htaccess instead of PHP (mod_headers must be enabled of course): Header always set Access-Control-Allow-Origin: "*" The keyword "always" also sends the header with redirects.
    1 point
  10. JSYK, this issue has been fixed by Ryan. His preferred fix also addresses when the #login_start gets stale via cache which is important. Issue #1839 corrects ProcessLogin refresh issue when client and server have different times for UTC/GMT. The fix has been pushed to the current dev branch as of today for continued testing.
    1 point
  11. I would assume the $nameid variable is not available in your closure unless you add use ($nameid) to include it. $pages is available only because WireCache supports optionally passing API variables as arguments. This should work: $name = $cache->get('cachedname', "+2 minutes", function($pages) use($nameid) { return $pages->find("template=names, parent=1195, id=$nameid, status=published, limit=3"); }); Here is some documentation about closures: PHP: Anonymous functions - Manual
    1 point
  12. Here you go: https://www.baumrock.com/en/processwire/modules/rockpagebuilder/docs/fields-access/ Accessing Fields All fields of your block will be available to your $block variable just like usual: echo $block->your_field_name; echo $block->edit('your_field_name'); Method Syntax RockPageBuilder comes with a helper to make working with long fieldnames easier and less verbose. The reason for that is that when I create new blocks I always prefix all fields with the block template name. That way I can be sure that when I copy that block to another project the fieldnames don't collide with existing fields. The problem here is that we end up with long fieldnames like this: rockcommerce_invoice_pdf. When using RockPageBuilder you can do this: echo $block->pdf(); This feature is actually a feature of RockMigrations' MagicPages module. You can also pass parameters to request a different state of the field: $block->pdf(); // frontend editable field $block->pdf(1); // using ->getFormatted() $block->pdf(2); // using ->getUnformatted() Note that the method call has to be the last part of the string after the final underscore: // field foo_bar_baz $block->baz() // field foo_something $block->something()
    1 point
  13. Ah yes sorry for that - I see it's not in the docs and I'll add that asap ?
    1 point
  14. Thanks @bernhard, that's a good start! It seems that $block->edit('something') does work for those (textarea/tinyMCE) fields, but $block->something() doesn't. Yet it works for plain text fields. I can see in the source that calling $block->something() for a textarea field doesn't output any of the frontend stuff (pw-editcopy, contenteditable etc.). I expect this isn't a RockPageBuilder problem but something else; I just haven't figured it out yet. At least I can get it to work with the more verbose version. I did touch the frontend edit module settings briefly, but only after I encountered this problem; I thought maybe I had to switch on editing for those fields; that didn't make any difference so I turned it off again. If I can diagnose the problem I'll post back with further details. I'm running PW 3.0.229, RPB 4.7.2, PHP 8.2
    1 point
  15. Thanks for the quick answer. No IDE. I am old, and too old for that ? Works now!
    1 point
  16. That's a namespace issue. Your IDE should tell you that. Just add this on top of your ready.php file: use RockPageBuilder\Block; use RockPageBuilder\BlockSettingsArray; I've updated the docs ? Thx
    1 point
  17. I also experienced this issue. Following on netcarver's advice above, it turns out that my device clock is ahead by over 5 minutes. I adjusted my device time & it worked but thought a broader solution might be better. This fixed my issue. var startTime = parseInt($('#login_start').val()); // GMT/UTC // var maxSeconds = 300; // max age for login form before refreshing it (300=5min) var clientStartTime = Math.floor(new Date().getTime() / 1000); var clockDifference = clientStartTime - startTime; var maxSeconds = 300 + clockDifference; But I prefer it be done in the core, so I opened an issue in github.
    1 point
  18. Does this help? https://processwire.com/blog/posts/processwire-3.0.107-core-updates/#trash-for-all Found here:
    1 point
  19. This week ProcessWire gained powerful new tools for finding pages and controlling how they are loaded. If you like to maximize performance and efficiency, you’ll like what 3.0.172 brings— https://processwire.com/blog/posts/find-faster-and-more-efficiently/
    1 point
  20. 2021 update: bit easier, better security, no warnings about using <meta http-equiv="Content-Security-Policy">. Rather than rely on <meta http-equiv="Content-Security-Policy"> I have tweaked things a little. Bit more secure, bit easier to do and added nonce function as well to further lock down script-src. If we remove all CSP from htaccess we can define everything in one place and set header() with php instead, right before <!DOCTYPE html> I find it cleaner and easier to customize that way. Allows as much logic and conditional loading as you need. For example: <?php if ($user->isGuest()): $loadMap = $page->name === "map"; $hasMapImgs = ($loadMap) ? " https://*.googleapis.com https://maps.gstatic.com https://*.ggpht.com" : ""; $hasMapStyles = ($loadMap) ? " 'unsafe-inline' https://fonts.googleapis.com" : ""; $cspImgSrc = " img-src 'self' https://www.google-analytics.com data: 'self'" . $hasMapImgs . ";"; $cspStyleSrc = " style-src 'self'" . $hasMapStyles . ";"; $cspCond = $cspImgSrc.$cspStyleSrc; function generateRandomString($length = 25) { $characters = '0123456789'; $charactersLength = strlen($characters); $randomString = ''; for ($i = 0; $i < $length; $i++) { $randomString .= $characters[rand(0, $charactersLength - 1)]; } return $randomString; } $nonce = generateRandomString(35); $csp = "Content-Security-Policy: base-uri 'self'; frame-ancestors 'self'; default-src 'none'; child-src 'self'; manifest-src 'self'; form-action 'self'; connect-src 'self' https://www.google-analytics.com https://maps.googleapis.com; font-src 'self' data: https://fonts.gstatic.com; frame-src 'self' https://www.youtube.com; script-src 'self' https: 'unsafe-inline' 'nonce-" . $nonce . "' 'strict-dynamic';" . $cspCond ; header($csp); ?> <?php endif; ?> <!DOCTYPE html> https://observatory.mozilla.org/analyze/www.alfresco-bar-bistro.com.au
    1 point
×
×
  • Create New...