diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7a4db70 --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +mosaico-php-backend.iml +backend-php/vendor +backend-php/composer.phar +*.orig +data +dist/** +templates/** diff --git a/README.md b/README.md index cc2dd67..dd1c084 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,17 @@ This is a PHP backend for Mosaico Mosaico can be found at https://github.com/voidlabs/mosaico -First, install and set up Mosaico. Then install these files on top of the Mosaico installation. +## Getting started + +1. install and set up Mosaico +2. copy `mosaico/dist` to your webserver, e.g. to `myserver/mosaico/dist` +3. copy `mosaico/templates` to youer webserver, e.g. to `myserver/mosaico/templates` +4. copy `index.html`, `editor.html`, `dl`, `img`, `upload` from this project on top of the Mosaico installation e.g. `myserver/mosaico/*` +5. run `php composer.phar install` if you want to use InlineStyle on your server. (See https://getcomposer.org/doc/00-intro.md how to get and use composer.) + You can do this offline and copy `vendor`to e.g. `myserver/mosaico`; The results of this installation are not checked in to this project. +6. adapt `config.php` according to your needs and copy it to e.g. `myserver/mosaico` +7. goto `{url of your server}/mosaico` + ## Dependencies @@ -14,6 +24,8 @@ You also do need to have Imagemagick support enabled in your PHP configuration. This project also requires Premailer (http://premailer.dialect.ca/). Premailer is used to inline the CSS styles. If that service is ever taken down, we will have to find an alternate solution. Or, if you have an alternate solution that does not require dependencies on a web service, feel free to contribute! +Alternatively you can use InlineStyle (https://github.com/christiaan/InlineStyle) which runs ony your own server. You need to install it via composer. This is experimental, we will have to investigate if it works well with mosaico. + ## New folders and files ``` backend-php/config.php diff --git a/backend-php/composer.json b/backend-php/composer.json new file mode 100644 index 0000000..7e9ff2e --- /dev/null +++ b/backend-php/composer.json @@ -0,0 +1,5 @@ +{ + "require": { + "inlinestyle/inlinestyle": "^1.2" + } +} diff --git a/backend-php/composer.lock b/backend-php/composer.lock new file mode 100644 index 0000000..e12bd02 --- /dev/null +++ b/backend-php/composer.lock @@ -0,0 +1,119 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", + "This file is @generated automatically" + ], + "hash": "4e173b2a9dad411979481973095b6f7a", + "content-hash": "ec03203bcd361b983c62933f0659d8f5", + "packages": [ + { + "name": "inlinestyle/inlinestyle", + "version": "1.2.5", + "source": { + "type": "git", + "url": "https://github.com/christiaan/InlineStyle.git", + "reference": "4077e9037112be3a5545094aa3921183553c5f61" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/christiaan/InlineStyle/zipball/4077e9037112be3a5545094aa3921183553c5f61", + "reference": "4077e9037112be3a5545094aa3921183553c5f61", + "shasum": "" + }, + "require": { + "php": ">=5.3.3", + "symfony/css-selector": "~2.1" + }, + "require-dev": { + "phpunit/phpunit": "3.7.*" + }, + "type": "library", + "autoload": { + "psr-0": { + "InlineStyle": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Christiaan Baartse" + }, + { + "name": "Michael Tibben" + } + ], + "description": "Apply CSS stylesheets directly as inline styles to a HTML document", + "keywords": [ + "css", + "email", + "inline" + ], + "time": "2014-05-21 18:16:54" + }, + { + "name": "symfony/css-selector", + "version": "v2.7.7", + "source": { + "type": "git", + "url": "https://github.com/symfony/css-selector.git", + "reference": "abb47717fb88aebd9437da2fc8bb01a50a36679f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/css-selector/zipball/abb47717fb88aebd9437da2fc8bb01a50a36679f", + "reference": "abb47717fb88aebd9437da2fc8bb01a50a36679f", + "shasum": "" + }, + "require": { + "php": ">=5.3.9" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.7-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\CssSelector\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jean-François Simon", + "email": "jeanfrancois.simon@sensiolabs.com" + }, + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony CssSelector Component", + "homepage": "https://symfony.com", + "time": "2015-10-30 20:10:21" + } + ], + "packages-dev": [], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": [], + "prefer-stable": false, + "prefer-lowest": false, + "platform": [], + "platform-dev": [] +} diff --git a/backend-php/config.php b/backend-php/config.php index d0a6e72..e699b87 100644 --- a/backend-php/config.php +++ b/backend-php/config.php @@ -11,24 +11,27 @@ BASE_DIR => dirname( dirname( $_SERVER[ "SCRIPT_FILENAME" ] ) ) . "/", /* url to the uploads folder (relative to BASE_URL) */ - UPLOADS_URL => "uploads/", + UPLOADS_URL => "data/uploads/", /* local file system path to the uploads folder (relative to BASE_DIR) */ - UPLOADS_DIR => "uploads/", + UPLOADS_DIR => "data/uploads/", /* url to the static images folder (relative to BASE_URL) */ - STATIC_URL => "uploads/static/", + STATIC_URL => "data/static/", /* local file system path to the static images folder (relative to BASE_DIR) */ - STATIC_DIR => "uploads/static/", + STATIC_DIR => "data/static/", /* url to the thumbnail images folder (relative to BASE_URL */ - THUMBNAILS_URL => "uploads/thumbnails/", + THUMBNAILS_URL => "data/thumbnails/", /* local file system path to the thumbnail images folder (relative to BASE_DIR) */ - THUMBNAILS_DIR => "uploads/thumbnails/", + THUMBNAILS_DIR => "data/thumbnails/", /* width and height of generated thumbnails */ THUMBNAIL_WIDTH => 90, - THUMBNAIL_HEIGHT => 90 + THUMBNAIL_HEIGHT => 90, + + /* premailer */ + PREMAILER => 'inlinestyle' // inlinestyle | premailer ]; diff --git a/backend-php/index.php b/backend-php/index.php index 84e8ceb..37be6c2 100644 --- a/backend-php/index.php +++ b/backend-php/index.php @@ -14,9 +14,11 @@ const THUMBNAILS_DIR = 8; const THUMBNAIL_WIDTH = 9; const THUMBNAIL_HEIGHT = 10; +const PREMAILER = 11; + require "config.php"; -require "premailer.php"; + //die( "
" . print_r( $config, true ) . "" ); @@ -26,122 +28,106 @@ $http_response_code = 200; -$url = parse_url( $_SERVER[ "REQUEST_URI" ] ); +$url = parse_url($_SERVER["REQUEST_URI"]); -if ( array_key_exists( "path", $url ) ) -{ - $request = substr( $url[ "path" ], strlen( dirname( $url[ "path" ] ) ) ); - - //die( "
" . print_r( $request, true ) . "" ); - - $request_handlers = [ - "/upload/" => "ProcessUploadRequest", - "/img/" => "ProcessImgRequest", - "/dl/" => "ProcessDlRequest" - ]; - - if ( array_key_exists( $request, $request_handlers ) ) - { - $request_handlers[ $request ](); - } - else - { - $http_response_code = 404; - } -} -else -{ - $http_response_code = 500; +if (array_key_exists("path", $url)) { + $request = substr($url["path"], strlen(dirname($url["path"]))); + + //die( "
" . print_r( $request, true ) . "" ); + + $request_handlers = [ + "/upload/" => "ProcessUploadRequest", + "/img/" => "ProcessImgRequest", + "/dl/" => "ProcessDlRequest" + ]; + + if (array_key_exists($request, $request_handlers)) { + $request_handlers[$request](); + } else { + $http_response_code = 404; + } +} else { + $http_response_code = 500; } -http_response_code( $http_response_code ); +http_response_code($http_response_code); /** * handler for upload requests */ function ProcessUploadRequest() { - global $config; - global $http_return_code; - - $files = array(); - - if ( $_SERVER[ "REQUEST_METHOD" ] == "GET" ) - { - $dir = scandir( $config[ BASE_DIR ] . $config[ UPLOADS_DIR ] ); - - foreach ( $dir as $file_name ) - { - $file_path = $config[ BASE_DIR ] . $config[ UPLOADS_DIR ] . $file_name; - - if ( is_file( $file_path ) ) - { - $size = filesize( $file_path ); - - $file = [ - "name" => $file_name, - "url" => $config[ BASE_URL ] . $config[ UPLOADS_URL ] . $file_name, - "size" => $size - ]; - - if ( file_exists( $config[ BASE_DIR ] . $config[ THUMBNAILS_DIR ] . $file_name ) ) - { - $file[ "thumbnailUrl" ] = $config[ BASE_URL ] . $config[ THUMBNAILS_URL ] . $file_name; - } - - $files[] = $file; - } - } - } - else if ( !empty( $_FILES ) ) - { - foreach ( $_FILES[ "files" ][ "error" ] as $key => $error ) - { - if ( $error == UPLOAD_ERR_OK ) - { - $tmp_name = $_FILES[ "files" ][ "tmp_name" ][ $key ]; - - $file_name = $_FILES[ "files" ][ "name" ][ $key ]; - - $file_path = $config[ BASE_DIR ] . $config[ UPLOADS_DIR ] . $file_name; - - if ( move_uploaded_file( $tmp_name, $file_path ) === TRUE ) - { - $size = filesize( $file_path ); - - $image = new Imagick( $file_path ); - - $image->resizeImage( $config[ THUMBNAIL_WIDTH ], $config[ THUMBNAIL_HEIGHT ], Imagick::FILTER_LANCZOS, 1.0, TRUE ); - $image->writeImage( $config[ BASE_DIR ] . $config[ THUMBNAILS_DIR ] . $file_name ); - $image->destroy(); - - $file = array( - "name" => $file_name, - "url" => $config[ BASE_URL ] . $config[ UPLOADS_URL ] . $file_name, - "size" => $size, - "thumbnailUrl" => $config[ BASE_URL ] . $config[ THUMBNAILS_URL ] . $file_name - ); - - $files[] = $file; - } - else - { - $http_return_code = 500; - return; - } - } - else - { - $http_return_code = 400; - return; - } - } - } - - header( "Content-Type: application/json; charset=utf-8" ); - header( "Connection: close" ); - - echo json_encode( array( "files" => $files ) ); + global $config; + global $http_return_code; + + $files = array(); + + if ($_SERVER["REQUEST_METHOD"] == "GET") { + $dir = scandir($config[BASE_DIR] . $config[UPLOADS_DIR]); + + foreach ($dir as $file_name) { + $file_path = $config[BASE_DIR] . $config[UPLOADS_DIR] . $file_name; + + if (is_file($file_path)) { + $size = filesize($file_path); + + $file = [ + "name" => $file_name, + "url" => $config[BASE_URL] . $config[UPLOADS_URL] . $file_name, + "size" => $size + ]; + + if (file_exists($config[BASE_DIR] . $config[THUMBNAILS_DIR] . $file_name)) { + $file["thumbnailUrl"] = $config[BASE_URL] . $config[THUMBNAILS_URL] . $file_name; + } + + $files[] = $file; + } + } + } else { + if (!empty($_FILES)) { + foreach ($_FILES["files"]["error"] as $key => $error) { + if ($error == UPLOAD_ERR_OK) { + $tmp_name = $_FILES["files"]["tmp_name"][$key]; + + $file_name = $_FILES["files"]["name"][$key]; + + $file_path = $config[BASE_DIR] . $config[UPLOADS_DIR] . $file_name; + + if (move_uploaded_file($tmp_name, $file_path) === true) { + $size = filesize($file_path); + + $image = new Imagick($file_path); + + $image->resizeImage($config[THUMBNAIL_WIDTH], $config[THUMBNAIL_HEIGHT], + Imagick::FILTER_LANCZOS, 1.0, true); + $image->writeImage($config[BASE_DIR] . $config[THUMBNAILS_DIR] . $file_name); + $image->destroy(); + + $file = array( + "name" => $file_name, + "url" => $config[BASE_URL] . $config[UPLOADS_URL] . $file_name, + "size" => $size, + "thumbnailUrl" => $config[BASE_URL] . $config[THUMBNAILS_URL] . $file_name + ); + + $files[] = $file; + } else { + $http_return_code = 500; + return; + } + } else { + $http_return_code = 400; + return; + } + } + } + } + + header("Content-Type: application/json; charset=utf-8"); + header("Connection: close"); + + echo json_encode(array("files" => $files)); } /** @@ -149,100 +135,93 @@ function ProcessUploadRequest() */ function ProcessImgRequest() { - if ( $_SERVER[ "REQUEST_METHOD" ] == "GET" ) - { - $method = $_GET[ "method" ]; + if ($_SERVER["REQUEST_METHOD"] == "GET") { + $method = $_GET["method"]; - $params = explode( ",", $_GET[ "params" ] ); + $params = explode(",", $_GET["params"]); - $width = (int) $params[ 0 ]; - $height = (int) $params[ 1 ]; + $width = (int)$params[0]; + $height = (int)$params[1]; - if ( $method == "placeholder" ) - { - $image = new Imagick(); + if ($method == "placeholder") { + $image = new Imagick(); - $image->newImage( $width, $height, "#707070" ); - $image->setImageFormat( "png" ); + $image->newImage($width, $height, "#707070"); + $image->setImageFormat("png"); - $x = 0; - $y = 0; - $size = 40; + $x = 0; + $y = 0; + $size = 40; - $draw = new ImagickDraw(); + $draw = new ImagickDraw(); - while ( $y < $height ) - { - $draw->setFillColor( "#808080" ); + while ($y < $height) { + $draw->setFillColor("#808080"); - $points = [ - [ "x" => $x, "y" => $y ], - [ "x" => $x + $size, "y" => $y ], - [ "x" => $x + $size * 2, "y" => $y + $size ], - [ "x" => $x + $size * 2, "y" => $y + $size * 2 ] - ]; + $points = [ + ["x" => $x, "y" => $y], + ["x" => $x + $size, "y" => $y], + ["x" => $x + $size * 2, "y" => $y + $size], + ["x" => $x + $size * 2, "y" => $y + $size * 2] + ]; - $draw->polygon( $points ); + $draw->polygon($points); - $points = [ - [ "x" => $x, "y" => $y + $size ], - [ "x" => $x + $size, "y" => $y + $size * 2 ], - [ "x" => $x, "y" => $y + $size * 2 ] - ]; + $points = [ + ["x" => $x, "y" => $y + $size], + ["x" => $x + $size, "y" => $y + $size * 2], + ["x" => $x, "y" => $y + $size * 2] + ]; - $draw->polygon( $points ); + $draw->polygon($points); - $x += $size * 2; + $x += $size * 2; - if ( $x > $width ) - { - $x = 0; - $y += $size * 2; - } - } + if ($x > $width) { + $x = 0; + $y += $size * 2; + } + } - $draw->setFillColor( "#B0B0B0" ); - $draw->setFontSize( $width / 5 ); - $draw->setFontWeight( 800 ); - $draw->setGravity( Imagick::GRAVITY_CENTER ); - $draw->annotation( 0, 0, $width . " x " . $height ); + $draw->setFillColor("#B0B0B0"); + $draw->setFontSize($width / 5); + $draw->setFontWeight(800); + $draw->setGravity(Imagick::GRAVITY_CENTER); + $draw->annotation(0, 0, $width . " x " . $height); - $image->drawImage( $draw ); + $image->drawImage($draw); - header( "Content-type: image/png" ); + header("Content-type: image/png"); - echo $image; - } - else - { - $file_name = $_GET[ "src" ]; + echo $image; + } else { + $file_name = $_GET["src"]; - $path_parts = pathinfo( $file_name ); + $path_parts = pathinfo($file_name); - switch ( $path_parts[ "extension" ] ) - { - case "png": - $mime_type = "image/png"; - break; + switch ($path_parts["extension"]) { + case "png": + $mime_type = "image/png"; + break; - case "gif": - $mime_type = "image/gif"; - break; + case "gif": + $mime_type = "image/gif"; + break; - default: - $mime_type = "image/jpeg"; - break; - } + default: + $mime_type = "image/jpeg"; + break; + } - $file_name = $path_parts[ "basename" ]; + $file_name = $path_parts["basename"]; - $image = ResizeImage( $file_name, $method, $width, $height ); + $image = ResizeImage($file_name, $method, $width, $height); - header( "Content-type: " . $mime_type ); + header("Content-type: " . $mime_type); - echo $image; - } - } + echo $image; + } + } } /** @@ -250,131 +229,132 @@ function ProcessImgRequest() */ function ProcessDlRequest() { - global $config; - global $http_return_code; - - /* run this puppy through premailer */ + global $config; + global $http_return_code; - $premailer = Premailer::html( $_POST[ "html" ], true, "hpricot", $config[ BASE_URL ] ); + /* run this puppy through premailer */ - $html = $premailer[ "html" ]; + switch ($config[PREMAILER]) { + case 'premailer': + require "premailer.php"; + $premailer = Premailer::html($_POST["html"], true, "hpricot", $config[BASE_URL]); + $html = $premailer["html"]; + break; - /* create static versions of resized images */ - - $matches = []; + case 'inlinestyle': + require 'vendor/autoload.php'; + $htmldoc = new \InlineStyle\InlineStyle($_POST["html"]); + $htmldoc->applyStylesheet($htmldoc->extractStylesheets()); + $html = $htmldoc->getHTML(); + break; + } + /* create static versions of resized images */ - $num_full_pattern_matches = preg_match_all( '#
foobar
diff --git a/index.html b/index.html new file mode 100644 index 0000000..de57eab --- /dev/null +++ b/index.html @@ -0,0 +1,371 @@ + + + + +Id | Name | Created | Last changed | Operations | +
---|---|---|---|---|
#key |
+ versamix | +YYYY-MM-DD | +YYYY-MM-DD | ++ + + + | +
Designing and coding an email that works on every device and every client is a daunting task even for professionals.
+Mosaico allows you to realize a beautiful and effective template without a team of professionals and hours of testing to let it work everywhere.
+Responsive and tested Template, working with all major email clients and devices
+Rapid graphic personalization of the overall theme
+Flexibility and style customization of single elements
+Intuitive drag & drop image upload and automatic resizing to fit available space
+Global undo/redo system: stop wasting time with saves, reviews and confirmations
+Custom templates support, with a simple template language (make your html design work on Mosaico in few hours)
+Open Source: Mosaico is distributed under the GPL license and the complete code base is available on GitHub
+