Leaderboard
Popular Content
Showing content with the highest reputation on 08/21/2015 in all areas
-
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, 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 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.5 points
-
New processwire - responsive - site live on www.rugpijnweg.nl Thanks to this forum I found vxda who did all the coding while the design was done with his colleague Piotr from 7thavenue. Rugpijnweg.nl uses procache, and gets high scores on gtmetrix.com. It still needs some better way to optimize images and serve scaled images. Any feedback much appreciated, this site is meant to be fully responsive. Used Modules: - Procache (thanks Ryan) - ModulesManager (thanks soma) - ProcessJumplinks (thanks mike-rockett) - Thumbnails (thanks apeisa) - Get Video Thumbs (thanks adrian) Still some minor stuff needs done, happy to incorporate any feedback to make it better!2 points
-
forklift can do some amazing things, multi-rename, browse archives etc; also the split screen each with unlimited tabs is great when working on huge projects where you need fast access to 10 different folders; and then you can save entire workspaces...2 points
-
The Forklift software that I use (Forklift 2, Version 2.6.5) is updated regularly.2 points
-
Hi Guys, A new launch from us. Ensoul.co.uk - Interior Design, Architecture & Project Management, London. Background Based in London, Ensoul are Interior Architects specialising in high-end / luxury interiors, basement conversions, extensions, and residential renovations etc. We based the site on ProcessWire because it's agile, scalable and a pleasure to work with. Given that the Ensoul team will shortly be updating the site and blog in-house we needed a CMS that would also be intuitive for them to use. Overall the nature of the site is very visual and relies heavily on photography. It was essential the CMS had solid image management built in and this was another factor in choosing to run it on ProcessWire. More on that shortly. The plan This is Phase 1 (design and launch) of a multi phase project. Phase 2 consists of optimisation, refinement and a comprehensive SEO project. In particular we'll be redesigning the homepage, building a blog and looking to speed up page loads. With ProCache due to be installed very shortly, we plan to dramatically increase the site speed, minify a lot of the JS and gain points on Googles mobile speed test tool. Image Management I just wanted to highlight some of the nice image management features which ProcessWire brought to the table. 1. Background images Most of the pages have a large background image and we wanted the freedom to swap and change these on a page by page basis and test a lot of different photos. Given 80% of the page and background image is covered by content, not every image was going to work. We solved this simply by creating an image filed called Background Image into which we (or client) can drag and drop a photo of their choice. Any background photos are integrated into the jQuery Backstretch plugin In the case of the homepage where three background images are used, PW and Backstretch will create a slideshow instead. <?php // if Background_Image field contains more than 1 photo, echo the images in a slideshow // Mainly for Homepage if($page->Background_Image->count > 1 ) { $bgimage = $page->Background_Image; echo '<script>$.backstretch(["'; echo $bgimage->implode('", "', "url"); // results in url", "url", "url echo '"], {duration: 5000, fade: 1000});</script>'; } else // otherwise echo a single image on its own // Mainly for all other pages if($page->Background_Image->count == 1 ) { $bgimage = $page->Background_Image; foreach ($bgimage as $image) { echo" <script> $.backstretch('$image->url'); </script> "; } } ?> 2. Portfolio The Portfolio page is image heavy and features a masonry grid of photos which are then filtered by project type. On the front end, I was able to restrict each thumbnail and pop-up image to the size of my choice without having to crop each individual photo. To achieve this, I used a $thumb and $large variable and PW automagically handled the cropping. foreach($page->Images as $image) { $large = $image->width(800); $thumb = $image->size(340); echo ".... Creating the filters which toggle the display of rooms by type was surprisingly easy with ProcessWire. I used the image tag field which I hadn't really used before and quickly allowed me to tag a photo as a kitchen, bedroom or basement etc. It really was an eye opener into the power of PW: <div class="portfolioFilter"> <strong>View:</strong> <a href="#" data-filter="*" class="current">All</a> <a href="#" data-filter=".Kitchen">Kitchen</a> <a href="#" data-filter=".Bedroom">Bedroom</a> <a href="#" data-filter=".Bathroom">Bathroom</a> <a href="#" data-filter=".Sitting-Room">Sitting Room</a> <a href="#" data-filter=".Gym">Gym</a> <a href="#" data-filter=".Basement">Basement</a> <a href="#" data-filter=".Home-Office">Home Office</a> <a href="#" data-filter=".Kids">Kids</a> <a href="#" data-filter=".Garden">Garden</a> </div> <div class="portfolioContainer"> <?php foreach($page->Images as $image) { $large = $image->width(800); $thumb = $image->size(340); echo " <a class='fancybox-portfolio port-item {$image->tags}' href='$image->url' rel='gallery1'> <img src='$thumb->url' alt='$thumb->description' class='portfolio-thumb'> </a>"; } ?> </div> 3. Coverage Thumbs The client is receiving regular coverage in leading interior design magazines and at the end of the project I required a way to differentiate between Features and Opinion pieces. Again, PW made this very easy for me. I knew I could easily create a field called Coverage Type and select the type of coverage a publication should belong to. Traditionally I would have relied on the Page field to achieve this but I didn't need the initial more involved setup which that would require. Instead I opted for the new, simpler Options field instead. Featured Modules RenoTheme CoreConfig Upgrade Markup Simple Navigation CroppableImage FormBuilder ProCache (shortly) SEO Page Path History Redirects SiteMap XML ListerPro WireMail SMTP Conclusion That's pretty much it. I know some of the techniques here won't set the PW world on fire and probably are pretty basic but hopefully seeing the screengrabs and examples will help other beginners understand PW a little better. Thanks as always to the PW community who helped build this with their advice and answers along the way.2 points
-
And then I found FieldtypeSecureFile. Figures. https://processwire.com/talk/topic/10671-fieldtypesecurefile/1 point
-
I was a DirectoryOpus fan on Windows, and while I agree that Forklift doesn't have anywhere near as many config options, what it does, it does mostly very well and it has everything I need. It is not perfect but, in hindsight, DOpus almost has too many options. Each to his own right1 point
-
Just take a look at the files inside site-default folder at github. There's a _func.php and a _init.php, which are set up like I explained it.1 point
-
1 point
-
The latest 99Lime website, http://www.yucatanliving.com. Everything you ever wanted to know about living and experiencing life in the Yucatan. This was a huge project with a ton of different sections and templates. With Processwire we created a very large site with a lot of different content types and information without making the administration complex. We are very happy with how this project turned out and our clients love it! Processwire rocks the casbah! Shoutout to: Orlando world's most awesome processwire dev. Notable features Galleries Categories Search Advertising page /es Getting started 100% responsive Polls Event lists Custom Calendar of Events Module Custom Advertising Modules 404 Dynamic CSS colors Users and profiles. A few details. Over 1600 articles 20 templates A ton of Fields HTML KickStart jQuery Custom roles ProcessWire Modules used Custom CRON Module FieldTypeColorPicker FieldTypeConcat FieldTypeFontIconPicker Map Marker ProFields FieldTypePoll FieldTypeSelect Form Builder Migrator WordPress Migrator Batcher JumpLinks Comments Manager ListerPro New Comments System Video embed for YouTube/Vimeo Custom-built Advertising platform module. Custom-built Recurring Calendar module. Jquery Modules used Remodal ImageLightbox Sticky-kit BxSlider FitVids Jquery-Touch-Swipe-Drag1 point
-
I have a solution that 90% works, which is good enough for my needs. I have the complicated situation where there's an Images field, which needs to remain public, and a Documents field which needs to be protected in some situations. In my setup, I have a checkbox field is_member_page which is used to require authentication to view the page, so I've also used that in my hooks: # For files that should be protected, set the path to the .page-id files directory. Should auto-create the directory. $this->addHookAfter('PagefilesManager::path', function($event) { $page = $event->object->page; if ( $page->is_member_page ) { $event->return = dirname($event->return) . '/.' . $page->id . '/'; } }); I was using this next hook for a while, so the URL in the admin area pointed automatically to the download pass-through page. What I ran into is that this broke the preview in the Images field, though, so I opted against it. This means when I insert links to the files, I have to manually craft them. It's a minor inconvenience for being able to still preview and easily insert the images, though. # Optional; if you want to change the URL to automatically point to the download pass-through page. # Be sure to replace/set $YOUR_PAGE_ID as appropriate. $this->addHookAfter('PagefilesManager::url', function($event) { $page = $event->object->page; if ( $page->is_member_page ) { $event->return = wire('pages')->get($YOUR_PAGE_ID)->url . $event->object->page . '/' . $event->object->name; } }); This is where it gets a bit tricky, and violates DRY a bit. The above hooks affect the path and url for all file fields. I tried and tried, but I could not conditionally change them for only the Documents field while leaving the Images field. Instead, what I've done is checked if the inputfield is a FIle or an Image. If it's an image, I make a copy of it in the public folder, so it can be previewed in the backend (and used on the page ). $this->addHook('InputfieldFile::fileAdded', function($event) { if ( get_class($event->object) == 'InputfieldFile' ) { $file = $event->arguments(0); $path = $file->filename(); $page = $file->page; # if: member only page if ( $page->is_member_page ) { $secure_path = dirname(dirname($path)) . '/.' . $page->id . '/'; if ( !$file->copyToPath($secure_path) ) { $this->log->save('messages', "Error copying secure file to: {$secure_path}"); } } # end if } else if ( get_class($event->object) == 'InputfieldImage' ) { $file = $event->arguments(0); $path = $file->filename(); $page = $file->page; $public_dir = dirname(dirname($path)) . '/' . $page->id . '/'; # if: file does not exist in public dir yet; copy it if ( !file_exists($public_dir . $file->name) ) { if ( !$file->copyToPath($public_dir) ) { $this->log->save('messages', "Error copying file to: {$public_dir}"); } } # end if } }); Finally, to clean up my DRY mess, I make sure when an image is deleted from the protected directory, it's also deleted from the public directory. $this->addHook('InputfieldFile::processInputDeleteFile', function($event) { $file = $event->arguments(0); $path = $file->filename(); $page = $file->page; $public_path = dirname(dirname($path)) . '/' . $page->id . '/' . $file->name; if ( file_exists($public_path) ) { unlink($public_path); } }); My download pass-through page template is similar to the ones mentioned earlier in this thread, though I check the file extension and if it's a PDF I deliver it with MIME type application/pdf and not with Content-Disposition: attachment -- that way the PDF can be previewed in modern browsers, depending on the user's settings. (I can share this template too, if anyone is interested). Any feedback is welcome!1 point
-
This module is so amazing great! There is no task of navigation that can't go throw this nice piece of code.... Sorry for this idle post - but i think complimenting is important, too Jus finished my first MarkupSimpleNavigation with the use of the build in hooks to get a MegaMenu working fine...take me only some minutes until i found this gist: https://gist.github.com/somatonic/6258081 Thank you very much Soma i love it!1 point
-
Hi Guys, Here's another website from us. It is some months old, but I was waiting for all the content to be in place before showing. http://davinci-porto.pt/ This is a frames workshop and art gallery in the main art neighbourhood from Porto. the content is in Portuguese, sorry Exceptionally in this site, there was no intervention from Erika, so the design is mine.1 point
-
If you are working with sublime text I can definitely recommend SFTP package for sublime which integrates very nicely. Main advantage is that you don't need to switch between code editor and ftp program. Everything file transfer related happens from within the code editor, which, in my opinion, greatly enhances the workflow. You can even sync folders bidirectionally or diff local to remote files etc.1 point
-
I am currently working on a project where Android apps connect to a PW DB to put and get data. For data exchange I decided to create a REStful service with PW. There is a REST helper class for PW from clsource which is working great. The Android apps also display HTML that is rendered by PW and sent as JSON to the Android app. I am not in charge of the JAVA part so I can't tell you what is needed on the Android side to render that JSON. There is the Ionic framework that helps in developing android apps based on web technologies without using JAVA.1 point
-
Thanks kongondo! I am falling in love with process wire ( long time user of Wordpress). I was kind of hoping to keep one or two templates to use and just switch the header ( as they will all use the same fields etc etc and the number there will potentially be 25 different versions of one template header) I am sorry, I should have clarified in my original post.1 point
-
Not tested but something simular would work I guess $this->addHookAfter('Pages::saveReady', $this, 'hookPagesSave'); public function hookPagesSave(HookEvent $event) { // Modified page, already contains changes in memory, not yet in DB $page = $event->arguments('page'); // If there is a change in your field if ($page->isChanged('name_of_you_field')) { // Page as it is in the DB $oldPage = $this->pages->get($page->id); // ... Now you could do your comparison old <-> new. } }1 point
-
Here is something I hacked together quickly for automatically adding new child pages to the pagetable field. This is only if you are using the page as the parent. It also handles deletion of items if they are trashed externally. I also disabled the internal check for orphans - because they have been automatically added already, there is no need for the "Children were found that may be added to this table. Check the box next to any you would like to add." option. I seems to be working great here, but please test carefully!! Add this to your admin.php file: wire()->addHookBefore('InputfieldPageTable::render', function($event) { $pp = wire('pages')->get(wire('input')->get->id); $ptf = $event->object; //remove pages from pagetable field if they were externally trashed foreach($pp->{$ptf->name} as $item) { if($item->is(Page::statusTrash)) $pp->{$ptf->name}->remove($item); } //add pages to pagetable field if they were created externally foreach($pp->children as $child) { if(!$ptf->has($child->id)) { $pp->{$ptf->name}->add($child); $pp->of(false); $pp->save($ptf->name); } } //reset orphans property so that we don't get a message asking to add new pages that are now already automatically added $ptf->setOrphans(new pageArray()); });1 point
-
I think this is most likely the case, assuming you do have multiple render() calls. Your _init.php may not be the right place to do some of this stuff, or if it is, then you may need to add additional check so that you don't have the same things being run twice. For instance, you have a $config->scripts->removeAll(). If you add some scripts to $config->scripts, and later have another $page->render() call, then the files you previously added to it would again be removed by your $config->scripts->removeAll(). There are a couple ways you could solve this. First would be to just move your code that shouldn't be run twice to a separate include file, and then use PHP's include_once() function on that file. For instance, your _init.php could have this: include_once("./_init_once.php"); The above is the safest bet, because if your _init.php defines any functions or classes, then you don't have to worry about them being defined twice (and resulting in a fatal error). But if you want to keep everything in your _init.php, you could do this: if(!defined("LOADED")) { define("LOADED", true); // your code here // ... } Lastly, I wanted to mention that your files will have access to an $options variable, which has a 'pageStack' property containing a stack (array) of pages that initiated the current render. It will be empty the current render() is not recursive. So you could accomplish the same thing as above like this: if(empty($options['pageStack'])) { // your code here // ... } One more thing I just remembered is that you could also tell your render() call to skip the prependTemplateFile: echo $somePage->render(array('prependFile' => ''));1 point
-
What I would do is create a page "/tickets/". Under that page there will be children with a "ticket" template. That ticket template has a Page fieldtype (let's say: user_reference) reference to a user. This way you can get the tickets from one user by doing: $pages->find("ticket_reference=$particularuser"); When a user orders a ticket you basicly create a page underneath "/tickets/" and store the user id in the user_reference field. You can save it like: $ticket = new Page(); $ticket->template = $templates->get("ticket"); $ticket->parent = $pages->get("/tickets/"); $ticket->title = "Some title"; $ticket->user_reference = $currentUser; $ticket->save(); All written in the browser, but you should get the idea. Also you might want to check out this excellent thread by soma to create your forms. My advise is to start really simple. Just save and reference user. Test it thoroughly and add additional fields.1 point