Jump to content


  • Content Count

  • Joined

  • Last visited

  • Days Won


jean-luc last won the day on November 13 2016

jean-luc had the most liked content!

Community Reputation

47 Excellent

About jean-luc

  • Rank
    Jr. Member

Contact Methods

  • Website URL

Profile Information

  • Gender
    Not Telling
  • Location
    Rennes - France

Recent Profile Visitors

3,088 profile views
  1. 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: header("HTTP/1.1 303 See Other"); $session->redirect ($someUrl, /*$http301=*/false);
  2. @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. Hi @erikvanberkum 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.
  4. 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…
  5. 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.
  6. I just made a few edits in the tutorial to improve clarity and fix typos...
  7. 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 database Let's start. Create a docker container for the site database For 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 database We 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 configuration In 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 site 1. Create an image-specific directory with the following contents and `cd` to it bash-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` content FROM 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 configuration We 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 image docker build -t php-5.6-pw-celedev . 5. Create a Data-only container for the site files docker run --name celedev-data php-5.6-pw-celedev echo "Celedev site data-only container" 6. Run the web server container docker 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 previously During 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 Docker 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 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,>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 data We 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 image We 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.
  8. 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:
  9. 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
  10. 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)
  11. 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.
  12. 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.
  13. 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: 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?
  14. @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.
  15. 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 ?
  • Create New...