• Content Count

  • Joined

  • Last visited

  • Days Won


Sergio last won the day on April 21 2018

Sergio had the most liked content!

Community Reputation

534 Excellent

1 Follower

About Sergio

  • Rank
    Sr. Member
  • Birthday 04/13/1979

Profile Information

  • Gender
  • Location
    Belo Horizonte, Brazil

Recent Profile Visitors

6,247 profile views
  1. Sergio

    AFAIK, only English is installed by default (at least on Digital Ocean servers) so I always went up "sshing" and installing my Portuguese ones Check in your shared server panel docs if they have more locales installed.
  2. Sergio

    This module looks very cool, thank you! I've built a similar solution (although simpler in the config side) where I run a cronjob daily to update the count on a field on each page. It works like a charm, but of course, there are other ways.
  3. Sergio
  4. I will check on that flag, @horst ! Thank you for clarifying and to offer your help.
  5. Hi everyone, Yesterday I began working on a module to create a filesystem abstraction for files and images using the Flysytem library (, out of the necessity of having the images copied (and probably linked) on Amazon S3 and other places like Dropbox. There two reasons why I decided to tackle this: 1 - When I work on the project in my machine, I need a way to automatically sync the images from the server and/or the external storage. When an image is added on the server and the database is imported on my local env, PW shows a blank thumbnail of it. The idea for the module if to check if the page image has a width == 0 and if it exists on the server, add it to the local filesystem. 2 - In the past, I had to move a large website to a different server in a hurry and a lot of images were left behind (it was a mess). So I'm planning for a possible future worst-case scenario of the server exploding The code I quickly wrote is below (please bear with me that it's pretty raw at the moment). One thing I had to figure it out is why PW fires the Pageimage::size hook wherever a page is loaded in the admin, even though the thumbnails are already created. I was planning to save the image variations on S3 as well. Can someone clarify? I know that @LostKobrakai was working on a similar project, and so I would like to ask him and everyone else if you think I (and who may help) should evolve this idea into a full-featured module where the user can select which server (adapter) to use (AWS, Digital Ocean spaces, Dropbox etc.) <?php namespace ProcessWire; use Aws\S3\S3Client; use League\Flysystem\AwsS3v3\AwsS3Adapter; use League\Flysystem\Filesystem; use League\Flysystem\Adapter\Local; use Spatie\Dropbox\Client; use Spatie\FlysystemDropbox\DropboxAdapter; class ProcessFly extends WireData implements Module, ConfigurableModule { public static function getModuleInfo() { return array( 'title' => 'ProcessWire Flysystem Integration', 'version' => 001, 'summary' => 'Synchronize all the page assets uploaded through PW to a specified bucket in Amazon S3 and...', 'author' => 'Sérgio Jardim', 'singular' => true, 'autoload' => true, 'icon' => 'image' ); } public function init() { $this->client = S3Client::factory([ 'credentials' => [ 'key' => '', 'secret' => '', ], 'region' => 'sa-east-1', 'version' => 'latest', ]); $this->bucket_name = ""; $this->dir_name = "images"; $this->s3_adapter = new AwsS3Adapter($this->client, $this->bucket_name, $this->dir_name); $this->s3_filesystem = new Filesystem($this->s3_adapter); // DROPBOX $this->authorizationToken = ""; $this->dropbox_client = new Client($this->authorizationToken); $this->adapter_dropbox = new DropboxAdapter($this->dropbox_client); $this->dropbox_filesystem = new Filesystem($this->adapter_dropbox); $this->addHookAfter('Pages::saved', $this, 'checkImageS3'); // Download images that are not in local filesystem for whatever reason but are available in the remove one. $this->addHookAfter('InputfieldFile::fileAdded', $this, 'uploadImageS3'); // Fired when a file/image is added to a page in the admin // $this->addHookAfter('Pageimage::size', $this, 'uploadImageS3'); // Fired when a file is resized via API. Note: this hook is also called when the page is loaded in Admin. Need to know why. $this->addHookAfter('Pageimage::url', $this, 'redirectImageURL'); //Replace image URL for the S3 path } public function redirectImageURL($event){ if($event->page->template == 'admin') return; else $event->return = "[bucket name]/images/" . $event->object->page . "/" . $event->object->name; } // UPLOAD public function uploadImageS3($event){ if(count($event->return)) { //if it is a image resize event get image variation data $file = $event->return; } else { $file = $event->arguments(0); } $filename = $file->name; $filename_original = $file->url; $pathToFile = $file->page . "/" . $filename; $system_path = $file->filename(); try{ $file_on_s3 = $this->s3_filesystem->has($pathToFile); //check if file exists on AWS if(!$file_on_s3) { $contents = file_get_contents($system_path); $this->s3_filesystem->put($pathToFile, $contents, array( 'visibility' => 'public', )); //upload file with the same folder structure as in assets/files: page_id/file_name //Also add a copy to Dropbox (OPTIONAL) $this->dropbox_filesystem->put($pathToFile, $contents); } } catch (Exception $e){ throw new WireException("Error: Image not Added to S3: {$e->getMessage()}"); } } public function checkImageS3($event){ $page = $event->arguments(0); if(count($page->images)) { foreach($page->images as $file) { if($file->width == 0) { return $this->downloadImageS3($page, $file); }; } } } public function downloadImageS3($page, $file){ $pathToFile = $page->id . "/" . $file->name; $file_on_s3 = $this->s3_filesystem->has($pathToFile); //check if file exists on AWS if($file_on_s3) { $image = "[bucket name]/images/" . $pathToFile; // $page->images->remove($image); $page->images->add($image); $page->save(); } else { throw new WireException("Error: Image not found on S3: {$file->name}"); } } }
  6. Thanks! It was as it's a pretty simple extension (source below). I followed the VS Code official docs: and looked at the examples they gave. // The module 'vscode' contains the VS Code extensibility API // Import the module and reference it with the alias vscode in your code below const vscode = require('vscode'); // this method is called when your extension is activated // your extension is activated the very first time the command is executed function activate(context) { // Use the console to output diagnostic information (console.log) and errors (console.error) // This line of code will only be executed once when your extension is activated console.log('Congratulations, your extension "whichthemeisthat" is now active!'); // The command has been defined in the package.json file // Now provide the implementation of the command with registerCommand // The commandId parameter must match the command field in package.json let disposable = vscode.commands.registerCommand('extension.whichThemeIsThat', function () { // The code you place here will be executed every time your command is executed // context.subscriptions.push(disposable); // create a new status bar item that we can now manage myStatusBarItem = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Right, 100); context.subscriptions.push(myStatusBarItem);; updateStatusBarItem(); // register some listener that make sure the status bar // item always up-to-date context.subscriptions.push(vscode.window.onDidChangeActiveTextEditor(updateStatusBarItem)); context.subscriptions.push(vscode.workspace.onDidChangeConfiguration(updateStatusBarItem)); }); } function updateStatusBarItem() { const theme = vscode.workspace.getConfiguration().get('workbench.colorTheme'); const font = vscode.workspace.getConfiguration().get('editor.fontFamily'); myStatusBarItem.text = '$(eye) Theme: ' + theme + ' + ' + 'Font: ' + font; } exports.activate = activate; // this method is called when your extension is deactivated function deactivate() { } exports.deactivate = deactivate;
  7. Which Theme/Font is That? Tired of hearing that question when you share a screenshot or a tutorial on YouTube? Your problems are over! Introducing "Which Theme is That" Visual Studio Code extension. This extension will show on the status bar your workspace's current theme name and font. Install Grab it here: How to Run Install the extension and run the Command Palette Ctrl+Shift+P or Cmd+Shift+P on Mac. Search for "Which Theme is That?" and hit Enter. Screenshot Enjoy!
  8. Sergio

    Welcome to the forum, @MateThemes ! As @Gideon So already pointed out, to accomplish this you will need to implement a condition on your template code depending on your business needs, but it can also be achieved without any custom code if it's just a matter of hiding a portion of a content from a page that has both languages. If you could explain a little bit more about what you want to accomplish, we can help.
  9. Sergio

    It is! Thanks!
  10. Sergio

    I'm getting a 404 on your website due to Chrome's blocking scripts on http as insecure. See screenshot. Version 70.0.3538.110 (Official Build) (64-bit) on Windows 10
  11. Sergio

    Olá, Guy! I'm hosting the app on Laravel Forge with FastCGI enabled. I've based my config on this @u-nikos post: Here's my version: server { listen 443 ssl http2; listen [::]:443 ssl http2; server_name; root /home/forge/; index index.html index.htm index.php; charset utf-8; # ----------------------------------------------------------------------------------------------- # Access Restrictions: Protect ProcessWire system files # ----------------------------------------------------------------------------------------------- # Block access to ProcessWire system files location ~ \.(inc|info|module|sh|sql)$ { deny all; } # Block access to any file or directory that begins with a period location ~ /\. { deny all; } # Block access to protected assets directories location ~ ^/(site|site-[^/]+)/assets/(cache|logs|backups|sessions|config|install|tmp)($|/.*$) { deny all; } # Block acceess to the /site/install/ directory location ~ ^/(site|site-[^/]+)/install($|/.*$) { deny all; } # Block dirs in /site/assets/ dirs that start with a hyphen location ~ ^/(site|site-[^/]+)/assets.*/-.+/.* { deny all; } # Block access to /wire/config.php, /site/config.php, /site/config-dev.php, and /wire/index.config.php location ~ ^/(wire|site|site-[^/]+)/(config|index\.config|config-dev)\.php$ { deny all; } # Block access to any PHP-based files in /templates-admin/ location ~ ^/(wire|site|site-[^/]+)/templates-admin($|/|/.*\.(php|html?|tpl|inc))$ { deny all; } # Block access to any PHP or markup files in /site/templates/ location ~ ^/(site|site-[^/]+)/templates($|/|/.*\.(php|html?|tpl|inc))$ { deny all; } # Block access to any PHP files in /site/assets/ location ~ ^/(site|site-[^/]+)/assets($|/|/.*\.php)$ { deny all; } # Block access to any PHP files in core or core module directories location ~ ^/wire/(core|modules)/.*\.(php|inc|tpl|module)$ { deny all; } # Block access to any PHP files in /site/modules/ location ~ ^/(site|site-[^/]+)/modules/.*\.(php|inc|tpl|module)$ { deny all; } # Block access to any software identifying txt files location ~ ^/(COPYRIGHT|INSTALL|README|htaccess)\.(txt|md)$ { deny all; } # Block all http access to the default/uninstalled site-default directory location ~ ^/site-default/ { deny all; } #Amplify dashboard location /nginx_status { stub_status on; allow; deny all; } # ----------------------------------------------------------------------------------------------- # If the request is for a static file, then set expires header and disable logging. # Give control to ProcessWire if the requested file or directory is non-existing. # ----------------------------------------------------------------------------------------------- location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|eot|woff|ttf)$ { expires 15d; log_not_found off; access_log off; try_files $uri $uri/ /index.php?it=$uri&$query_string; } # ----------------------------------------------------------------------------------------------- # ProCache Rules # ----------------------------------------------------------------------------------------------- set $cache_uri $request_uri; if ($request_method = POST) { set $cache_uri 'nocache'; } if ($http_cookie ~* "wires_challenge") { set $cache_uri 'nocache'; } if ($http_cookie ~* "persist") { set $cache_uri 'nocache'; } # ----------------------------------------------------------------------------------------------- # This location processes all other requests. If the request is for a file or directory that # physically exists on the server, then load the file. Else give control to ProcessWire. # ----------------------------------------------------------------------------------------------- location / { expires -1; try_files /site/assets/ProCache-b3d534d...d/$cache_uri/index.html $uri $uri/ /index.php?it=$uri&$args; } location = /favicon.ico { access_log off; log_not_found off; } location = /robots.txt { access_log off; log_not_found off; } access_log off; error_log /var/log/nginx/ error; error_page 404 /index.php; location ~ \.php$ { fastcgi_split_path_info ^(.+\.php)(/.+)$; fastcgi_pass unix:/var/run/php/php7.2-fpm.sock; fastcgi_index index.php; include fastcgi_params; } location ~ /\.ht { deny all; } }
  12. Sergio

    Hey @Guy Verville, excellent write-up!! Thank you for that! I've been using ProCache for about 3 years now in Nginx without any issues. So, go ahead! PS: I'd like to suggest you post in on Medium, etc. as well to increase reach. If you decide so, I also suggest to add to Ryans role: creator and maintainer of Processwire... instead of just designer, as people may only read "graphic/web designer" when they see that.
  13. Two examples I've used. //Page "intro" property that can be accessed via $page->intro: wire()->addHookProperty('Page::intro', function($event) { $page = $event->object; if ($page->summary) { //some pages on the admin do not have a summary field, only body $intro = strip_tags($page->summary); } else { $body = strip_tags($page->body); $intro = $body; $length = 450; if (strlen($body) > $length) { $intro = preg_replace("/^(.{1,$length})(\s.*|$)/s", '\\1...', $intro); } } $event->return = $intro; }); // I have a computed page_views field. If the page is a podcast, I added the views from Soundcloud to the page_total_views hook. wire()->addHookProperty('Page::page_total_views', function($event) { $page = $event->object; $views = 0; if ($page->soundcloud_playback !== "") { //if page has a soundcloud field, it's a podcast page $page->of(false); $en = wire('languages')->get("default"); $soundcloud_playback_en = intval($page->getLanguageValue($en, 'soundcloud_playback')); // get the unformatted value in English $pt = wire('languages')->get("pt"); $soundcloud_playback_pt = intval($page->getLanguageValue($pt, 'soundcloud_playback')); // get the unformatted value in Portuguese $views = intval($page->page_views) + $soundcloud_playback_en + $soundcloud_playback_pt; } else { $views = $page->page_views; } $event->return = $views; });
  14. WOW!! This looks awesome, @joshuag!! Seems very useful, especially for speed up the creation of complex templates!!
  15. Sergio

    This is very impressive! Excellent job @bernhard!! Could you detail more about the PDF generation? Library used, how the custom design was made, how you control widows / orphans etc. Thank you!