Skip to content

Instantly share code, notes, and snippets.

@sjardim
Last active August 11, 2023 04:20
Show Gist options
  • Star 6 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sjardim/d74fae71b5bfe6a44ab88efc9aaa5279 to your computer and use it in GitHub Desktop.
Save sjardim/d74fae71b5bfe6a44ab88efc9aaa5279 to your computer and use it in GitHub Desktop.
'Synchronize all the page images uploaded through ProcessWire to a specified bucket in Amazon S3 and other places using Flysystem library.
<?php namespace ProcessWire;
use Aws\S3\S3Client;
use League\Flysystem\AwsS3v3\AwsS3Adapter;
use League\Flysystem\Filesystem;
use League\Flysystem\Adapter\Local;
class ProcessSync extends WireData implements Module, ConfigurableModule {
public static function getModuleInfo() {
return array(
'title' => 'Sync Images and Files with Flysystem Integration',
'summary' => 'Synchronize all the page images uploaded through ProcessWire to a specified bucket in Amazon S3 and other places using Flysystem library.',
'author' => 'Sérgio Jardim',
'href' => '',
'singular' => true,
'autoload' => true,
'icon' => 'amazon',
'version' => 001,
'requires' => array('ProcessWire>=3.0.80', 'PHP>=5.6')
);
}
/**
* Data as used by the get/set functions
*
*/
protected $data = array();
/**
* ------------------------------------------------------------------------
* Default configuration values
* ------------------------------------------------------------------------
* @return array
*/
public static function getDefaultConfig() {
return array(
'AmazonS3Key' => '',
'AmazonS3Secret' => '',
'AmazonS3Region' => '',
'AmazonS3BucketName' => '',
'AmazonS3DirName' => '',
);
}
/**
* ------------------------------------------------------------------------
* Populate default configuration (will be overwritten after constructor
* with user's own configuration)
* ------------------------------------------------------------------------
* @return object
*/
public function __construct() {
foreach(self::getDefaultConfig() as $key => $value) {
$this->$key = $value;
}
}
/**
* ------------------------------------------------------------------------
* Build module configuration page
* ------------------------------------------------------------------------
* @param array $data
* @return mixed
*/
static public function getModuleConfigInputfields(array $data) {
$data = array_merge(self::getDefaultConfig(), $data);
// ------------------------------------------------------------------------
// Build config screen form
// ------------------------------------------------------------------------
$form = new InputfieldWrapper();
// ------------------------------------------------------------------------
// AmazonS3Key
// ------------------------------------------------------------------------
$field = wire('modules')->get("InputfieldText");
$field->name = "AmazonS3Key";
$field->label = __("S3 User credential's key");
$field->icon = "key";
$field->columnWidth = '50';
$field->value = $data['AmazonS3Key'];
$form->add($field);
// ------------------------------------------------------------------------
// AmazonS3Secret
// ------------------------------------------------------------------------
$field = wire('modules')->get("InputfieldText");
$field->name = "AmazonS3Secret";
$field->label = __("S3 User credential's secret");
$field->icon = "book";
$field->columnWidth = '50';
$field->value = $data['AmazonS3Secret'];
$form->add($field);
// ------------------------------------------------------------------------
// AmazonS3Region
// ------------------------------------------------------------------------
$field = wire('modules')->get("InputfieldText");
$field->name = "AmazonS3Region";
$field->label = __("S3 Region");
$field->icon = "globe";
$field->columnWidth = '33';
$field->notes = 'e.g.: "sa-east-1"';
$field->value = $data['AmazonS3Region'];
$form->add($field);
// ------------------------------------------------------------------------
// AmazonS3BucketName
// ------------------------------------------------------------------------
$field = wire('modules')->get("InputfieldText");
$field->name = "AmazonS3BucketName";
$field->label = __("S3 Bucket Name");
$field->icon = "inbox";
$field->columnWidth = '33';
$field->notes = 'e.g.: "website-name"';
$field->value = $data['AmazonS3BucketName'];
$form->add($field);
// ------------------------------------------------------------------------
// AmazonS3DirName
// ------------------------------------------------------------------------
$field = wire('modules')->get("InputfieldText");
$field->name = "AmazonS3DirName";
$field->label = __("S3 Directory Name");
$field->icon = "folder-open";
$field->columnWidth = '33';
$field->notes = 'e.g.: "images"';
$field->value = $data['AmazonS3DirName'];
$form->add($field);
// ------------------------------------------------------------------------
// Build form
// ------------------------------------------------------------------------
return $form;
}
public function init() {
$this->client = S3Client::factory([
'credentials' => [
'key' => $this->AmazonS3Key,
'secret' => $this->AmazonS3Secret,
],
'region' => $this->AmazonS3Region,
'version' => 'latest',
]);
$this->bucket_name = $this->AmazonS3BucketName;
$this->dir_name = $this->AmazonS3DirName;
$this->s3_adapter = new AwsS3Adapter($this->client, $this->bucket_name, $this->dir_name);
$this->s3_filesystem = new Filesystem($this->s3_adapter);
// Fired when a file/image is added to a page in the admin
$this->addHookAfter('InputfieldFile::fileAdded', $this, 'uploadImageS3');
if(!wire('config')->production) {
//Download images from server when their url is requested
//But on fire this hook when not in production by checking the production flag set in the config file
$this->addHookAfter('Pagefile::url, Pagefile::filename', $this, 'downloadImageServer');
}
}
public function redirectImageURL($event){
if($event->page->template == 'admin') return;
else
$event->return =
"https://" .
$this->AmazonS3BucketName .
".s3-" .
$this->AmazonS3Region .
".amazonaws.com/" .
$this->AmazonS3DirName . "/" .
$event->object->page . "/" .
$event->object->name;
}
// UPLOAD
/**
* Hook before InputfieldFile::fileAdded in auto mode
* Optimize image on upload
*
* @param HookEvent $event
* @return bool false if image extension is not in allowedExtensions
*
*/
public function uploadImageS3($event){
$file = $event->argumentsByName('pagefile');
$filename = $file->name;
$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
if( wire('config')->debug ) wire('log')->save("process_sync", "Image uploaded to S3: {$system_path}");
}
}
catch (Exception $e){
throw new WireException("Error: Image not Added to S3: {$e->getMessage()}");
}
}
/**
* This is called from AutoSmush module
*
* @param HookEvent $event
*
*/
public function uploadImageVariationS3($AutoSmushImage){
preg_match_all('/(\/site\/assets\/files\/)(\d+)(.+)/', $AutoSmushImage->url, $matches);
//(\d+) = ####
//(.+) = /IMAGENAME.400x0.jpg
$pathToFile = $matches[2][0].$matches[3][0];
//.../public/site/assets/files/####/IMAGENAME.400x0.jpg"
$system_path = $AutoSmushImage->filename;
try{
$contents = file_get_contents($system_path);
//Write or Update Files
$this->s3_filesystem->put($pathToFile, $contents, array(
'visibility' => 'public',
)); //upload file with the same folder structure as in assets/files: page_id/file_name
if( wire('config')->debug ) wire('log')->save("process_sync", "Image VARIATION uploaded to S3 (after AutoSmush): {$pathToFile}");
}
catch (Exception $e){
throw new WireException("Error: Image variation not Added to S3: {$e->getMessage()}");
}
}
public function uploadExistingImages($file){
$filename = $file->name;
$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
if( wire('config')->debug ) wire('log')->save("process_sync", "File uploaded to S3: {$system_path}");
}
}
catch (Exception $e){
throw new WireException("Error: Image variation not added to S3: {$e->getMessage()}");
}
}
//https://processwire.com/blog/posts/pw-3.0.137/
public function downloadImageServer($event){
$config = $event->wire('config');
$file = $event->return;
if($event->method == 'url') {
// convert url to disk path
$file = $config->paths->root . substr($file, strlen($config->urls->root));
}
if( !file_exists($file) ) {
// download file from source if it doesn't exist here
$src = '[YOUR-PRODUCTION-URL]/site/assets/files/';
$url = str_replace($config->paths->files, $src, $file);
$http = new WireHttp();
$http->download($url, $file);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment