-
Posts
21 -
Joined
-
Last visited
-
Days Won
2
Posts posted by jean-luc
-
-
On 23/11/2017 at 1:46 PM, houseofdeadleg said:
After a bit of further work, I'm now getting a parse error – Parse Error: syntax error, unexpected '[', expecting ')' (line 284 of /var/www/xxxxxx/site/modules/MarkupTwitterFeed/MarkupTwitterFeed.module)
The line in question is –
$linkHtml = str_replace(['{href}', '{url}'], [wire('sanitizer')->entities($u['expanded_url']), $u['display_url']], $options['listItemLink']);
@houseofdeadleg So far, no idea of what could be wrong. Can you open an issue in https://github.com/jlj/MarkupTwitterFeed/issues, so we can investigate it further without adding too many message to this topic?
(Please describe your configuration (version of PW, of php, …) and include a fragment of the template code in your site that shows the problem)
-
3 hours ago, erikvanberkum said:
I have installed your fork on Processwire 3.0.42 and get the following error:
Parse error: syntax error, unexpected 'site' (T_STRING), expecting ',' or ')'
File: .../modules/MarkupTwitterFeed/tmhOAuth/tmhOAuth.php:14
Any ideas how to get around this? I upgrade your module over the one from Ryan.
14: defined('\ProcessWire\wire("config")->paths->root . 'site/modules/MarkupTwitterFeed/tmhOAuth'') or define('\ProcessWire\wire("config")->paths->root . 'site/modules/MarkupTwitterFeed/tmhOAuth'', dirname(\ProcessWire\wire("config")->paths->root . 'site/modules/MarkupTwitterFeed/tmhOAuth/tmhOAuth.php'));
I have not tried the MarkupTwitterFeed module with Processwire 3, but I can tell what caused the error here:
Actually the original tmhOAuth.php:14 is:
defined('__DIR__') or define('__DIR__', dirname(__FILE__));
This code makes the (visibly wrong here) assumption that PHP magic constant __DIR__ is not expanded when placed between single quotes. Here, __DIR__ was replaced by its value (a PHP expression) at compile time, and the surrounding quotes around it cause a syntax error.
To solve this, you can simply comment or remove line 14 in tmhOAuth.php. Doing so which should be perfectly safe because __DIR__ is defined (probably by processwire), and so the check is not needed.
[Edit] __DIR__ is available since PHP 5.3.0, so with Processwire 3.x requiring PHP version 5.3.8 or higher, it is actually safe to delete line 14 of tmhOAuth.php.
Hope this helps.
-
22 minutes ago, netcarver said:
@jean-luc Nice. Any chance of issuing your updates as a pull request to the original repo?
Already done that (https://github.com/ryancramerdesign/MarkupTwitterFeed/pull/5), but the previous pull request I did for this same module in January 2015 has stayed unanswered, so I don't know if this one will be integrated…
-
2
-
-
Hello,
If you use this module, you may be interested by my fork https://github.com/jlj/MarkupTwitterFeed, that adds the following features to the official MarkupTweeterFeed module:
- support of the recent Twitter extended tweet format (tweets longer than 140 chars);
- support of emoji characters in tweets;
- display of image in a tweet (only the first image is displayed in the curent version);
- display of video in a tweet (with a html5 video markup);
- for retweets, display of the original author and avatar in the tweet.
Also, it allows a more flexible per-item rendering, as in the following example:
$t = $modules->get('MarkupTwitterFeed'); $t->limit = 10; $renderOptions = array( 'showDate' => 'before, after', 'dateFormat' => __("M j, Y"), // Tweet block date format 'listItemOpen' => "\n\t<div class='tweet-item'>", 'listItemClose' => "</div>", 'listItemText' => "<i class='fa fa-twitter'></i> <span class='tweet-text'>{text}</span>", 'listItemDate' => " <div class='date'><i class='fa fa-calendar'></i> {date}</div>", 'listItemLink' => "<a rel='nofollow' href='{href}'>{url}</a>", 'videoAttributes' => "autoplay loop muted", 'preserveLineBreaks' => 'coalesce', ); foreach($t as $tweet) { echo $t->renderItem($tweet, $renderOptions); }
Examples from my website:
This module has been tested with pw 2.7.2 only, so I cannot guarantee the proper working with 3.0…
Hope this will be useful for others here.
-
8
-
I just made a few edits in the tutorial to improve clarity and fix typos...
-
Docker (http://www.docker.com) is an open platform for building, shipping and running distributed applications.Docker containers are a great way to package a complete application with its specific dependencies in a portable way so that it can easily be deployed on any compatible network or cloud infrastructure.Recently I spent a few days making my ProcessWire site run in a Docker container, and - as I could not find any good tutorial for this - it sounded like a good idea to write one.You will find on the web plenty of presentations and tutorials about Docker, so I won't start with the basic concepts, and this tuto assumes that you have a first understanding of Docker's fundamentals.What we want to do here is to migrate an existing site to a set of docker containers.Therefore, to start with, you should have:- docker installed on your computer;- the site directory of your ProcessWIre site- a backup of your site's MySQL databaseLet's start.Create a docker container for the site databaseFor several reasons (insulation, security, scalability), it is preferable to host the site database in a separate docker container.1. Set-up a SQL database with MariaDb or MySQL
$ docker run --name database -e MYSQL_ROOT_PASSWORD=rootdbpassword -d mariadb
Here I choose to use the MariaDB official container in its latest version, but MySQLwould be just fine as well.2. Run a PhpMyAdmin container and create the ProcessWire databaseWe first select an simple image with PhpMyAdmin on the Docker Hub: nazarpc/phpmyadmin and we create a docker container based on this image. This container will access the port exposed by the database container via a private networking interface. We specify this with the `--link` option.It can be run temporarily (and exited by ctrl-C):docker run --rm --link database:mysql -p 8881:80 nazarpc/phpmyadmin
Or it can be run as a daemon in the background:docker run -d --name phpmyadmin --link database:mysql -p 8881:80 nazarpc/phpmyadmin
From phpmyadmin (accessed from your browser at http://hostaddress:8881) you can now create your ProcessWire database, create a dedicated user for it, and import the database content from a previously saved SQL file.Note: alternatively, you can do all database operations from the command line in the database docker container created during step 1, or use another mysql user interface container if you prefer…3. Update the database parameters in your site configurationIn your site's `config.php` file, the sql server name shall be set to `mysql`:$config->dbHost = 'mysql';
Other `$config->dbXxx` settings shall match the database name, user and password of the just-created database.Create a Docker Image for Apache, PHP and the Processwire site1. Create an image-specific directory with the following contents and `cd` to itbash-3.2$ ls -l . config .: total 16 -rw-rw-rw- 1 jean-luc staff 1163 21 aoû 12:09 Dockerfile drwxr-xr-x 17 jean-luc staff 578 17 aoû 12:48 ProcessWire drwxr-xr-x 7 jean-luc staff 238 21 aoû 12:07 config drwxr-xr-x 7 jean-luc staff 238 20 aoû 18:46 site config: total 160 -rw-rw-rw- 1 jean-luc staff 160 20 aoû 18:28 msmtprc -rw-rw-rw- 1 jean-luc staff 72518 20 aoû 18:56 php.ini
where:-
`ProcessWire` contains the version of ProcessWire that we want to use for this site;
It can be retrieved from github with a link like https://github.com/ryancramerdesign/ProcessWire/archive/{version}.zip`
For example, the 2.6.13 dev version can be obtained by the link https://github.com/ryancramerdesign/ProcessWire/archive/7d37db8d6b4ca6a132e50aff496a70e48fcd2284.zip - `site`: our site-specific files
- `Dockerfile`: the dockerfile for building the image (see below)
- `config`: a directory containing specific configuration files copied to the docker image (see below)
2. Set the `Dockerfile` contentFROM php:5.6-apache RUN apt-get update \ && apt-get install -y libfreetype6-dev libjpeg62-turbo-dev libmcrypt-dev libpng12-dev zziplib-bin msmtp\ && a2enmod rewrite \ && a2enmod ssl \ && docker-php-ext-install mysqli pdo_mysql iconv mcrypt zip \ && docker-php-ext-configure gd --with-freetype-dir=/usr/include/ --with-jpeg-dir=/usr/include/ \ && docker-php-ext-install gd EXPOSE 80 EXPOSE 443 # Add a specific php.ini file COPY config/php.ini /usr/local/etc/php/ # Configure the mail sent utility msmtp (http://msmtp.sourceforge.net) and make it readable only by www-data COPY config/msmtprc /usr/local/etc/php/ RUN chmod 600 /usr/local/etc/php/msmtprc \ && chown www-data:www-data /usr/local/etc/php/msmtprc # Remove all default site files in /var/www/html RUN rm -fR /var/www/html/* # Copy ProcessWire core files COPY ProcessWire/wire /var/www/html/wire COPY ProcessWire/index.php /var/www/html/index.php COPY ProcessWire/htaccess.txt /var/www/html/.htaccess # Copy site-specific files COPY site /var/www/html/site # Make www-data the owner of site-specific files RUN chown -R www-data:www-data /var/www/html/site VOLUME /var/www/html/site
Based on the official image `php:5.6-apache`, it installs missing packages to the system, adds mod-rewrite and mod-ssl to Apache, plus a number of PHP modules needed by Processwire (core or modules): mysqli, pdo_mysql, iconv, mcrypt, zip, and gd.Then it copies the site files to the location expected by the Apache server. Finally it declares a Docker volume `/var/www/html/site` (i.e. the site files and assets), so that it can be shared with other containers.3. Set the msmtp configurationWe need to configure a sendmail utility, so that we can send emails from php, for example when a user registers on the website. The simplest way to do it is to rely on an external smtp server to do the actual sending. That's why we use msmtp.- define the desired smtp account in `config/msmtprc`account celedev-webmaster tls on tls_certcheck off auth on host smtp.celedev.com port 587 user webmaster@celedev.com from webmaster@celedev.com password thepasswordofwebmasteratceledevdotcom
- in `config/php.ini`, configure the sendmail command so it uses msmtp:sendmail_path = /usr/bin/msmtp -C /usr/local/etc/php/msmtprc --logfile /var/log/msmtp.log -a celedev-webmaster -t
4. Build the Docker imagedocker build -t php-5.6-pw-celedev .
5. Create a Data-only container for the site filesdocker run --name celedev-data php-5.6-pw-celedev echo "Celedev site data-only container"
6. Run the web server containerdocker run --name celedev-site -p 8088:80 --link database:mysql --volumes-from celedev-data -d php-5.6-pw-celedev
Note that this container is linked to our database and shares the 'celedev-data' volume created previouslyDuring development, it can be convenient to keep an access to the host file system from the container. For this, we can add a shared volume to the previous command:docker run --name celedev-site -p 8088:80 --link database:mysql -v /Users/jean-luc/web/test-docker:/hostdir --volumes-from celedev-data -d php-5.6-pw-celedev
Our ProcessWire website is now up and running and we can test it in our browser at http://hostaddress:8088. Great!What we now have in Dockerbash-3.2$ docker images REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE php-5.6-pw-celedev latest 2aaeb241c2e2 3 hours ago 1.149 GB nazarpc/phpmyadmin latest e25cd4fd48b3 8 days ago 521 MB mariadb latest dd208bafcc33 2 weeks ago 302.2 MB debian latest 9a61b6b1315e 5 weeks ago 125.2 MB bash-3.2$ docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 68cc5d976f0d php-5.6-pw-celedev "apache2-foreground" 20 hours ago Up 20 hours 443/tcp, 0.0.0.0:8088->80/tcp celedev-site 0729fe6d6752 php-5.6-pw-celedev "echo 'Celedev site d" 20 hours ago Exited (0) 20 hours ago celedev-data e3e9e3a4715c mariadb "/docker-entrypoint.s" 3 days ago Up 3 days 3306/tcp database
Saving the site dataWe can create an archive of the site files by running a tar command in a dedicated container:bash-3.2$ docker run --rm -it --volumes-from celedev-data -v /Users/jean-luc/web/test-docker:/hostdir debian /bin/bash root@2973c5af3eaf:/# cd /var/www/html/ root@2973c5af3eaf:/var/www/html# tar cvf /hostdir/backup.tar site root@2973c5af3eaf:exit bash-3.2$
Tagging and archiving the Docker imageWe can also add a tag to the docker image that we have created in step 4 (recommended):bash-3.2$ docker tag 2aaeb241c2e2 php-5.6-pw-celedev:0.11 bash-3.2$ docker images REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE php-5.6-pw-celedev latest 2aaeb241c2e2 3 hours ago 1.149 GB php-5.6-pw-celedev 0.11 2aaeb241c2e2 3 hours ago 1.149 GB nazarpc/phpmyadmin latest e25cd4fd48b3 8 days ago 521 MB mariadb latest dd208bafcc33 2 weeks ago 302.2 MB
And we can archive this image locally if we dont want to push it now to the Docker Hub:bash-3.2$ docker save php-5.6-pw-celedev:0.11 | gzip > php-5.6-pw-celedev-0.11.tar.gz
And that's it!
You now have a portable image of your ProcessWire website that you can run directly on any docker-compatible system.
-
19
-
`ProcessWire` contains the version of ProcessWire that we want to use for this site;
-
About handling the display of images from a twitter feed, I did this today by adding code inside MarkupTwitterFeed.module. I post it here in case it could help other pw users having the same needs.
// To be added in public function renderItem, before: $out = $options['listItemOpen']; if (isset($item['entities']['media'])) { $mediaUrlField = wire('config')->https ? 'media_url_https' : 'media_url'; foreach($item['entities']['media'] as $m) { if($options['listItemPhoto']) { $photoHtml = str_replace(array('{src}', '{title}'), array(wire('sanitizer')->entities($m[$mediaUrlField]), wire('sanitizer')->entities($m['display_url'])), $options['listItemPhoto']); $text = preg_replace('!' . preg_quote($m['url'], '!') . '(\b|$)!i', $photoHtml, $text); } else { // Hide the media reference in the text $text = preg_replace('!' . preg_quote($m['url'], '!') . '(\b|$)!i', '', $text); } } }
where $options['listItemPhoto'] contains the html for a Twitter photo. For example, if you simply want to display the image, you can set it to:
$options['listItemPhoto'] = "<img src='{src}' title='{title}'>";
This will be rendered like in this screenshot:
-
4
-
-
how to call image twitter use module Markup Twitter Feed ?? please help me
If your question, it is about how to display images in tweets, the default module markup does not handle images.
But that is not hard to do: you have to use a foreach loop to iterate over the tweets and create your own markup inside the loop, like in the example in the readme.
$t = $modules->get('MarkupTwitterFeed'); $t->limit = 3; foreach($t as $item) { echo "<p>$item[text]<br /><span class='date'>$item[created_at]</span></p>"; }
For the internal structure of twitter media, you can have a look at https://dev.twitter.com/overview/api/entities-in-twitter-objects
-
1
-
-
Interesting. But then you're logged out?
No, you stay logged in.
Actually, session_write_close() does not close the current session, but only "End the current session and store session data." (see http://fr2.php.net/manual/en/function.session-write-close.php)
-
Using wireSendFile() for flexible download is great to mask the real file location on the server or to select the file according to the language, but it comes with an issue that appears for large files or slow connections: until the download completes, any other access to the website from the same session is blocked.
For a user viewing a page that contains such a download link, this means that when he clicks on the download link, the download starts as expected, but until the download completes, the user can not navigate to a different page of the site, which is probably not the expected behavior.
The solution to avoid this issue is to call session_write_close() before wireSendFile(), like this:
$options = array( // boolean: halt program execution after file send 'exit' => true, // boolean|null: whether file should force download (null=let content-type header decide) 'forceDownload' => true, // string: filename you want the download to show on the user's computer, or blank to use existing. 'downloadFilename' => $clientFilename, ); // Close the php session, so wireSendFile doesn't block other connections from the same session while downloading session_write_close(); wireSendFile($serverFilePath, $options);
This should not have any unwanted effect as long as the 'exit' option is set to true (because wireSendFile() calls exit() just after readfile() in this case).
Note: The issue comes from PHP's session management, where "session data is locked to prevent concurrent writes only one script may operate on a session at any time" (as says PHP session_write_close documentation ). A google search "php readfile session_write_close" brings many exemple of this lock problem.
-
3
-
-
I have a problem with $page->localUrl() throwing error at 404 page:
Fatal error: Exception: Method Page::localUrl does not exist or is not callable in this context (in /Users/Roope/Sites/demo/wire/core/Wire.php line 320) #0 /Users/Roope/Sites/demo/site/templates/inc/header.php(61): Wire->__call('localUrl', Array) #1 /Users/Roope/Sites/demo/site/templates/inc/header.php(61): Page->localUrl(Object(Language)) #2 /Users/Roope/Sites/demo/site/templates/basic.php(1): include('/Users/Roope/Si...') #3 /Users/Roope/Sites/demo/wire/core/TemplateFile.php(139): require('/Users/Roope/Si...') #4 [internal function]: TemplateFile->___render() #5 /Users/Roope/Sites/demo/wire/core/Wire.php(359): call_user_func_array(Array, Array) #6 /Users/Roope/Sites/demo/wire/core/Wire.php(317): Wire->runHooks('render', Array) #7 /Users/Roope/Sites/demo/wire/m in /Users/Roope/Sites/demo/index.php on line 214
In header.php around line 61 I have language switcher code borrowed from previous page:
<?php echo '<ul>'; foreach($languages as $language) { if(!$page->viewable($language)) continue; // check if page not published for this language $class = "$language" == "$user->language" ? ' class="active"' : ''; $url = $page->localUrl($language); echo '<li'.$class.'><a href="'.$url.'">'.$language->title.'</a></li>'; } echo '</ul>';
I updated PW to latest dev but that didn't have any affect on this one. Besides 404 error, everything else runs smoothly. Any thoughts?
I have regularly similar errors here in my site with a language switcher in the shared header, and, after analyzing the site logs, it turns out that these errors occur only when robots (googlebot, baidu…) are visiting the site and try to get non existing files.
I finally succeeded to reproduce the error systematically by entering urls in the /site directory of he site, like for example /site/aaaaa.
So my (maybe naive) guess is that when PW tries to serve a real file and doesn't find it, the /http404/ page does not have the language url hooks installed, hence the exception.
For the record, the simple workaround that I implemented just consists in catching WireExceptions when rendering the language switcher, like this:
<ul class="dropdown-menu"> <?php foreach ($languages as $language) { // if this page isn't viewable (active) for the language, skip it if (!$page->viewable($language)) continue; // Add the language to the menu if ($user->language->id == $language->id) echo "<li class='disabled ticked'><a>{$language->title}</a></li>"; else { try { echo "<li><a href='{$page->localUrl($language)}'>{$language->title}</a></li>"; } catch(WireException $e) {} } } ?> </ul>
This has the advantage that it keeps the simplicity of the call to $page->localUrl($language), and it makes the robots happy by returning a 404 error as expected.
-
Finally I'm not sure about the best way of handling this hanna-code-inside-reapeater-field case…
On one hand, a naive hanna code writer like myself would expect $page to refer to the user-visible field container page;
On the other hand, having a reference to the current "repeater page" can be useful in many cases, like this one:Something I've been wondering about: How would I go about accessing specific repeater fields with the Hanna Code.
[...]
If Text, Date and Price where just page fields, this would be pretty obvious. However for them being fields inside a repeater I think there's something like the jQuery siblings selector needed, but I don't see anything like that in the api documentation.
Any input on this would be very much appreciated
Maybe a solution would be to have two different PHP variables accessible from within the hanna code, where the one referring to the repeater page would be null if the current field is not part of a repeater.
Easy to implement, but, as always, not so easy to keep it consistent with the rest of the PW API.
Ryan, your opinion about this? -
@slkwrm
Thanks for the hint.
Actually it turns out that the repeater page's parent is not the page where the repeater field is, but this page can be got by calling the method getForPage. So the correct code is:$containerPage = $page; if ($page instanceof RepeaterPage) { $containerPage = $page->getForPage(); } // can be checked with echo $containerPage->id;
I added this container page calculation at the right place (I hope!) in TextformatterHannaCode.module and the repeater field case now seems to work for hanna codes.
-
6
-
-
This turned out to be an issue with the order of variables returned by core TemplateFile::getArray. It was doing an array_merge which gave preference to API variables, preventing them from being overwritten. Maybe that's a good thing in general, but I don't think we want that limitation. I went ahead and changed it in dev. Now it gives preference to local variables, which enables Hanna Code to provide the behavior it was supposed to.
I think I've found another issue related to the use of $page in a Hanna code: if the field containing the hanna code is in a repeater, $page seems to point to something internal to the repeater instead of pointing to the page containing the repeater field.
Example with a hanna code echoing the page id:
// in a field directly in the home page echo $page->id; --> 1 // in the field from a repeater on the same home page echo $page->id; --> 1060
From what I understand by looking at the repeater module source code, repeaters are implemented with pages. So the observed behavior is probably a side effect of this.
Any idea of how to fix this ?
-
1
-
-
Thanks Ryan. I tested my $article_page case with the last dev version and it works just as expected now.
This really opens a huge potential for Hanna code regarding cross-fields referencing in a page independently of the rendering context.
-
1
-
-
Thanks, glad you are enjoying it! I'm assuming you are running the latest PW (2.4, formerly dev branch) since you are using multi-language features, but just in case, make sure you are running 2.4 as the support of multi-language is much better in 2.4 than in 2.3.
Sure. As I was just starting with PW, I was luck enough to pick dev version 2.3.15 (which said "release candidate for 2.4") and upgraded to 2.4 yesterday.
-
The page that originated the request is meant the one that is rendered, and that's not your article page.
Agreed: my article page is the page containing the field where the Hanna code exists.
That's why the behavior I observe don't look consistent with HannaCode module's documentation saying:
The $page API variable available to your Hanna code represents the page where the Hanna code exists. It is possible for this to be different from wire('page'), which represents the page that originated the request.
And this behavior would be great by the way, because this is probably what you expect when putting a Hanna code in a page field.
Thanks anyway for the suggestion. I will try it.
-
I read your Post atleast 5 times, But I really don't understand what you're asking.
Ok. I've tried to clarify it in the original post.
-
Probably I'm missing something simple, but the $page variable doesn't seem to be referring to the page where the field including the HannaCode is, as the doc says:
The $page API variable available to your Hanna code represents the page where the Hanna code exists. It is possible for this to be different from wire('page'), which represents the page that originated the request.For example, I have a very simple code [[files_url]] to get the files directory url from a text fiel in a page:
echo $config->urls->files . $page->id . "/";
It's used for referencing page images or file from a "body" field:
<img class="video-image" onclick="play_video('NxgHAktpdF8')" src="[[files_url]]an_image.jpg" />
When using this code in a "body" field in page $article_page, referenced from the currently displayed page wire("page") like this
function renderArticle($article_page) { //... $out .= $article_page->body; //... return $out; } echo renderArticle($article_page);
the page id in the html page is the wrong one, i.e. the one from wire("page") and not the article_page's id.
To clarify, here is what I get:
echo $article_page->id; => 1048 echo wire("page")->id; => 1041 echo $article_page->body => <img class="video-image" onclick="play_video('NxgHAktpdF8')" src="/site/assets/files/1041/an_image.jpg" />
Which causes an image-not-found error, as the expected result should have been:
echo $article_page->body => <img class="video-image" onclick="play_video('NxgHAktpdF8')" src="/site/assets/files/1048/an_image.jpg" />
I'm running version 2.3.15 from dev branch.
So bug or expected behavior? And any idea of how to make it work?
(For such a simple use case I could of course do some kind of string replacement in my template, but I would prefer to stay with HannaCodes for regularity if possible
)
-
This is one that we can feasibly get working without too much trouble, if there's a need for it.Doesn't matter what language the user is viewing. Also trying to do something like "name$langID=blabla" just throws error that name1013 isn't a field.
One way you could do it is to change your "name" selector to a path selector:
$p = $pages->find($page->parent->path . "namefor_german");
Thanks! I was trying to do something similar to manage language-friendly permalinks to news articles:
$article_page = $pages->get('/site-articles')->child("categories.name=news, post_date>=$start_date, post_date<=$end_date, name{$user->language}|name=$input->urlSegment2");
Replaced it by:
$candidate_pages = $pages->find($pages->get('/site-articles')->path . $input->urlSegment2); $article_page = $candidate_pages->get("categories.name=news, post_date>=$start_date, post_date<=$end_date");
And now it works.
BTW, I'm in the (almost finished) process of porting an existing site to ProcessWire and the multi-language support in pw is just fantastic!
-
1
-
$session->redirect with arbitrary 30x status code
in Wishlist & Roadmap
Posted
Actually you can directly use the $session->redirect() method for sending any 30X status code, by taking advantage of its second parameter 'http301'.
When this parameter is set to false, ProcessWire uses PHP default behavior regarding location headers (PHP Manual says: […] "Location:" header. Not only does it send this header back to the browser, but it also returns a REDIRECT (302) status code to the browser unless the 201 or a 3xx status code has already been set.)
Therefore, to send a 303 status code, you can simply write: