1. Pimcore Version 3.x Documentation Installation and Upgrade System Requirements Installation (Apache)

Size: px
Start display at page:

Download "1. Pimcore Version 3.x Documentation... 3 1.1 Installation and Upgrade... 5 1.1.1 System Requirements... 6 1.1.2 Installation (Apache)... 7 1.1."

Transcription

1 1. Pimcore Version 3.x Documentation Installation and Upgrade System Requirements Installation (Apache) Nginx Configuration Quick Start Package / Sample Data / Boilerplate Installation Upgrade Notes Version History Develop with pimcore Overview Documents Quick Start Guide Types Controllers Templates (Views) Editables Helpers (Available View Methods) Document-Types Thumbnails Glossary Redirects Document Lists Localize your Documents (i18n) Translation of Document Editables Website Translations / Shared Translations Navigation Document Tree Copy documents and rewrite relations to new documents Hardlinks for documents Assets Asset Lists Custom Settings (Properties) Image Thumbnails Video Thumbnails Asset Document Thumbnails (PDF, docx, odf,...) Asset (Localized) Metadata Data Objects Object Classes Data Fields Layout Elements Object Lists External System Interaction Inheritance Custom Icons Locking fields Object Variants Object Preview Object Tree Custom icons and style in object-tree (since 1.4.2) Custom Layouts Reports & Marketing Google Analytics Setup Google Analytics Reporting with OAuth2 Service Accounts General Building URL's Cache Custom Cache Backends Output-Cache Custom Reports (previously SQL Reports) Custom Routes (Static Routes) Extending pimcore Class-Mappings - Overwrite pimcore models Custom persistent models Event API (EventManager) Events Extend pimcore using composer Hook into the startup-process Google Custom Search Engine (Site Search ~ CSE) Integration Logging Magic Parameters Newsletter Notes & Events Placeholders Object Text Properties Predefined Properties

2 Static Helpers System Settings Tag & Snippet Management Targeting & Personalization Managing Global Targeting Rules and Personas (Target Groups) Document personalization Assign Personas based on visited pages Interacting with the targeting & personalization engine Targeting: Tips for developers UUID Support Versioning Website Settings Working with Sites (Multisite) Adaptive Design / Device Helper / Tool Application Logger Reference: Database Structure, Fields, Relations - "How are objects stored?" Console / CLI Web Services REST Webservice Localization Outputfilters LESS (CSS Compiler) Install lessc on your server (Debian) Mailing Framework Pimcore\Mail Class Best Practices CLI Script for Object Import Extending the Pimcore User with User - Object Relation High Traffic Server Setup (*nix based Environment) Write-only database connection Administrator's Guide Backups Commandline Interface Backend Search Reindex Cache Warming Command-line Backup Image & Video Thumbnail Generator MySQL Tools Install Plugins Setting up WebDav Translations User Permissions User's Guide Backend Search and Properties Keyboard Shortcuts Working with WebDAV BitKinex as WebDAV Client Cyberduck as WebDAV Client NetDrive as WebDAV Client Windows Explorer as WebDAV Client Extensions Deeplinks into the pimcore Admin Interface Extension management using Composer Extension Manager Hooks Plugin Developer's Guide Example Plugin Anatomy and Design Plugin Backend UI Development and JS Hooks Useful Hints Adding custom main-navigation item FAQ

3 Pimcore Version 3.x Documentation Table of Contents Installation and Upgrade System Requirements Installation (Apache) Nginx Configuration Quick Start Package / Sample Data / Boilerplate Installation Upgrade Notes Version History Develop with pimcore Overview Documents Quick Start Guide Types Controllers Templates (Views) Editables Areablock Create your own bricks Area Block Checkbox Date Href (1 to 1 Relation) Image Input Link Multihref Multiselect Numeric Renderlet Select Snippet (embed) Table Textarea Video WYSIWYG PDF (Document) Helpers (Available View Methods) Document-Types Thumbnails Glossary Redirects Document Lists Localize your Documents (i18n) Translation of Document Editables Website Translations / Shared Translations Navigation Document Tree Copy documents and rewrite relations to new documents Hardlinks for documents Assets Asset Lists Custom Settings (Properties) Image Thumbnails Video Thumbnails Asset Document Thumbnails (PDF, docx, odf,...) Asset (Localized) Metadata Data Objects Object Classes Data Fields Date, Date & Time, Time Geographic Fields - Point, Bounds, Polygon Href, Multihref, Objects - Relations, Dependencies and Lazy Loading Localized Fields Non-Owner Objects Number Fields - Numeric, Slider Other Fields - Image, Image with Hotspots, Checkbox, Link Select Fields - Select, Multiselect, User, Country, Language Structured Data Fields Key Value Pairs Structured Data Fields - Classification Store

4 Structured Data Fields - Fieldcollections Structured Data Fields - Objectbricks Structured Data Fields - Structured Table Structured Data Fields - Table Text Input Fields - Input, Password,Textarea, WYSIWYG Video Field Layout Elements Object Lists External System Interaction Inheritance Custom Icons Locking fields Object Variants Object Preview Object Tree Custom icons and style in object-tree (since 1.4.2) Custom Layouts Reports & Marketing Google Analytics Setup Google Analytics Reporting with OAuth2 Service Accounts General Building URL's Cache Custom Cache Backends Output-Cache Custom Reports (previously SQL Reports) Custom Routes (Static Routes) Extending pimcore Class-Mappings - Overwrite pimcore models Custom persistent models Event API (EventManager) Events Working with Events Extend pimcore using composer Hook into the startup-process Google Custom Search Engine (Site Search ~ CSE) Integration Logging Magic Parameters Newsletter Notes & Events Placeholders Object Text Properties Predefined Properties Static Helpers System Settings Tag & Snippet Management Targeting & Personalization Managing Global Targeting Rules and Personas (Target Groups) Conditions Actions Document personalization Assign Personas based on visited pages Interacting with the targeting & personalization engine Targeting: Tips for developers UUID Support Versioning Website Settings Working with Sites (Multisite) Adaptive Design / Device Helper / Tool Application Logger Reference: Database Structure, Fields, Relations - "How are objects stored?" Console / CLI Web Services REST Webservice Localization Outputfilters LESS (CSS Compiler) Install lessc on your server (Debian) Mailing Framework Pimcore\Mail Class Best Practices CLI Script for Object Import Extending the Pimcore User with User - Object Relation High Traffic Server Setup (*nix based Environment)

5 Write-only database connection Administrator's Guide Backups Commandline Interface Backend Search Reindex Cache Warming Command-line Backup Image & Video Thumbnail Generator MySQL Tools Install Plugins Setting up WebDav Translations User Permissions User's Guide Backend Search and Properties Keyboard Shortcuts Working with WebDAV BitKinex as WebDAV Client Cyberduck as WebDAV Client NetDrive as WebDAV Client Windows Explorer as WebDAV Client Extensions Deeplinks into the pimcore Admin Interface Extension management using Composer Extension Manager Hooks Plugin Developer's Guide Example Plugin Anatomy and Design Plugin Backend UI Development and JS Hooks Useful Hints Adding custom main-navigation item Develop for pimcore GitHub Releasing a new Version FAQ Documentation Guideline Activity Draft Installation and Upgrade Quick Install Instructions (for professionals) The easiest way to install Pimcore is from your terminal. If you are having problems or need a more detailed guide, please have a look at this page. Install from package cd /your/document/root wget # OR curl -O unzip pimcore-latest.zip Install via Composer cd /your/working/directory curl -ss php php composer.phar create-project pimcore/pimcore./your-project-name dev-master cd your-project-name This will install latest pimcore from "master" branch. You can replace "dev-master" with specific version you need, see available releases. Create database:

6 mysql -u root -p -D information_schema -e "CREATE DATABASE pimcore charset=utf8;" Create a vhost in your web server that points to the document-root directory of the project and then visit the intallation page at domain name here)/install - which will bring up a configuration page to complete the database and admin user account details. Detailed Install Guide If you wish to manually install it or need further instructions or help, check this guide. Upgrade your pimcore pimcore comes with a built-in update tool in the admin interface that does all the work Please check the upgrade notes before you perform an update on your installation. See also: System Requirements Server Requirements Apache >= 2.2 PHP >= 5.4 MySQL / MariaDB >= version 5.5 ** We recommend to run pimcore only in a dedicated server environment (Linux based OS) for production. Webserver PHP offically support only for Apache (others may work, but not tested), nginx see here. mod_rewrite.htaccess (AllowOverride All) mod_php / FastCGI (FPM) incl. CLI binary Required Settings and Extensions memory_limit >= 128M mcrypt pdo_mysql or mysqli iconv dom simplexml gd exif file_info multibyte support (mbstring) zlib / zip / bz2 openssl CLI SAPI *** Recommended but not required imagick PECL (otherwise GD2 is used instead, which supports only JPG, GIF, PNG(24) and WBMP) opcache ( >= PHP 5.5) / APC PECL (PHP 5.4) memcache PECL curl (required if you want to use the Google API integrations) phpredis PECL ** Also take care of the upload limit in PHP. You can change this value in php.ini. *** possibility to create scheduled tasks (cron jobs,...) *nix based system (Recommended, not required)

7 FFMPEG one of the latest releases (Version >= 2.6) ( installation guide) (used for video thumbnails / transcoding) Ghostscript => Version >= 9.10 (used for PDF converter) LibreOffice => Version >= 4 (used for document converter) wkhtmltoimage / wkhtmltopdf Version >= 0.12 xvfb (in combination with wkhtmltox) mbayer html2text timeout (GNU core utils) pdftotext (poppler utils) inkscape (required for advanced SVG rasterizing - special image adapter for Web2Print) imgmin pngcrush jpegoptim MySQL / MariaDB InnoDB / XtraDB storage engine MyISAM storage engine MEMORY storage engine Insert table data Select table data Update table data Delete table data Create tables Drop tables Alter tables Manage indexes Create temp-tables Lock tables Execute Create view Show view What about Windows? Pimcore doesn't support Windows officially, but it should work almost perfectly. * For production we highly recommend a Linux based system. What about MAMP (MacOS)? Current versions of MAMP are shipped with a PHP version that has a bug and that might cause some problems. If you are planning to use MAMP for development, please take a look at this post: * For production we highly recommend a Linux based system. What about HHVM? Pimcore should run fine using HHVM (FastCGI). Browser Requirements Pimcore supports always the latest 2 versions of all 4 major browsers at the time of a release. Example: Release on April 11, 2014, latest 2 versions of IE at this time were 11 and 10. Google Chrome (Recommended) Mozilla Firefox Microsoft Internet Explorer / Edge Apple Safari Recommended Browser: Google Chrome Installation (Apache) This guide applies to both the blank (professionals) & the sample data packages (beginners). This tutorial shows how to install pimcore (blank & sample data) on an usual LAMP environment. Before you begin see the system requirements first. Download & copy to server First of all download the latest release or the sample data package. Extract the files and put it into the directory ( ) on your server. ( document-root pimcore doesn't work in subdirectories If you want to know why, read more here)

8 If you want to put it into a different directory (e.g. outside of the document-root for security issues), this is also no problem - you only have to adapt the path to the pimcore/config/startup.php in your bootstrap (index.php) file. Tip: Extract the archive directly on the server if possible, because it would take some time to upload the huge amount of files via FTP/SFTP. Apache Configuration The install package contains already the.htaccess file. If nothing went wrong during the transfer to the server you should already have it in your document-root. If not get it directly out of the download package. Nginx Configuration A (beta) nginx configuration is available here: Nginx Configuration Filesystem (Permissions) Pimcore requires write access to the following directories: /website/var and /pimcore. If you use plugins, it also requires access to the /plugi ns directory. If you know which user executes php on your system (PHP-FPM user, Apache user,...), simply give write access to the appropriate user. Execute the following commands on the shell (eg. via SSH, ) - replace YOURUSER and YOURGROUP with your configuration: chown -R YOURUSER:YOURGROUP website/var pimcore On Debian systems (and most other Linux distributions) mostly the www-data user executes the php files, just execute the following commands in your install directory. chown -R www-data:www-data website/var pimcore plugins MySQL Pimcore needs an MySQL database, the only thing you should assure is that the database uses UTF-8 as character set. If you create a new database just set the character set to utf8_general_ci. Note: You have to create the database manually. CREATE DATABASE name_of_database charset=utf8; Setup the Maintenance-Script To use the scheduled features (like, scheduler, backup, plugins,...) of pimcore you have to setup a scheduled task / cronjob. We recommend to run the task every 5 minutes, but at least it should be every 10 minutes. You can run the task also every minute since pimcore recognizes if there is an active job and executes only the jobs which are not active. For Linux systems with installed cron-daemon you can use the following for setup: Switch to the user which should execute the job, then type crontab -e in the terminal. Now your system editor should open. Add the following line at the end: */5 * * * * /path/to/php-cli /path/to/pimcore/cli/maintenance.php NOTE: Please be sure that the user which executes the task has the permission to write in /website/var/ if this is not possible you can use tools like sudo eg.: */5 * * * * sudo -u php /usr/bin/php /path/to/pimcore/cli/maintenance.php Please understand that we can't offer solutions for further operating systems. If you want your maintenance jobs to be executed multithreaded, and if you want to utilize the multhreaded job manager of pimcore, you have to enable pcntl in php. If pcntl is not available, jobs are processed in a sequential manner. There are some options available (for debugging,...) for the maintenance script, use the following command to get an overview:

9 /path/to/php-cli /path/to/pimcore/cli/maintenance.php --help There are much more options available like specifing jobs to be executed: /path/to/php-cli /path/to/pimcore/cli/maintenance.php -j scheduledtasks,logmaintenance With this arguments only the 2 defined jobs are executed, all other will be ignored. This allows you to plan your resources an jobs in more detail. For more information see the help-message. Webinstaller Now we are ready to launch the web-installer if everything is ok you should be able to launch the installer with the adress main.com/install/ Now just follow the installer. The installer doesn't appear. What to do? As you can imagine there could be many reasons why it does not work, first of all check the system requirements. Then follow the above instructions step by step and check if everything conforms. Also check the debug log at /website/var/log/debug.log it will give you detailed infomation about the status of Pimcore. Virtual Hosts (Windows) Make sure you have the correct settings, specifically: Allowoverride All <VirtualHost *:80> DocumentRoot "C:/sites/pimcore" ServerName pimcore.local <directory "C:/sites/pimcore"> Allowoverride All allow from all </directory> </VirtualHost> After the Webinstaller (optional but recommended) Open "Settings" -> "System" in the administration, and configure your new installed system. We reccomend the fill out the following options: Timezone PHP-CLI binary path Languages (for the website or data in objects) HTTP-Connectivity... Nginx Configuration This guide is still unfinished, it only includes the working server config not other nginx requirements. This guide is based on the general/apache install guide. Installation on nginx is entirely possible, and in my experience quite a lot faster then apache.i won't dive into how niginx is installed etc. But I will share the nginx configuration which works.

10 nginx configuration Below is the configuration for a nginx server ( just the server bit, the http etc. bit can be kept default, as long as you include mime.types ). The configuration was derived from the.htaccess in Pimcore v (Build: 2783) # mime types are covered in nginx.conf by: # http { # include mime.types; # # mod_status is not (yet) available for nginx, so that directive is skipped server { listen 80; server_name pimcore.loc; root /var/www/pimcore/; access_log /var/www/pimcore/website/var/log/nginx_access.log; error_log /var/www/pimcore/website/var/log/nginx_error.log error; # ASSETS: check if request method is GET (because of WebDAV) and if the requested file (asset) exists on the filesystem, if both match, deliver the asset directly # nginx doesn't do double conditions, so we concat a teststring, if it's "AB" both checks are true set $getassets ""; if ($uri ~* ^/website/var/assets) { set $getassets "${getassetsa"; if ($request_method = GET) { set $getassets "${getassetsb"; if ($getassets = "AB") { rewrite ^ $uri$args last; # You could also use the location directive below, which is MUCH faster then the if statements above # However, this doen't check for request method GET #location ~* ^/website/var/assets { # try_files $uri /index.php?$args; # index index.php; # # allow access to plugin-data and core assets ( done by excluding.*/static and static) # forbid the direct access to pimcore-internal data (eg. config-files,...) # ~* = case-insensitive location ~* ^(/plugins/(?!.*/static).* ^/pimcore/(?!(static modules/3rdparty)).* /website/var/(?!tmp assets plugins areas) ^.*modules/.*/static.*) { return 403; # basic zend-framework setup see: location / { # First attempt to serve request as file, then as directory, then fall back to index.php try_files $uri $uri/ /index.php?$args; index index.php; # pass the PHP scripts to FastCGI server listening on :9000 location ~ \.php$ { # Zero-day exploit defense. # # Won't work properly (404 error) if the file is not stored on this server, which

11 is entirely possible with php-fpm/php-fcgi. # Comment the 'try_files' line out if you set up php-fpm/php-fcgi on another machine. And then cross your fingers that you won't get hacked. try_files $uri =404; # This configuration depends on your set-up. The default nginx.conf contains an example for your setup. # fastcgi_pass :9000; (if you cannot run on a socket) fastcgi_pass unix:/var/run/php5-fpm.sock; fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; include fastcgi_params; # Set a maximum execution time. This should be the same as your php_max_exec time and php-fpm pool request_terminate_timeout to prevent # any gateway timeouts and/or worker processes being tied up. fastcgi_read_timeout 60; # cache some files # ~* = case-insensitive location ~* \.(jpe?g gif png bmp ico css js pdf zip htm html docx? xlsx? pptx? txt wav swf avi mp\d)$ { access_log off; log_not_found off; try_files $uri $uri/ /website/var/assets$uri /index.php?$args;

12 expires 1w; Quick Start Package / Sample Data / Boilerplate Installation The demo / sample data package contains the entire pimcore package (based on the nightly build) including example templates and contents, it is also available online here. How to install the package Download the sample data package from our download page Extract the ZIP package to your document root Follow the normal installation guide Demo as Docker image The demo is also available as a Docker image. Upgrade Notes Update from Version 2.x to Version 3 PHP Namespaces introduced! Version 3 of pimcore should be 100% backward compatible to version 2, but of course this also depends on how you're using pimcore in your custom code. What changed: Basically we replaced the prefixed class names by real namespaces so the full name (incl. namespace) of a class stays the same. We've created a custom autoloader which does not only load the classes but also creates alias on-the-fly for the legacy class names. In our opinion this is a very simple and lightweight solution with nearly no overhead. Example: Pimcore_Controller_Action_Frontend => Pimcore\Controller\Action\Frontend The important thing here is that both class names work at the same time, so you can do the following for instance: use Pimcore\Model\Object\Service; $service = new Service(); if($service instanceof \Object_Service) { // yes that will be true ;-) So there should be no need to change existing code and you can further develop your projects using the old style or by using them together at the same time. Unfortunately it wasn't possible to do this 1:1 renaming for all of the classes, there are 2 exceptions: abstract classes, interfaces and list classes. The following should give you an overview of what has changed: Abstracts: Element_Abstract => Element\AbstractElement (in the namespace Pimcore\Model) Object_Abstract => Object\AbstractObject Pimcore_API_Plugin_Abstract => Pimcore\API\Plugin\AbstractPlugin... Interfaces:

13 Element_Interface => Element\ElementInterface Pimcore_API_Plugin_Interface => Pimcore\API\Plugin\PluginInterface... Lists: Document_List => Document\Listing Object_Product_List => Object\Product\Listing Asset_List => Asset\Listing This matches with the naming conventions of Zend Framework 2 and Symfony, so the idea should be clear. But this was not the reason to change it, we had no other choice since reseved keywords cannot be used as a class name or segment of a namespace. Again, this changes are also automatically handled by the custom autoloader, so you can still use both at the same time => 100% backward compatible. Another thing that has changed is that you don't have to use this ugly call Object_Abstract::getById(); anymore, to get an object of an unknown type, now you can simply use Object::getById() as known from documents and assets in the past. Known Issues: 1.) Dealing with Zend Framework controller, action, view helpers and plugins Unfortunately the ZF uses often the name of the class as a key to retrieve plugins and helpers. If you're using pimcore core helpers or plugins (your project helpers/plugins can stay the same) in your code you have to change the name when retrieving the plugins. Example: 2.x style: $front = \Zend_Controller_Front::getInstance(); $front->unregisterplugin("pimcore_controller_plugin_cache"); 3.x style $front = \Zend_Controller_Front::getInstance(); $front->unregisterplugin("pimcore\\controller\\plugin\\cache"); So it's a good practise to search you project files for "unregisterplugin", "addprefix",... Version History

14 Version Build Date th March th March th April th May th May th May th July th August th September th October th November th January th January th February th May th June th October th February th February th April nd July th August th October nd March th April th November th December th January th February th April th May nd July st September th January th January th January th January th March th March th May th September Develop with pimcore Overview Documents Quick Start Guide Types Controllers Templates (Views) Editables Areablock Create your own bricks Area Block Checkbox Date Href (1 to 1 Relation) Image

15 Input Link Multihref Multiselect Numeric Renderlet Select Snippet (embed) Table Textarea Video WYSIWYG PDF (Document) Helpers (Available View Methods) Document-Types Thumbnails Glossary Redirects Document Lists Localize your Documents (i18n) Translation of Document Editables Website Translations / Shared Translations Navigation Document Tree Copy documents and rewrite relations to new documents Hardlinks for documents Assets Asset Lists Custom Settings (Properties) Image Thumbnails Video Thumbnails Asset Document Thumbnails (PDF, docx, odf,...) Asset (Localized) Metadata Data Objects Object Classes Data Fields Date, Date & Time, Time Geographic Fields - Point, Bounds, Polygon Href, Multihref, Objects - Relations, Dependencies and Lazy Loading Localized Fields Non-Owner Objects Number Fields - Numeric, Slider Other Fields - Image, Image with Hotspots, Checkbox, Link Select Fields - Select, Multiselect, User, Country, Language Structured Data Fields Key Value Pairs Structured Data Fields - Classification Store Structured Data Fields - Fieldcollections Structured Data Fields - Objectbricks Structured Data Fields - Structured Table Structured Data Fields - Table Text Input Fields - Input, Password,Textarea, WYSIWYG Video Field Layout Elements Object Lists External System Interaction Inheritance Custom Icons Locking fields Object Variants Object Preview Object Tree Custom icons and style in object-tree (since 1.4.2) Custom Layouts Reports & Marketing Google Analytics Setup Google Analytics Reporting with OAuth2 Service Accounts General Building URL's Cache Custom Cache Backends Output-Cache Custom Reports (previously SQL Reports) Custom Routes (Static Routes) Extending pimcore Class-Mappings - Overwrite pimcore models Custom persistent models

16 Event API (EventManager) Events Working with Events Extend pimcore using composer Hook into the startup-process Google Custom Search Engine (Site Search ~ CSE) Integration Logging Magic Parameters Newsletter Notes & Events Placeholders Object Text Properties Predefined Properties Static Helpers System Settings Tag & Snippet Management Targeting & Personalization Managing Global Targeting Rules and Personas (Target Groups) Conditions Actions Document personalization Assign Personas based on visited pages Interacting with the targeting & personalization engine Targeting: Tips for developers UUID Support Versioning Website Settings Working with Sites (Multisite) Adaptive Design / Device Helper / Tool Application Logger Reference: Database Structure, Fields, Relations - "How are objects stored?" Console / CLI Web Services REST Webservice Localization Outputfilters LESS (CSS Compiler) Install lessc on your server (Debian) Mailing Framework Pimcore\Mail Class Best Practices CLI Script for Object Import Extending the Pimcore User with User - Object Relation High Traffic Server Setup (*nix based Environment) Write-only database connection Overview The architecture Pimcore follows the MVC pattern, if you don't know what that means/is please read this article first 0%93view%E2%80%93controller. Since Pimcore depends on Zend Framework it also uses the MVC implementation of ZF. If you are new to the Zend Framework or the ZF MVC, please also read before you go to the next steps. Basics First of all you have to know a few basics (you also can find them in the editor guide). Pimcore has 3 core modules: Documents This is the classic application of a web content management system (WCMS). You create templates, make them editable and create pages (fill them with content) which can be viewed under the specified address. Assets Manage static resources such as images, documents (pdf, docx, ), videos, They can be inserted into the pages/snippets.

17 Objects (Classes) The most powerful module. This can be used for RAD (Rapid Application Development). You can easily create a data model with drag & drop, the system creates the model for you in the backend (yes also the php files to work with). Explanation of the files/folders After the installation there should be 3 folders of Pimcore: /pimcore /plugins /website. The only really interesting one is /website, this folder includes all things concerning your website. The website folder Documents How documents work Every document has a path (in the document tree) that directly represents the address in the browser (eg. ent). When a visitor requests a page at a specific address, Pimcore uses a custom route in the front controller that determines if there is a suitable document for the requested address. If there is a document, the route then uses the controller, action and view defined in the document's settings, passing them to the dispatcher along with the document itself. The requested document is then available as a property in the controller, which is defined for the document ($this->document). Read more on Controllers here. File structure There are three important folders within the /website folder that correspond to documents. Path Description Example /website/controllers /website/views/scripts /website/views/layouts The controllers directory, the naming follows the Zend Framework naming-conv ention The template directory, the naming (subfolders) follows also the naming-convention of ZF (/website/views/scripts/[controller]/[action]. php) Optionally: here you can put your layouts which are used by pages ContentController.php /website/views/scripts/content/view-single. php (if the above controller were to contain an action called viewsingleaction) layout.php (this is the default when enabled) Document Configuration It is possible to define the controller/action and the template directly in the document, though not all of them are necessary. The table below shows what configurations are possible: Type Controller Action Template Description

18 1 The specified controller/action is executed, based on the names of them the right template is rendered (eg. views/scripts/controller/ action.php) 2 Same as above but the template specified is rendered and not the autodiscovered template 3 Renders the template with the default controller/action, this is practical if there is only templating stuff Optionally you can specify a module to each of the above combinations, this is useful if you want to use controllers/actions or templates out of plugins which are simply another ZF module. The default module (when empty) is website Pimcore is shipped with a default controller containing a default action, which is called when only a template is given to the document, you can edit the detaults in Settings -> System : Putting Configuration & Filestructure together The previous two sections outline how documents can be configured to use different controller/action and views. The table below represents the three configurations: Type 1 Only Controller/Action Type 2 Controller/Action and Template Type 3 Only Template

19 If you have a controller named content wi th an action named defaultaction the template file required will be /website/views/scripts/content/default.ph p The specified controller/action is executed, but then the specified template is rendered The default controller/action defined in the system settings are executed, then the specified template is rendered. Document Properties Properties are a very powerful tool in combination with documents. Some examples where properties can be very useful in combination with documents. Navigation Header Images Sidebars SEO If you build the navigation based on the document-tree, sometimes you don't want a page to appear in the navigation, in this case you can define a property (eg. excludefromnav [boolean]) while iterating through the tree in the templates, check the current document for this property if its true don't display it in the navigation Often there are header images on a website, if you don't want to define it for every page, you can use the properties, because of their inheritance you can define a default one at the root document, and overrule this in a deeper document. See header images You also can use properties for SEO. It's very painful to define a nice title and description for every page on your site, with properties this is not necessary. Some more examples: Protected areas (closed user groups) Change the appearance of the website depending on the properties (eg. Microsites, nested sites) Mark them for some automated exports (PDF, RPC's, ) and much more... As you can see there are really useful cases for properties, feel free to assess them. A few Facts Documents follow the MVC pattern; therefore, Pimcore therefore requires that there is at least one controller with an action and a view file. Pimcore comes with a DefaultController containing a defaultaction and a view file. Because of the MVC architecture, there is a clear seperation between your data, the functionality and the templates. Pimcore uses an autoloader Zend_Loader_Autoloader, so you don't have to include your files manually, but you have to respect the naming of the files and classes (PSR-0, PEAR, ZF,...). Or you create your own autoloader to use external libraries such as e zcomponents, how this works you can read here. Pimcore has no need for a template engine since it uses Zend Views, which offer many advantages. If you don't know how Zend _View works, please read more here. The normal way to create templates for Pimcore is to use pure PHP. There's no new template syntax to learn - just code what you want - feel free! Although the templates are written in PHP, there is a clear seperation as mentioned before, so don't worry :) If you're still keen to use a template engine such as Smarty, you can do! Quick Start Guide Introduction In this Quick Start Guide we are going to create a basic contentpage. This contentpage consists of a controller/action and a view. First I recommend you to read the overview. This helps you to understand what we are doing here. Furthermore I recommend you to read the documents-page. This page explains you what we are doing here. Together with this Quick Start Guide you should be able to understand pimcore documents in general. The first few steps will be done directly in the code. Only for the last steps we are logging into the backend. Create a new controller/action Create a new PHP file in the folder /website/controllers and name it to ContentController.php Open the file an put the following content into it.

20 This is a basic setup of an action/controller. The method enablelayout registers \Zend_Layout to decorate our contentpage. In the defaultaction we can put some custom code or assign values to the template. <?php use Website\Controller\Action; use Pimcore\Model\Asset; class ContentController extends Action { public function defaultaction () { $this->enablelayout(); Do not end the file with a "?>". Create the template Now we can create the templates for our new contentpage. Create a new folder in /website/view/scripts and name it like the controller (in this case content ). Put a new PHP file into this folder and name it like our action (default.php). Then we can put some template code into it, for example: <?php $this->layout()->setlayout('standard');?> <h1><?= $this->input("headline", ["width" => 540]);?></h1> <?php while ($this->block("contentblock")->loop()) {?> <h2><?= $this->input("subline");?></h2> <?= $this->wysiwyg("content");?> <?php?> Add Layout Pimcore uses the advantages of Zend_Layout out of the ZF, for details please read more here about it. Because we have enabled the layout engine in our controller, we can use layouts to wrap our contentpage with another template which contains the main navigation, a sidebar, With this code: <?php $this->layout()->setlayout('standard');?> We tell the engine that we want to use the layout standard. Now create a new php file in the folder /website/views/layouts and name it to standard.php (just like the name of the layout appending.php). Then we can also put some HTML and template code into it:

21 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" " <html xmlns=" <head> <meta http-equiv="content-type" content="text/html; charset=utf-8" /> <link rel="stylesheet" type="text/css" href="/static/source/css/screen.css" /> </head> <body> <?= $this->layout()->content?> </body> </html> The code <?= $this->layout()->content?> is the placeholder where your contentpage will be inserted. Adding the layout/template to a page Now we need to connect the template to a page. This will now be done in the pimcore-backend. First click on the left under "Documents" at "home" and then select the tab "settings" in the newly opened window. You will then see something like this: In controller and action you will have to write the name of the controller and the name of the action. After you have saved it you can test this new controller and action. Testing it After clicking on the edit tab you should see something like this:

22 which will look like this (in the frontend) when its filled with content: Types There are 5 different types of documents in the default Pimcore package (namespace Pimcore\Model). These are: Document\Page A page which can be filled with content and is available through an URL (eg. /about_us/imprint) Document\Snippet Used for simple HTML pieces (teaser elements, sidebars, ), they are similar to pages but they don't have settings like Title, Description. Snippets are accessible via their URL address by default. Document\Link

23 Simply a link, they exist because it's possible to create the navigation through the document-tree. Document\Folder Used to manage your documents in folders, or to define custom properties. Document\ Used to send s with Pimcore\Mail, they are similar to Document\Snippets and the have additional settings for receivers. About Document\ The Document\ extends the Document\Snippet and can be used to send s The settings tab contains a group of related properties, i.e. Subject, From, To, CC, BCC If the logging isn't deactivated by the Pimcore\Mail Class you can see the the sent s in the "Sent s" Tab. (The Column "Dynamic params" is hidden by default so you have to unhide it manually) Dynamic Parameters passed when was sent:

24 Controllers The controller contains the functionality of a page or snippet. Pimcore offers an abstract class (Pimcore\Controller\Action\Frontend), which must be implemented by your controllers (or use Website\Controller\Action out of your website folder). The naming of the file and the class is the same as in Zend Framework. Because website is configured as the default module in the front controller, you don't have to add a prefix to your controller classnames. Examples Controllername Filename Classname content ContentController.php ContentController news NewsController.php NewsController Put your controllers in the following directory: /website/controllers There are some helpers defined in Pimcore\Controller\Action\Frontend (the abstract class your controller must extend). But the best way is to use the Website\Controller\Action (/website/lib/website/controller/action.php) which is already shipped with Pimcore and implements the Pim core\controller\action\frontend and can be modified and extended the way you need. You can use: Method Arguments Description enablelayout none Register Zend_Layout for you, if called you can use this layout in your views. disablelayout none The opposite of enablelayout. disableviewautorender none Disables the auto renderer for the current action, for actions which don't need a view. removeviewrenderer none Removes the view renderer. This is not only for the current action, once the renderer is removed you can't render a view anymore, no matter which scope. forcerender none Call this method to force rendering, this can be useful when you need the response directly. setdocument Pimcore\Model\Document $document With this method you can set the default document for the current action which will be available in the view and the action with $this->document. If you want to use one of the methods (hooks) below which are offered by ZF you have to call their parent: predispatch postdispatch init There are also some properties which can be useful: Name Type Description document Document Reference to the current document

25 editmode boolean True if you are in editmode (admin) Example use Pimcore\Model\Document;... public function init() { parent::init(); // example of properties if ($this->document instanceof Document\Page) { // do something... // your custom code... public function predispatch() { parent::predispatch(); if ($this->editmode) { // do something only in editmode... // your custom code Templates (Views) As mentioned already before, Pimcore uses \Zend_View as its template engine, and the standard template language is PHP. The Pimcore implementation of \Zend_View namely Pimcore\View offers special methods to increase the usability: Method inc template cache translate glossary Description Use this function to directly include a document Use this method to include a template In template caching i18n / translations Glossary Additionally you can use the Zend_View helpers which are shipped with ZF. There are some really cool helpers which are really useful when used in combination with Pimcore. Some Examples Method action headmeta headtitle Description d.view.helpers.initial.action d.view.helpers.initial.headmeta d.view.helpers.initial.headtitle

26 translate d.view.helpers.initial.translate You can use your own custom Zend_View helpers, or create some new one to make your life easier. There are some properties which are automatic available in the view: Name Type Description editmode boolean Is true if you are in editmode (admin), false if you are on the website controller Pimcore\Controller\Action\Frontend A reference to the controller document Document Reference to the current document object you can directly access the properties of the document in the view (eg. $this?document?gettitle();) Editables (Placeholders for content) Pimcore offers a basic set of placeholders which can be placed directly into the template. In editmode they appear as an editable widget, where you can put your content in. While in frontend-mode the content is directly embedded into the HTML. There is a standard scheme for how to call the editables. The first argument is always the name of the element (as string), the second argument is an array with multiple options (configurations) in it. Because most of the elements are based directly on Ext.form elements, you can also pass configurations directly to the Ext components (see API reference of Ext) Click here to get a detailed overview about the editables. Example <!-- creates a input in editmode (admin) and directly outputs the text in frontend --> <h1><?= $this->input("headline", ["width" => 540]);?></h1> <!-- advances template --> <?php $this->layout()->setlayout('standard');?> <h1><?= $this->input("headline", ["width" => 540]);?></h1> <h1><?= $this->numeric("number", ["width" => 540]);?></h1> <?php while ($this->block("contentblock")->enumerate()) {?> <?php if($this->editmode) {?> <?= $this->select("blocktype",[ "store" => [ ["wysiwyg", "WYSIWYG"], ["contentimages", "WYSIWYG with images"], ["video", "Video"] ], "onchange" => "editwindow.reload.bind(editwindow)" ]);?> <?php?> <?php if(!$this->select("blocktype")->isempty()) { $this->template("content/blocks/".$this->select("blocktype")->getdata().".php");?> <?php?> Editables The editables are placeholders in the templates, which are input widgets in the admin (editmode) and output the content in frontend mode. Area Areablock

27 General Block Checkbox Date Href (1 to 1 Relation) Image Input Link Multihref Multiselect Numeric PDF (Document) Renderlet Select Snippet (embed) Table Textarea Video WYSIWYG Most of the editables use ExtJS widgets, these editables can be also configured with options of the underlying ExtJS widget. For example: <?= $this->input('iframe_src', [ 'grow' => true, 'cls' => 'my-css-class' ]);?> You can also use Zend_Json_Expr to add "native" Javascript to an editable: <?= $this->input('iframe_src', [ 'validator' => new \Zend_Json_Expr(' function(value){ if(value.match(/ return true; else{ return "invalid"; ') ]);?> Areablock The areablock is the content construction kit for documents offered by pimcore. The concept is like you know it already from the block element. The difference is that you can insert predefined "mini applications" called bricks into an areablock.

28 Integrate an areablock in a template Similar to the other document editables, an areablock can be integrated in any document view template as follows: <?= $this->areablock('myareablock')?> advanced usage with allowed areas:

29 <?= $this->areablock("myareablock",array( "allowed"=>array("iframe","googletagcloud","spacer","rssreader"), "group" => array( "First Group" => array("iframe", "spacer"), "Second Group" => array("rssreader") ), "areablock_toolbar" => array( "title" => "", "width" => 230, "x" => 20, "y" => 50, "xalign" => "right", "buttonwidth" => 218, "buttonmaxcharacters" => 35 ), "params" => array( "iframe" => array( // some additional parameters / configuration for the brick type "iframe" "parameter1" => "value1", "parameter2" => "value2" ), "googletagcloud" => array( // additional parameter for the brick type "googletagcloud" "param1" => "value1" ) )));?> Accessing the parameters by name from within the brick: //echo the value of parameter named "param1" for this brick echo $this->param1; Sorting items in the toolbar

30 //available areas: A,B,C,D,E,F $allowedareas = ['D', 'E', 'F']; $sorting = ['F','E','D']; echo $this->areablock($id, [ 'allowed' => $allowedareas, 'sorting' => $sorting //... ]); //output: F,E,D echo $this->areablock($id, [ 'sorting' => $sorting //... ]); //output: F,E,D, A,B,C echo $this->areablock($id, [ 'allowed' => $allowedareas, //... ]); //output: D,E,F echo $this->areablock($id, [ //... ]); //output: A,B,C,D,E,F Configuration Name Type Description allowed array An array of area-id's which are allowed for this tag. The order of items in the array is also used as the default sorting, but of course you can combine this configuration with "sorting" sorting array An array of area-id's in the order you want to display them in the toolbar. params array Optional Parameter, this can also contain additional brick-specific configurations, see "brick-specific configuration" group array Array with group configuration (see example above) manual bool forces the manual mode, which enables a complete free implementation for areablocks, for example using real <table> elements... example see below reload bool set to true, to force a reload in editmode after reordering items (default: false) toolbar bool set to false to not display the extra toolbar for areablocks (default: true) dontcheckenabled bool set to true to display all installed area bricks, regardless if they are enabled in the extension manager limit int limit the amount of elements areablock_toolbar array Array with option that allows you to change the position... of the toolbar

31 areadir string absolute path (from document-root) to an area directory, only areas out of this path will be shown eg. /website/views/customareas/ Brick-specific configuration Brick-specific configurations are passed using the params configuration (see above). Name Type Description forceeditinview bool if a brick contains an edit.php there's no editmode for the view.php, if you want to have the editmode enabled in both templates, enable this option Example: <?= $this->areablock("myarea", array( "params" => array( "my_brick" => array( "forceeditinview" => true ) ) ));?> Methods Name renderindex() getcount() getcurrent() getcurrentindex() getelement() Description renders only one specific block within the areablock total count of blocks current number of block (useful within area bricks) get the current index (index is different from position, as you can move block around) get an element out of an areabrick How to create "bricks" for the areablock? Please read more here..." Using manual mode The manual mode offers you the possibility to deal with areablocks the way you like, this is for example useful with tables: <?php $areablock = $this->areablock("myarea", array("manual" => true))->start();?> <table width="100%"> <?php while ($areablock->loop()) {?> <?php $areablock->blockconstruct();?> <tr> <td> <?php $areablock->blockstart();?> <?php $areablock->content();?> <?php $areablock->blockend();?> </td> </tr> <?php $areablock->blockdestruct();?> <?php?> </table> <?php $areablock->end();?>

32 Create your own bricks Architecture of a brick The architecture is simple and straightforward: You can put your own bricks into /website/views/areas downloaded bricks from the marketplace are located in /website/var/areas On the right side you can see how a brick can be structed. Mandatory files are area.xml and view.php. Optional files are action.php, edit.p hp and icon.png. If you put an icon.png (16x16 pixel) into the brick's folder, this icon is added automatically to the toolbar, there's no need to specify the icon in the area.xml again. The area.xml contains some meta-infos concerning the brick, and the view.php is a simple \Zend_View - script where you can use all pimcor e editables. How to create a brick First of all create the area.xml containing the meta-data- For example: <?xml version="1.0"?> <zend-config xmlns:zf=" <id>iframe</id> <name>iframe</name> <description>embed contents from other URL (websites) via iframe</description> <!-- the icon is optional, see above for details (this node is normally used to refer to an icon in the pimcore core icon-set) --> <icon>/pimcore/static/img/icon/html.png</icon> <version>1.0</version> <mycustomconfig>myvalue</mycustomconfig> </zend-config> The bold definitions are mandatory, but you can add your custom configuration for the brick as well. In the following example you can see how to access the configuration and your custom properties in the configuration. Then you have to create your view script called view.php. There is an info-objects which contains informations about the current brick. You can access this info-object in your view with $this->brick, the object contains the configuration from above (\Zend_Config) an some other metadata (described later). For example:

33 <?php if ($this->editmode) {?> <!-- with <?= $this->brick->getpath();?> you get the path to the area out of the info-object --> <link rel="stylesheet" type="text/css" href="<?= $this->brick->getpath();?>/editmode.css" /> <div> <h2>iframe</h2> <div> URL: <?= $this->input("iframe_url");?> </div> <br /> <b>advanced Configuration</b> <div> Width: <?= $this->numeric("iframe_width");?>px (default: 100%) </div> <div> Height: <?= $this->numeric("iframe_height");?>px (default: 400px) </div> <div> Transparent: <?= $this->checkbox("iframe_transparent");?> (default: false) </div> </div> <?php else {?> <?php if (!$this->input("iframe_url")->isempty()) {?> <?php // defaults $transparent = "false"; $width = "100%"; $height = "400"; if(!$this->numeric("iframe_width")->isempty()) { $width = (string) $this->numeric("iframe_width"); if(!$this->numeric("iframe_height")->isempty()) { $height = (string) $this->numeric("iframe_height"); if($this->checkbox("iframe_transparent")->ischecked()) { $transparent = "true";?> <iframe src="<?= $this->input("iframe_url");?>" width="<?= $width;?>" height="<?= $height;?>" allowtransparency="<?= $transparent;?>" frameborder="0" ></iframe> <?php?> <?php?> Once the code is in place, the areablock will appear as an extension in the Extension Manager (Extras->Extensions->Manage Extensions). From there, you have to enable the areablock.

34 That's all. In this example we need no action.php because everything is managed with editables. The info-object ($this->brick) As mentioned already above, the info-object contains useful informations about the current brick. The info-object ist available as a common view variable ( $this->brick) in your view scripts (edit.php and view.php). In the follwing table you can see all available methods in your views (both in edit.php and view.php): Method $this->brick->getid() $this->brick->getconfig() $this->brick->getindex() $this->brick->getpath() Description returns the id of the current brick returns the configuration (Zend_Config) out of area.xml (to get your custom properties,... ) returns the current index inside the areablock returns the (web-)path to the current brick, this is useful for embedding external stylesheets, javascripts,... For an example usage see the above example (referencing the stylesheet). Configuration in Editmode (edit.php) To allow users to add data to the brick you have to the file edit.php which can include HTML and editables. When this file is present an icon will appear for the user which can be clicked to display and edit the editable fields. Warning: Using edit.php will disable all editables in view.php (they appear like in the frontend, but cannot be edited). You cannot have editables in both files. Contents of edit.php: Class: <?= $this->input('class');?> Accessing the data in view.php

35 <?php $class = ''; if(!$this->input('class')->isempty()) { $class = $this->input('class')->getdata();?> Actions for Bricks (action.php) Sometimes a brick is more than just a view-script an contains some functionality which shouldn't be directly in the view. In this case you can use the action.php. The action.php doesn't contain a "real" ZF - compatible controller/action it is just a little helper to get some logic and code out of the view, but the behavior is like in a common ZF-controller. To use this feature simply create a new file called action.php in your brick directory, and insert the following code (and replace "MyBrickName" with the name of your brick): <?php namespace Pimcore\Model\Document\Tag\Area; use Pimcore\Model\Document; class MyBrickName extends Document\Tag\Area\AbstractArea { public function action () { $myvar = $this->_getparam("myparam"); //... $this->view->myvar = $myvar; /** * Executed after a brick is rendered */ public function postrenderaction(){ //... /** * Returns a custom html wrapper element (return an empty string if you don't want a wrapper element) */ public function getbrickhtmltagopen($brick){ return '<span class="customwrapperdiv">'; public function getbrickhtmltagclose($brick){ return '</span>'; The method action(); is called automatically before rendering the view.php or edit.php. Of course you can create your own methods in the class, but ensure that your class extends Pimcore\Model\Document\Tag\Area\AbstractArea ;-) The method postrenderaction() ist called after a brick is rendered. The methods "getbrickhtmltagopen" and "getbrickhtmltagclose" allowes you to use custom Brick wrappers (if the methods aren't specified - the regular "Pimcore-HTML-Brick-Wrappers" will be inserted) You can access the action.php Object in your views with $this->actionobject

36 Inside this class/object there are some general methods available (inherited from Pimcore\Model\Document\Tag\Area\AbstractArea) which offers you some handy features like the info-object (as described above), the config and much more... to see it in detail check out the contents of Pimcore\Model\Document\Tag\Area\AbstractArea, the methods are really self-describing. For a detailed example please have a look into our examples below. Enabling bricks Self created bricks are extensions just like those downloaded from the repository. Before they can be used, they need to be enabled in the extension manager (Extras > Manage Extensions) Examples You can find many examples in the demo / quick start package: Area The area editable is similar to the areablock editable, the only difference is that the area bricks are not wrapped into a block element, and the editor cannot choose which area is used, this has to be done at the editable configuration in the template. This editable is especially to use bricks also outside an areablock, for example in common block elements or just the element itself. Configuration Name Type Description type string ID of the brick which should be used in this area params array Optional Parameter see areablock for details Methods Name Description Example <div style="margin: 20px;"> <?= $this->area("myarea", array("type" => "googleanalyticstagcloud"));?> </div> Block A block element is a iterating component which is really powerful. Basically a block is only a loop, but you can use other editables in this loop, so it's possible to repeat a set of editables to create a structured page. The items in the loop as well as their order can be defined by the editor with the block controls. Configuration Name Type Description limit integer Max. amount if iterations default integer If block is empty, this specifies the iterations at startup manual bool forces the manual mode, which enables a complete free implementation for blocks, for example using read <table> elements... example see below Methods Name getcount() getcurrent() Description Get the total amount of iterations Get the current index while looping

37 getelements() Return a array for every loop to access the defined childs The Block Controls Control Operation + Add a new block at the current position - Remove the current block Move block up Move block down Basic usage <?php while($this->block("contentblock")->loop()) {?> <h2><?= $this->input("subline");?></h2> <?= $this->wysiwyg("content");?> <?php?> This will result in editmode in this: And in the frontend:

38 Advanced Usage <?php while($this->block("contentblock")->loop()) {?> <?php if($this->editmode) {?> <?= $this->select("blocktype",array( "store" => array( array("wysiwyg", "WYSIWYG"), array("contentimages", "WYSIWYG with images"), array("video", "Video") ), "reload" => true ));?> <?php?> <?php if(!$this->select("blocktype")->isempty()) { $this->template("content/blocks/".$this->select("blocktype")->getdata().".php");?> <?php?> <?php while($this->block("teasers",array("limit" => 2))->loop()) {?> <?= $this->snippet("teaser")?> <?php?> Example for getcurrent() <?php while ($this->block("myblock")->loop()) {?> <?php if ($this->block("myblock")->getcurrent() > 0) {?> Insert this line only after the first iteration<br /> <br /> <?php?> <h2><?= $this->input("subline");?></h2> <?php?> Using manual mode The manual mode offers you the possibility to deal with block the way you like, this is for example useful with tables:

39 <?php $block = $this->block("gridblock", array("manual" => true))->start();?> <table> <tr> <?php while ($block->loop()) {?> <?php $block->blockconstruct();?> <td customattribute="<?= $this->input("myinput")->getdata()?>"> <?php $block->blockstart();?> <div style="width:200px; height:200px;border:1px solid black;"> <?= $this->input("myinput");?> </div> <?php $block->blockend();?> </td> <?php $block->blockdestruct();?> <?php?> </tr> </table> <?php $block->end();?> Using fluent interfaces // load document $doc = \Pimcore\Model\Document\Page::getByPath('/en/basic-examples/galleries'); // Bsp #1 get the first picture from the first "gallery-single-images" brick $image = $doc ->getelement('content') // view.php > $this->areablock('content') ->getelement('gallery-single-images')[0] // get the first entry for this brick ->getblock('gallery')->getelements()[0] // view.php > $this->block("gallery")->loop() ->getimage('image') // view.php > $this->image("image") ; var_dump("bsp #1: ". $image->getsrc()); Checkbox Configuration Name Type Description reload boolean Set to true to reload the page in editmode after changing the state Accessible Methods & Types Name Type Description value boolean Status of the checkbox ischecked() boolean Get status of the checkbox

40 Simple Example <?= $this->checkbox("mycheckbox")?> Advanced Example <?php if( $this->checkbox("mycheckbox")->ischecked() ) {?> //Code <??> Date Basic Usage The following code will create a simple date widget in editmode. In frontend it will output the date as defined in output. Localization (output-format,...) is automatically used from the global locale, which is either defined by the underlying document or in your code ( \ Zend_Registry::set("Zend_Locale", new \Zend_Locale("en")); ), for more information, please read the topic pimcore localization. Simple Example // simple example <?= $this->date("mydate", array( "format" => "d.m.y" ));?> Configuration Name Type Description format string A String which describes how to output the date see Ext.form.DateField. Additionally you can use every configuration property of Ext.form.DateField to customize the date widget in editmode. Href (1 to 1 Relation) Href provides to create a reference to an other element in pimcore (document, asset, object). This can be useful to link a video for example (in editmode show the href to link the video out of the assets, outside embed a object code an make a reference to the video.). In frontend-mode the href returns the path of the linked element. Configuration Name Type Description types array Allowed types (document, asset, object), if empty all types are allowed subtypes array Allowed subtypes grouped by type (folder, page, snippet, image, video, object,...), if empty all subtypes are allowed (see example below) classes array Allowed object class names, if empty all classes are allowed reload true false true triggers page reload on each change width int Width of the field in pixel uploadpath string Target path for (inline) uploaded assets Properties and methods

41 Name Type Description getelement() Document Asset Object Object assigned to the href getfullpath() string Get the path of the assigned element element Document Asset Object The property for getelement() it's a good idea to use the getter Examples // Basic usage <?= $this->href("myhref");?> // Usage with restriction <?= $this->href("myhref",array( "types"=>array("asset","object"), "subtypes"=>array( "asset" => array("video","image"), "object" => array("object") ), "classes" => array("myclass"), ));?> // Advanced usage with getelement() <?php if ($this->editmode) {?> <?= $this->href("myhref");?> <?php else {?> <?php $myhref = $this->href("myhref")->getelement(); echo $myhref->getname();?> <?php?> // Advanced usage (the video example) <?php if ($this->editmode) {?> <?= $this->href("myhref");?> <?php else {?> <?php if ($this->href("myhref")->getelement() instanceof Asset\Video) {?> <script type="text/javascript"> var params = { quality: 'high' ; var flashvars = { videofile : "<?= $this->href("myhref")->getfullpath()?>" ; swfobject.embedswf("/static/swf/player.swf", "videoplayerelement", "400", "300", "9.0.0", "", flashvars, params); </script> <?php?> <?php?> Image Configuration Name Type Description title string You can give the image widget in editmode a title width integer Width of the image in pixel

42 height integer Height of the image in pixel thumbnail string Name of the configured thumbnail which should be used hidetext boolean Hides the input for the ALT-text in editmode reload boolean Set true to reload the page in editmode after updating the image minwidth integer min. width of the image (in pixel) minheight integer min. height of the image (in pixel) attributes array custom attributes for the <img /> tag - this can be used to pass custom attributes (not w3c) removeattributes array You can remove standard attributes using this configuration, e.g. "removeattributes" => ["controls","poster"] ( since 3.1.0) uploadpath string Target path for (inline) uploaded images highresolution float factor the thumbnail dimensions should be multiplied with (html attributes width and height contain the original dimensions... used for "Retina" displays, print,...) disablewidthheightattributes bool width & height attributes are set automatically by pimcore, to avoid this set this option (eg. to true => isset check) dropclass string This option can be used to add multiple alternative drop-targets and context menus on custom HTML elements in your code. Just add the class specified here also to custom HTML elements and they will get a drop target too. You can also pass every valid attribute an img-tag can have ( w3.org - Image), such as: class style... Methods Name Arguments Return Value Description getthumbnail($name) (string/array) $name Pimcore\Model\Asset\Image\Th umbnail gettext() / getalt() - string, alt/title text from the image getsrc() - string, absolute path to the image get a specific thumbnail of the image The entered alternative text in the widget The path to the original image which is referenced getimage() - Asset_Image The asset object which is referenced (Asset_Image) (since 1.4.6) gethotspots() - array returns the hotspot data (see example below) getmarker() - array returns the marker data (see example below) Examples // Basic usage <?= $this->image("myimage");?> // Advanced usage <?= $this->image("myimage", array(

43 "title" => "Drag your image here", "width" => 200, "height" => 200, "thumbnail" => "contentimages" ));?> // An example with a direct thumbnail configuration <?= $this->image("myimage", array( "title" => "Drag your image here", "width" => 200, "height" => 200, "thumbnail" => array( "width" => 200, "height" => 200, "interlace" => true, "quality" => 90 ) ));?> // example with custom attributes <?= $this->image("image", array( "thumbnail" => "contentfullimage", "attributes" => array( "custom-attr" => "value", "data-role" => "image" ) ))?> // get retina image <?= $this->image("myimage", array( "thumbnail" => array( "width" => 200, "height" => 200 ), "highresolution" => 2 ));?> // will output<img src="/website/var/thumb_9999 [email protected]" width="200" height="200" /> <!-- but the real image size is 400x400 pixel --> // custom image tag (thumbnail objects) <?php if($this->editmode) {?> <?= $this->image("myimage", array("thumbnail" => "mythumbnail"));?> <?php else {?> <?php $thumbnail = $this->image("myimage")->getthumbnail("mythumbnail");?> <img src="<?= $thumbnail;?>" width="<?= $thumbnail->getwidth();?>" height="<?= $thumbnail->getheight();?>" data-custom="xxxx" /> <?php?> // disable automatic width and height attributes <?= $this->image("myimage", [ "thumbnail" => "examplescalewidth", "disablewidthheightattributes" => true ])?>

44 // custom drop targets <div class="mycustomimagedroptarget anotherclass">my first alternative drop target</div> <?= $this->image("image", array( "thumbnail" => "contentfullimage", "dropclass" => "mycustomimagedroptarget"

45 ))?> <div class="mycustomimagedroptarget someclass">my second alternative drop target</div> Fieldspecific Image Cropping for Documents With right click on the image in document edit mode, it is possible to define field specific image cropping. So there is no need any more to define specific images or thumbnails if a specific region of an image should be shown. Just assign the original image and define field specific cropping directly within the document.

46 Marker & Hotspots This functionality is available on every image editable (no configuration necessary). Setting a marker or a hotspot on an image has no direct effect on the output, the assigned image is displayed as usual. You as a developer have to get the data out of the image editable to build amazing frontends with it. You can get the data with the methods getmarker() and gethotspots(). All dimensions are in percent and therefore independent from the image size, you have to change them back to pixels according to your image size. Example <div> <p> </p> </div> <?= $this->image("image", array( "thumbnail" => "contentfullimage" ))?> <?php if(!$this->editmode) {?> <?php // outside the editmode: do something with the data if($this->image("image")->gethotspots()) { p_r($this->image("image")->gethotspots()); if($this->image("image")->getmarker()) { p_r($this->image("image")->getmarker());?> <?php?>... and here is the output:

47 Input Configuration Name Type Description width integer Length of the input in editmode (in pixels) htmlspecialchars boolean Set to false to get the raw value without HTML special chars like & (default to true) nowrap boolean set to false to disable the automatic line break class string a css class that is added to the element only in editmode placeholder string a placeholder that is displayed when the field is empty ( since 3.0.4) Accessible properties Name Type Description text string Value of the input, this is useful to get the value even in editmode Example

48 // Basic usage <?= $this->input("headline");?> //Advanced usage <?= $this->input("headline", array("width" => 540));?> Validation It's possible to validate the input using one of the inbuilt Ext.form.VTypes ('alpha', 'alphanum', ' ' or 'url') or by making your own //basic in-built validation <?= $this->input("headline", array("vtype"=>"alphanum"));?> //advanced usage <?php if($this->editmode):?> <script type="text/javascript"> $(function(){ ); </script> <?php endif?> // must return true or false Ext.apply(Ext.form.VTypes, { starts_with_number: function(val, field){ if(val.match(/\d.*/)){ return true; else{ return false;, starts_with_numbertext: "This field should start with a number" ); <?= $this->input("headline", array("vtype"=>"starts_with_number", "vtypetext"=>"this field is invalid"));?> From version it is possible to pass a callback into the input options using Zend_Json_Expr <?php // function must return true or an error message echo $this->input("headline", array("width" => 540, "validator" => new Zend_Json_Expr(' function(value){ if(value.match(/\d.*/)) { return true; else { return "This field should start with a number"; ') ));?> Link

49 Configuration You can pass every valid attribute an a-tag can have ( w3.org - Link), such as: class target id style accesskey name title class... Name Type Description reload boolean Set to true to reload the page in editmode after changing the state Methods Name Return-Type Description gethref() string get the path of this link gettext() string get the text of the link gettarget() string get the target of the link getparameters() string get the query params of the link getanchor() string get the anchor text of the link gettitle() string get the title of the link getrel() string get the rel text of the link gettabindex() string get the tabindex of the link getaccesskey() string get the access key of the link isempty() string empty or not Simple Example <?= $this->link("mylink");?> Advanced Example <?php while ($this->block("linkblock")->loop()) {?> <?= $this->link("mylink", array("class" => "myclass"));?> <?php?> This editable is useful for structured links, which shouldn't be inside a WYSIWYG. It can be used with assets and documents or even as an external link starting with Example linklist:

50 Multihref Multihref provides to create a references to other elements in pimcore (document, asset, object). Configuration Name Type Description width integer Width for the widget in pixels (optional) height integer Height for the widget in pixels (optional) title string Title for the input-widget uploadpath string Target path for (inline) uploaded assets Accessible properties Name Type Description elements array Array of the assigned elements Available methods Name return Description getelements() array Array of the assigned elements current() int Get the current index while looping Example <?php if ($this->editmode) {?> <?= $this->multihref("multihref");?> <?php else {?> <!-- you can iterate through the elements using directly the tag --> <?php foreach($this->multihref("multihref") as $element) {?> <?= Element_Service::getElementType($element);?>: <?= $element->getfullpath();?> <br /> <?php?> <?php?> Multiselect Configuration

51 Name Type Description store array Key/Value pairs for the available options. width integer height integer Additionally you can use every configuration property of Ext.ux.form.MultiSelect to customize the select widgetin editmode. Example <?= $this->multiselect("multiselect", array( "width" => 200, "height" => 100, "store" => array( array("value1", "Text 1"), array("value2", "Text 2"), array("value3", "Text 3"), array("value4", "Text 4"), ) ))?> Numeric The numeric editable is like a normal textfield but with special configurations for numbers. Configuration Name Type Description width integer Width of the field in pixel minvalue float Define a minimum value maxvalue float Define a maximum value You can use every configuration property of Ext.ux.form.SpinnerField to customize the numeric widget in editmode. Accessible Properties Name Type Description number float Value of the numeric field, this is useful to get the value even in editmode Example // Basic usage <?= $this->numeric("mynumber");?> // Advanced usage <?= $this->numeric("mynumber", array( "width" => 300, "minvalue" => 0, "maxvalue" => 100, "decimalprecision" => 0 ));?> Renderlet The renderlet is a special container which is able to receive every object in Pimcore (Documents, Assets, Objects). You can decide in your controller/action what to do with the object which is linked to the renderlet. So it's possible to make a multifunctional dropbox in editmode where the editor can drop anything on it.

52 Configuration Name Type Description Mandatory width integer Width of the renderlet in pixel height integer Height of the renderlet in pixel module string Specify module (default: website) controller string Specify controller X action string Specify action X template string Specify template title string Add a title to the box in editmode reload bool Reload document on change Optionally you can pass every parameter (with a simple data type) you like to the renderlet which can be accessed by the configured controller with $this->getparam("yourkey"). In the configured Controller Action In the target controller action you get the follwing parameters which can be accessed by $this->getparam("key"). Name Type Description document Pimcore\Model\Document If the element which is dropped on the renderlet is a document this parameter is defined. object Pimcore\Model\Object If the element which is dropped on the renderlet is an object this parameter is defined. id integer The id of the element assigned to the renderlet type string The type of the element assigned to the renderlet (document asset object) subtype string The subtype of the element assigned to the renderlet (folder, image, link, page, classname,...) If you have defined custom parameters to the renderlet configuration you can access them also with $this->getparam() Basic Example <?= $this->renderlet("gallery", array( "controller" => "content", "action" => "gallery", "title" => "Drag an asset folder here to get a gallery", "height" => 400 ));?> Full Example

53 // code in the template / view // this goes in the block view <?= $this->renderlet("gallery", array( "controller" => "content", "action" => "gallery", "title" => "Drag an asset folder here to get a gallery", "height" => 400 ));?> // and this goes in the content view in the scripts/content- folder <?php foreach ($this->assets as $asset) { echo '<img src="'.$asset.'"/>';?> // code in the controller/action <?php use Website\Controller\Action; class ContentController extends Action { public function galleryaction () {?> if($this->getparam("type") == "asset") { if($asset = Asset::getById($this->getParam("id"))) { if($asset->gettype("folder")) { $childs = $asset->getchilds(); $this->view->assets = $childs; Editmode awareness Please be aware, that the renderlet itself is not editmode-aware. If you need to determine within the renderlet whether in editmode or not, you need to pass that parameter to the renderlet. Eg: $this->renderlet("myrenderlet", array(... 'editmode' => $this->editmode )); Within the renderlet, you can access the editmode parameter as follows: $this->getparam("editmode") Select Configuration

54 Name Type Description store array Key/Value pairs for the available options. reload bool Set true to reload the page in editmode after selecting an item Additionally you can use every configuration property of Ext.form.ComboBox to customize the select widget in editmode. Methods Name Type Description getdata string Value of the select, this is useful to get the value even in editmode Example // To preselect "option2" in editmode <?php if($this->editmode){ if($this->select("myselect")->isempty()){ $this->select("myselect")->setdatafromresource("option2");?> // Basic usage <?= $this->select("myselect",array( "store" => array( array("option1", "Option One"), array("option2", "Option Two"), array("option3", "Option Three") ) ));?> // Advanced usage <?= $this->select("blocktype",array( "store" => array( array("wysiwyg", "WYSIWYG"), array("contentimages", "WYSIWYG with images"), array("video", "Video") ), "reload" => true ));?> Snippet (embed) Use the snippet editable to embed a document snippet, for example teasers, boxes, footers, etc. Snippets are like little documents which can be embedded in other documents. You have to create them the same way as other documents. It is possible for users to drag snippets onto a document (like a sidebar item that is different on every page) or for the developer to place one on a fixed position in a (layout) template (like footer that is the same on every page, see code example). Creation To create your own snippet start with creating a PHP file in the directory website/views/scripts/snippets. This file can contain HTML and PHP code. You can add text input fields here. This file is the view that is being used for this snippet. The file website/controllers/snippetscontroller.php is the controller and contains the actions associated with all snippets. If you have named your view "footer.php" you should add a method "footeraction()" here. This method will be called every time the snippet is displayed. You can retrieve information from the database here and pass it on to the view here using $this->view and adding variables to it. After creating the view and the action the snippet will not yet appear for the user. Before a snippet can be used you need to define it as a custom Document Type. Configuration

55 Name Type Description width integer Width of the snippet in pixel height integer Height of the snippet in pixel title string You can give the element a title defaultheight integer A default height if the element is empty reload bool Reload document on change Accessible Properties Name Type Description id integer ID of the referenced Pimcore\Model\Document\Snippet snippet Pimcore\Model\Document\Snippet referenced document snippet object Example // Define a place for a snippet to be dragged onto, basic usage <?= $this->snippet("mysnippet")?> // Define a place for a snippet to be dragged onto, advanced usage <?= $this->snippet("mysnippet", array("width" => 250, "height" => 100))?> Display text from snippet on every page If you have a footer text that you want to appear on every page, like for example a copyright notice that the user should be able to edit, create a snippet as follows: <?= $this->wysiwyg('footer', array('width' => 500, 'height' => 100));?> Place the file in the snippets directory and name the file footer.php then add it a s a document type and create a snippet called "footer" as a document. It is important that this snippet is never moved, renamed or deleted. To prevent this you can set the Document permissions so that certain users can't perform these actions. To remove the snippet from the website it can be unpublished by the user. Then add the following code to your layout template (located in the "layouts" directory): <?php $s = Document\Snippet::getByPath('/snippets/footer'); if(is_object($s) && is_object($s->elements['footer'])) { echo $s->elements['footer']->frontend();?> This code loads the snippet from a specific location. It then checks if it indeed exists and continues to extract the text from the WYSIWYG-fiel d named "footer" and echoes it on the page. Security note: because we are using a WYSIWYG-field, HTML can be inserted by the user. If you don't want this; use another kind of input field. Don't forget to escape its contents before displaying them! Table Configuration Name Type Description width integer Width of the field in pixel

56 defaults array Array can have the following properties: rows, cols, data (see example) Accessible Methods & Properties Name Type Description getdata() array Get the data of the table as array Example <?= $this->table("tablename",array( "width" => 700, "height" => 400, "defaults" => array( "cols" => 6, "rows" => 10, "data" => array( array("value 1", "Value 2", "Value 3"), array("this", "is", "test") ) ) ))?> Textarea Configuration Name Type Description width integer Width of the textarea in pixel height integer Height of the textarea in pixel nl2br boolean Set to true to get also breaks in frontend htmlspecialchars boolean Set to false to get the raw value without HTML special chars like & (default: true) class string a css class that is added to the element only in editmode placeholder string a placeholder that is displayed when the field is empty (since 3.0.4) Accessible Properties Name Type Description text string Value of the textarea, this is useful to get the value even in editmode Example // Basic usage <?= $this->textarea("mytextarea")?> // Advanced usage <?= $this->textarea("mytextarea", array("width" => 300, "height" => 200))?> Video Configuration

57 Name Type Description width integer Width of the video in pixel height integer Height of the video in pixel attributes array Additional attributes for the generated <video> tag - only for type "asset" removeattributes array You can remove standard attributes using this configuration, e.g. "removeattributes" => ["controls","poster"] ( since 3.1.0) youtube array Parameters for youtube integration. Possible parameters: ogle.com/youtube/player_parameters - only for type "youtube" thumbnail string Name of the video-thumbnail (required when using automatic-transcoding of videos) see: Video Thumbnails imagethumbnail string Name of the image-thumbnail, this thumbnail config is used to generate the preview image (poster image), if not specified pimcore tries to get the information out of the video thumbnail. see also: Video Thumbnails disableprogressreload bool Parameter to disable the automatically page-reload if a thumbnail was not jet created animatedgifpreview bool set to false to disable the animated GIF previews (static images are used instead - PNG/JPEG). editmodeimagepreview bool set to true to display only an image and not the video player in editmode, this can be necessary if you have many videos on one page (performance) When using the HTML5 player don't forget to set the right mime-type. To do this, put the following lines into your.htaccess: AddType video/mp4.mp4 AddType video/webm.webm Methods Name Arguments Return Value Description getimagethumbnail($name) (string/array) $name string, absolute path to the thumbnail getvideotype() - string, type of the video (asset youtube vimeo url) get a specific image thumbnail of the video, or a thumbnail of the poster image (if assigned) this is to check which video type is assigned getvideoasset() - asset returns the video asset object if assigned, otherwise null

58 getthumbnail() (string/array) $name array, absolute paths to the different video thumbnails get a specific video-thumbnail of the video Return Value: [status] => finished [formats] => Array ( [mp4] => /website/var /tmp/video_3 414 example.mp4 [webm] => /website/var /tmp/video_3 414 example.webm ) getposterasset() Pimcore\Model\Asset returns the assigned poster image asset Accessible Properties Name Type Description id string Asset-ID, YouTube-URL, Vimeo-URL, External-URL,... type string One of asset, youtube, vimeo, url,... Example (default configuration) <?php // with direct configuration as array?> <?= $this->video("myvideo", array( "width" => 670, "height" => 400 ));?> Automatic Video transcoding (using default Flowplayer with idevice Fallback) <?php // using thumbnails with automatic video transcoding (requires FFMPEG)?> <?= $this->video("myvideo", array( "width" => 670, "height" => 400, "thumbnail" => "example" ));?> HTML 5 Example with automatic Video transcoding (using mediaelement.js)

59 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" " <html> <head> <?php if (!$this->editmode) {?> <script type="text/javascript" src="jquery.js"></script> <script type="text/javascript" src="mediaelement-and-player.min.js"></script> <link rel="stylesheet" href="mediaelementplayer.css" type="text/css" media="screen" /> <?php?> </head> <body> <?= $this->video("myvideo", array( "thumbnail" => "example", "width" => 400, "height" => 300, "attributes" => ["class" => "my-class another-class", "data-custom-attr" => "my-test"] ));?> </body> </html> <?php if (!$this->editmode) {?> <script type="text/javascript"> $('video,audio').mediaelementplayer(/* Options */); </script> <?php?> YouTube Example <div> <?= $this->video("myvideo", array( "thumbnail" => "example", "youtube" => array( "autoplay" => 1, "modestbranding" => 1 ) ));?> </div> Vimeo Example <div> <?= $this->video("myvideo", array( "thumbnail" => "example", "vimeo" => array( "autoplay" => 1, "loop" => 1 ) ));?> </div>

60 WYSIWYG Configuration Name Type Description width integer Width of the field in pixels height integer min-height of the field in pixels toolbargroups string A toolbar config array (see below) entermode integer Set it to 2 if you don't want to add the P-tag customconfig string Path to Javascript file with configuration for CKEditor Accessible Properties Name Type Description text string Value of the WYSIWYG, this is useful to get the value even in editmode See the 2nd example of the following code block. Basic Example // Basic usage <?= $this->wysiwyg("mywysiwyg")?> // Advanced useage <?= $this->wysiwyg("content", array( "width" => 700, "height" => 500));?> Custom Configuration of CKeditor // It's possible to full customize the whole editor. All arguments which are defined in the configuration are passed to the CKeditor config. For example: <?= $this->wysiwyg("content", array( "width" => 700, "height" => 500, "customconfig" => "/custom/ckeditor_config.js"));?> // with the follwing code you can get the text even in editmode // output the text in editmode <?php if ($this->editmode) {?> <?= $this->wysiwyg("content")->text?> <?php?> Example Custom Config for CKeditor

61 <?= $this->wysiwyg("content", [ "toolbargroups" => [ [ "name" => 'basicstyles', "groups" => [ 'basicstyles', 'list', "links"] ] ] ]);?> More examples and config options for the toolbar and toolbargroups can be found at s/plugins/toolbar/toolbar.html Please refer to the CKeditor 4.0 Documentation PDF (Document) IMPORTANT This editable requires ghostscript. See System Requirements for more details. This editable allows you to embed asset documents (pdf, doc, xls,...) into documents (like video, image,...) Configuration Name Type Description width integer Width of the viewer (default 100%) height integer Height of the viewerin pixel fullscreen bool allow fullscreen or not hotspotcallback closure possibility to add custom attributes on hotspot <div> tags,... see example below Methods Name Arguments Return Value Description gethotspots() - Array containing the hotspots defined in editmode Accessible Properties Name Type Description id string Asset-ID, YouTube-URL, Vimeo-URL, External-URL,... hotspots array Array containing the hotspots defined in editmode Example <?= $this->pdf("pdf", array());?> Advanced Example

62 <section class="area-pdf"> <?= $this->pdf("pdf", [ "hotspotcallback" => function ($data) { $data["attributes"] = []; if($data && isset($data["data"]) && is_array($data["data"])) { foreach($data["data"] as $metadata) { if($metadata["name"] == "myobjectrelation" && $metadata["value"] instanceof \Pimcore\Model\Object\News) { $data["attributes"]["data-news-object"] = $metadata["value"]->getid(); ]);?> return $data; <?php $jsvar = "pimcore_pdf_". $this->pdf("pdf")->getname();?> <script> /* // possible methods <?= $jsvar?>.topage(3); <?= $jsvar?>.nextpage(); <?= $jsvar?>.prevpage(); */ </script> </section> Helpers (Available View Methods) $this->inc(mixed $document, [array $params])

63 Use $this->inc() to include documents inside of views, for example a snippet. This is useful for footers, headers, navigations, sidebars,... Arguments $document can be either an ID, a path or even the Document object itself $params is optional and should be an array with key value pairs like in $this->action() from ZF. $enablecache is true by default, set it to false to disable the cache. Hashing is done across source and parameters to ensure a consisten result. use Pimcore\Model\Document; <!-- include path --> <?= $this->inc("/shared/boxes/buttons")?> <!-- include ID --> <?= $this->inc(256)?> <!-- include object --> <?php $doc = Document::getById(477); echo $this->inc($doc, array( "param1" => "value1" )); <!-- without cache --> <?= $this->inc(123, null, false)?>?> $this->template(string $path, [array $params], [bool $resetpassedparams], [bool $capture]) This method is designed to include an other template directly, without calling an action. <?php $this->template("includes/footer.php")?> <!-- with parameters --> <?php $this->template("includes/somthingelse.php", array( "param1" => "value1" ))?> Parameters in the included template are then accessible through $this->paramname i.e. from the example above <?= $this->param1?> $this->getparam(string $key) Get's a parameter (get, post,... ), it's an equivalent to $this->getparam() in the controller action. <?php if ($this->param1) {?> Some Output <?php?> $this->cache(string $key, [int $lifetime], [bool $force]) This is an implementation of a direct in-template cache. You can use this to cache some parts directly in the template, independent of the other global definable caching functionalities. This can be useful for templates which need a lot of calculation or require a huge amount of objects.

64 Lifetime is expected in seconds. If you define no lifetime the behavior is like the outputcache, so if you make any change in pimcore, the cache will be flushed. When specifying a lifetime this is independent from changes in the CMS. <?php if(!$this->cache("test_cache_key", 60)->start()) {?> <h1><?= $this->input("headline", array("width" => 670));?></h1> <?= microtime()?> <?php $this->cache("test_cache_key")->end();?> <?php?> $this->device(string $default) (since 3.0.6) This helper makes it easy to implement "Adaptive Design" in pimcore. <?php $device = $this->device("phone"); // first argument is the default setting?> <?php if($device->isphone()) {?> This is my phone content <?php else if($device->istablet()) {?> This text is shown on a tablet <?php else if($device->isdesktop()) {?> This is for default desktop Browser <?php?> <?php if($this->device()->isdesktop()) {?> Here is some desktop specific content <?php?> For details please see: Adaptive Design / Device Helper / Tool $this->glossary() Please see the glossary documentation. $this->t("translation_key") Shorthand for $this->translate() view helper see also: Website Translations / Shared Translations $this->ts("translation_key") Shorthand for $this->translateadmin() view helper see also: Translation of Document Editables Document-Types Pimcore allows you to define custom document-types, which allows you do define sets of settings for a page. An editor can then easily choose one of the type when creating a new page. To define document-type go to "Settings->Document-Types", then you get something like that: The type can be either a page or a snippet. After you have defined a type you can access it in the context menu or in the document settings:

65 Thumbnails pimcore offers an advanced thumbnail-service also called "image-pipeline". It allows you to transform images in unlimited steps to the expected result. You can configure them in "Settings -> Thumbnails". With this service every image which is stored in "Assets" can be transformed. pimcore doesn't support to modify images which are not stored as an asset inside pimcore. IMPORTANT: Use imagick PECL extension for best results, GDlib is just a fallback with limited functionality (only PNG, JPG, GIF) and less quality! Using ImageMagick pimcore supports hundreds of formats including: AI, EPS, TIFF, PNG, JPG, GIF, PSD,... To use the thumbnailing service of pimcore, you have to create a transformation pipeline first. To do so, open Settings -> Thumbnails, and click on "Add Thumbnail" to create a new configuration. The fields name, description, format and quality should be clear, interesting are now the transformations. Click on + to add a new transformation, so that it look like that for example:

66 Important: The transformations are performed in the order from the top to the bottom. This is for example important in the configuration above, if the you first round the corners this would be performed on the original image, and then the image will get resized, so the rounded corners are also resized. To retrieve a thumbnail from an asses simply call $asset->getthumbnail() on the asset object, this will return you the path to the thumbnail file beginning from the document root, for example: /website/var/tmp/thumb_65contentimages.png This path can then be directly used to display the image in a <img /> tag. For example: <?php use Pimcore\Model\Asset; // get an asset $asset = Asset::getById(1234);?> <?php if ($asset) {?> <img src="<?= $asset->getthumbnail("mythumbnailname")?>" /> <?php?> Explanation of the transformations Transformation Description Configuration Result ORIGINAL IMAGE This is the image which is used in the following transformations NONE ;-) RESIZE The image is exactly resized to the given dimensions without respecting the ratio.

67 SCALE BY HEIGHT The image is scaled respecting the ratio to the given height, the width is variable depending on the original ratio of the image (portrait, landscape). SCALE BY WIDTH The image is scaled respecting the ratio to the given width, the height is variable depending on the original ratio of the image (portrait, landscape). CONTAIN The image is scaled to either the given height or the width, depending on the ratio of the original image. That means that the image is scaled to fit into a "virtual" box with the dimensions given in the configuration. CROP Cuts out a box of the image starting at the given X,Y coordinates and using the width and height. COVER The image is resized so that it completely covers the given dimensions. Then the overlapping pieces are cropped depending on the given positioning. This is useful if you need a fixed size for a thumbnail but the source images have different ratios. FRAME The transformation is the same as CONTAIN the difference is, that the image gets exactly the entered dimensions by adding transparent borders left / right or top / bottom.

68 ROTATE Rotates the image with the given angle. The background is transparent by default. BACKGROUND COLOR ROUNDED CORNERS Background color is especially useful if you have transparent PNG as source data or if you're using the FRAME or the ROTATE transformations where you get transparencies. It allows you to give transparencies a color, and gives you the possibility to use them for examples JPEG's which doesn't support transparency. Rounds the corners to the given width/height. Usage Examples <?php // Use with the image tag in documents?> <div> <p> <?= $this->image("image", array("thumbnail" => "mythumbnail"))?> </p> </div> <?php // Use directly on the asset object?> <?php $asset = Asset::getByPath("/path/to/image.jpg"); $imagethumb = $asset->getthumbnail("mythumbnail");?> <img src="<?= $imagethumb;?>" width="<?= $imagethumb->getwidth();?>" height="<?= $imagethumb->getheight();?>" /> <?php // Use without preconfigured thumbnail?> <?= $this->image("image", array("thumbnail" => array( "width" => 500, "height" => 0, "aspectratio" => true, "interlace" => true, "quality" => 95,

69 "format" => "PNG" )))?> <?php // Use from an object-field?> <?php if ($this->myobject->getmyimage() instanceof Asset\Image) {?> <img src="<?= $this->myobject->getmyimage()->getthumbnail("mythumbnail");?>" /> <?php?> // where "mythumbnail" is the name of the thumbnail configuration in settings -> thumbnails <?php // Use from an object-field with dynamic configuration?><?php if ($this->myobject->getmyimage() instanceof Asset\Image) {?> <img src="<?= $this->myobject->getmyimage()->getthumbnail(array("width" => 220, "format" => "jpeg"));?>" /> <?php?> <?php // Use directly on the asset object using dynamic configuration?> <?php $asset = Asset::getByPath("/path/to/image.jpg"); $pathtoimagethumb = (string) $asset->getthumbnail(array("width" => 500, "format" => "png"));

70 ?> Advanced Examples Since version pimcore returns an Asset_Image_Thumbnail object when calling $asset->getthumbnail("..") instead of just the string to the generated image. This gives you even more flexibility when working with thumbnails. $thumbnail = $asset->getthumbnail("mythumbnail"); // get the final dimensions of the thumbnail (especially useful when working with dynamic configurations) $width = $thumbnail->getwidth(); $height = $thumbnail->getheight(); // get the html "img" tag for the thumbnail: echo $thumbnail->gethtml(); // get the path to the thumbnail $path = $thumbnail->getpath(); // Asset_Image_Thumbnail implements tostring, so you can still print the path by echo $thumbnail; // prints something like /website/var/tmp/...png More Examples // adding custom html attributes to the generated <img> or <picture> tag, using a dynamic thumbnail <?= $asset->getthumbnail([ "width" => 180, "height" => 180, "cover" => true ])->gethtml(["class" => "thumbnail", "data-my-name" => "my value"])?> // same with a thumbnail definition <?= $asset->getthumbnail("examplescalewidth")->gethtml([ "class" => "thumbnail", "data-my-name" => "my value" ])?> // disable the automatically added width & height attributes <?= $asset->getthumbnail("examplescalewidth")->gethtml(["disablewidthheightattributes" => true])?> Using ICC Color Profiles for CMYK -> RGB pimcore supports ICC color profiles to get better results when converting CMYK images (without embedded color profile) to RGB. Due licensing issues pimcore doesn't include the color profiles (*.icc files) in the download package, but you can download them for free here: Adobe ICC Profiles or here: ICC (color.org) After downloading the profiles put them into your /website folder or anywhere else on your sever (eg. /usr/share/color/icc). Then go to the pimcore system settings, open the assets section and configure the path to your favorite color profile. Dynamic Generation on Request

71 pimcore auto-generates a thumbnail if it doesn't exist and is directly called (not using any of the getthumbnail() methods). For example: Call /thumb contentimages/placeholder.jpeg (" 6644" is the asset ID and " contentimages" is the name of the thumbnail) directly in your browser. Now pimcore checks if the asset with the ID 6644 and the thumbnail with the key "contentimages" exists, if yes the thumbnail is generated on-the-fly and delivered to the client. When requesting the images again the image is directly served by apache, because the file already exists (just the same way it works with the getthumbnail() methods). The structure of the path is identically with the one generated by the getthumbnail() methods, so it doesn't matter whether the thumbnail is generated by getthumbnail() (inside a PHP process) or on-the-fly (in a separate process). Of course this works only with predefined (named) thumbnail configurations and not with dynamic configurations. Starting with build 2648 it's possible to use this feature in combination with "High Resolution Support". (Example see below). Deferred Rendering of Thumbnails Normally pimcore generates the thumbnail as soon as you call the method getthumbnail() on an Asset\Image. But there are cases where you want to avoid this for performance or other reasons. To just get the path to the thumbnail without generating it you have to pass a second parameter, by doing so the image is generated on request and not when calling getthumbnail() in your code. $asset = Asset\Image::getById(123); $asset->getthumbnail("myconfig", true); // 2nd parameter means "deferred" High-Resolution Support This is a special functionality to allow embedding high resolution (ppi/dpi) images. In the thumbnail configuration: The above configuration will generate a thumbnail with 500px width. When using this configuration in combination with the image editable using the following code

72 <?= $this->image("myimage", array("thumbnail" => "contentimages"));?> this will create the following output: <img src="/website/var/tmp/thumb_6644 width="250" height="190" /> It's also possible to add the high-res dynamically: <?= $this->image("myimage", array("thumbnail" => array("width" => 250, "contain" => true, "highresolution" => 2))) This will create an image width = 500px Combining "dynamic generation on request" with high resolution This is especially useful in the case you want to serve thunbnails depending on the ppi of the device screen or in combination with Web2Print documents (browser preview with normal size, tripled in size for PDF ouptut) Scripts like retina.js make it really easy to use this feature in the browser, since pimcore is compatible to Apple's prescribed high-resolution modifier (@2x). Example: You have this code in your template (the thumbnail configuration "testimage" doesn't make use of the "High Resolution" setting): <?= $this->image("myimage", array("thumbnail" => "testimage"));?> this generates the followinig ouput: <img src="/website/var/tmp/thumb_6644 testimage.png" width="250" height="190" /> when using retina.js the script tries to load the image including the high-resolution modifier: /website/var/tmp/thumb_6644 [email protected] This images doesn't exist on the file system, so it is dispatched by pimcore (see "Dynamic Generation on Request" above) which generates the thumbnail (all dimensions 2x). But of course you can use this feature without retina.js or something similar. Examples: /website/var/tmp/thumb_7865 [email protected] /website/var/tmp/thumb_6644 [email protected] Using float is possible too: /website/var/tmp/thumb_ [email protected]

73 Media Queries in Thumbnail Configuration If your're using media queries in your thumbnail configuration pimcore automatically generates a <picture> tag instead of an <img> tag when calling $asset->getthumbnail("example")->gethtml(). But in some cases it is necessary to get single thumbnails for certain media queries out of the thumbnail object, which is described in the examples below. $a = Asset::getById(71); // list all available medias in "gallerycarousel" thumbnail configuration p_r(array_keys(asset_image_thumbnail_config::getbyname("gallerycarousel")->getmedia s())); // get the <picture> element for "gallerycarousel" => default behavior $a->getthumbnail("gallerycarousel")->gethtml(); // get path of thumbnail for media query 940w $a->getthumbnail("gallerycarousel")->getmedia("940w"); // get <img> tag for media query 320w 2x $a->getthumbnail("gallerycarousel")->getmedia("320w")->gethtml(); // get 2x thumbnail path for media query 320w $a->getthumbnail("gallerycarousel")->getmedia("320w", 2); How to Disable Picture Polyfill (3.0.6) If you want to use your own Polyfill. <?php Asset\Image\Thumbnail::setEmbedPicturePolyfill(false);?> Glossary The glossary module is a powerful tool making internal linking easy and smart: in a special editor you can define your terms which are replaced automatically with a link to the defined page. But the glossary is not only useful for internal linking, it's also perfect for explaining abbreviations and/or acronyms. How it works Open the glossary editor and define some terms: Then you have to define one or more regions in your views, telling the glossary where you want it to replace your terms:

74 <?php $this->glossary()->start();?> <div> <?= $this->wysiwyg("content", array( "height" => 200 ));?> </div> <?php $this->glossary()->stop()?> Now the output of the WYSIWYG field will look like this: And the HTML-markup will look like that: <p> <abbr title="hypertext Preprocessor">PHP</abbr> is a widely used, general-purpose scripting language that was originally designed for web development to produce dynamic web pages. For this purpose, <abbr title="hypertext Preprocessor">PHP</abbr> code is embedded into the HTML source document and interpreted by a web server with a <abbr title="hypertext Preprocessor">PHP</abbr> processor module, which generates the web page document. As a general-purpose programming language, <abbr title="hypertext Preprocessor">PHP</abbr> code is processed by an interpreter application in command-line mode performing desired operating system operations and producing program output on its standard output channel. It may also function as a graphical application. <abbr title="hypertext Preprocessor">PHP</abbr> is available as a processor for most modern web servers and as standalone interpreter on most operating systems and computing platforms. You can <a href=" it free at php.net. </p> Note Since the glossary depends on languages you'll have to register a language first. Read more about this topic here. Redirects Define redirects for marketing URL's or for moved documents. You can use regular expressions to define the sources, the placeholders in the regex can be accessed in the target URL with the formatting functions of sprintf. Notice If you want to use a % in your target which shouldn't represent a placeholder (eg. %20 for space) then you have to escape the % with an \ (=backslash)

75 Example Using PCRE Backreference-Syntax It is possible to use simple PCRE backreferences (placeholders) in the target url. Example: Notice Only simple $1-n references are possible, no special sytax like $ Unknown macro: {1 1,... Document Lists Documents can be retrieved in the form of a document list just in the same manner as object lists. For example, if all documents modified in the last hour should be retrieved this code would do it: use Pimcore\Model\Document; $list = new Document\Listing(); $list->setcondition("modificationdate > ". time() - ( 60 * 60) ); $documents= $list->load(); Include unpublished Documents in the List Normally document lists only return published documents if you are not in pimcore admin mode. To get unpublished documents in the frontend, the "unpublished" property of the list must be set to true before loading it: $list = new Document\Listing(); $list->setcondition("modificationdate > ". time() - ( 60 * 60) ); $list->setunpublished(true); $documents = $list->load(); Using prepared statement placeholders and variables The syntax is similar to that from the Zend Framework described here: adapter.select.fetchall

76 // using a single variable $list = new Document\Listing(); $list->setcondition("modificationdate >?", (time() - ( 60 * 60)) ); $documents= $list->load(); // using more variables / placeholders $list = new Document\Listing(); $list->setcondition("modificationdate >? AND type =?", array(time(), "page") ); $documents= $list->load(); Localize your Documents (i18n) Localize your documents pimcore allows you to localize every document. You can find the setting in your document in the tab "Properties".There you can choose a language which is configured in the system settings. The selected language is registered as a property on the document, which is inherited to all of it's childs. Read more about properties here. If you have selected a language this will be automatically registered globally the ZF way (\Zend_Registry::set("Zend_Locale", new \Zend_Locale($this->document->getProperty("language")))). Because of this, every pimcore and ZF module automatically recognized the locale, for example \Zend_Date, \ Pimcore\Translate ( based on \Zend_Translate) as described later in this text. It's no longer required to set the locale manually for example in your \Website\Controller\Action::init(); Since the language is a simple property you can access it like every other property like:

77 // in your controller / action $language = $this->document->getproperty("language"); // in your view $language = $this->document->getproperty("language"); // any document $doc = \Pimcore\Model\Document::getById(234); $language = $doc->getproperty("language"); // accessing anywhere in your code using the registry (the common ZF way) $language = \Zend_Registry::get("Zend_Locale"); Once you have defined the language of your documents you can also use the translate helper in your views, as described here. Translating terms on the website Pimcore comes with a translation module for the website which ist explained in Translations Website. This is what needs to be done to use translations in templates and display them accordingly on the website (frontend). Translating the Backend Sometimes special translations are needed in the pimcore backend. For example, if pimcore is used in different languages the select editable element in documents needs to be translated to different languages for editors in pimcore edit mode. The view helper for these translations is explained in Translations Admin. Translation of Document Editables There is a View Helper availabe which allows you to use translations in your document editables. Example: Translation of options of a select editable (view script)... <?= $this->select("select",array( "store" => array( array("option1", $this->translateadmin("option One")), array("option2", $this->translateadmin("option Two")), array("option3", $this->translateadmin("option Three")) ) ));?> After adding a new translation, the document needs to be loaded once in edimode. This adds the new translation keys to Extras > Admin Translations where all extra translations can be edited.translateadmin Shorthands <?= $this->select("select",array( "store" => array( array("option1", $this->ts("option One")), array("option2", $this->ts("option Two")), array("option3", $this->ts("option Three")) ) ));?> Website Translations / Shared Translations

78 Pimcore provides a simple translation-tool based on \Zend_Translate. The only thing you have to do is to register a locale globally (the ZF way), the easiest way is to do that in the \Website\Controller\Action. In the example \Website\Controller\Action which is shipped with Pimcore there is already an example. Once you have registered the locale you can simply use the translate helper of \Zend_View in your templates with: <?= $this->translate("translation_key")?> or also by using a shorthand <?= $this->t("translation_key");?> Then call the page where you use the translation, once you have done this, pimcore registers the key in the translation administration, and you can edit the text in the grid. The columns in the translation administration for the languages is generated automatically by the used locales. Please make sure that all desired languages are set as valid frontend languages in the pimcore system settings as described here. Next the translations for all registered languages can be edited in Extras > Translations -> Shared Translations Example in Website\Controller\Action <?php namespace Website\Controller; use Pimcore\Controller\Action\Frontend; class Action extends Frontend { public function init () {?> parent::init(); $locale = new \Zend_Locale('en_US'); \Zend_Registry::set('Zend_Locale', $locale); Example in Templates / Views

79 <div> <address> <?= $this->translate("copyright")?></address> <a href="/imprint"><?= $this->translate("imprint")?></a> <a href="/legal"><?= $this->translate("legal_notice")?></a> </div> Shorthands <div> <address> <?= $this->t("copyright")?></address> <a href="/imprint"><?= $this->t("imprint")?></a> <a href="/legal"><?= $this->t("legal_notice")?></a> </div> Navigation Basics Pimcore comes with a standard navigation implementation in the form of a view helper, which utilizes Zend_Navigation. The Pimcore\View\Helper\PimcoreNavigation gets registered by default with the other pimcore view helpers. It builds a Zend_Navigation container based on the existing document structure and needs to be set up as follows in your view script or layout script: Only documents are included in this structure, directories are ignored, regardless of their navigation properties. <?php // get root node if there is no document defined (for pages which are routed directly through static route) if(!$this->document instanceof Document\Page) { $this->document = Document::getById(1); // get the document which should be used to start in navigation default home $navstartnode = $this->document->getproperty("navigationroot"); if(!$navstartnode instanceof Document\Page) { $navstartnode = Document::getById(1);?> $mainnavigation = $this->pimcorenavigation($this->document, $navstartnode); Having set up the navigation view helper as shown above, you can easily use the Zend Navigation Helpers to render a navigation tree, or breadcrumbs:

80 <!-- META NAVIGATION - ONLY 1st LEVEL --> <?php echo $mainnavigation->menu()->rendermenu($navigation, array("maxdepth" => 0));?>... <!-- BREADCRUMBS --> <div> <a href="/">home</a> > <?= $mainnavigation->breadcrumbs()->setmindepth(null);?> </div>... <!-- SIDEBAR NAVIGATION --> <div id="sidebar"> <div id="navigation"> <?= $mainnavigation->menu()->rendermenu($navigation);?> </div> </div> <!-- SIDEBAR NAVIGATION WITH A DIFFERENT HTML PREFIX --!> <div id="sidebar"> <div id="navigation"> <?= $this->pimcorenavigation($this->document, $this->navstartnode, $htmlidprefix)->menu()->rendermenu(null, [ "ulclass" => "nav bs-sidenav", "expandsiblingnodesofactivebranch" => true ]);?> </div> </div> But of course you can do this also in a single line Main Navigation <?= $this->pimcorenavigation($this->document, $mainnavstartnode)->menu()->rendermenu(null, [ "maxdepth" => 1, "ulclass" => "nav navbar-nav" ]);?> Sidebar Navigation <?= $this->pimcorenavigation($this->document, $startnode)->menu()->rendermenu(null, [ "ulclass" => "nav bs-sidenav", "expandsiblingnodesofactivebranch" => true ]);?> The rendermenu() method renders the menu to the deepest available level. Levels trees which are not within the active tree, and levels below the latest active page must be hidden using css. The example css below shows how to do that (includes 3 Levels)

81 #navigation ul li ul { display:none; #navigation ul li.active ul { display:block; #navigation ul li.active ul li ul { display:none; #navigation ul li.active ul li.active ul { display:block; #navigation ul li.active ul li.active ul li ul { display:none; #navigation ul li.active ul li.active ul li.active ul{ display:block; Setting a Document's Navigation Properties in pimcore Pages and links have "Navigation Settings" in their system properties as shown in the screen above. These navigation settings include the following properties: Name: Document's name used in the navigation (Label) Title: Document's title used in the navigation - the HTML Attribute title Target: Link Target (_blank, _self, _top, _parent)

82 Exclude from Navigation: Property to quickly exclude a Page from Navigation getproperty("navigation_exclude") Class: HTML class of the navigation element Anchor: Anchor appended to the document's URL Parameters: Parameters appended to the document's URL Relation: Only available in custom navigation script. Supposedly the HTML rel attribute to open the link in a sort of Lightbox / Clearbox Accesskey: Only available in custom navigation script Tab-Index: Only available in custom navigation script Individual (Partial) Navigation View Script If the standard HTML output of the render() method is not suitable for a project, there is the possibility to provide a custom script for the menu HTML. This can be achieved using the renderpartial() method of the Zend Menu Helper. For example inside your view: <?php $this->pimcorenavigation($this->document, $mainnavstartnode)->menu()->setpartial('includes/navigation.phtml')->render();?> /website/views/scripts/includes/navigation.phtml <ul class="navigation"> <?php foreach ($this->container as $page) { echo $this->navigation()->menu()->htmlify($page), PHP_EOL?> </ul> Additional Navigation Properties of a Document_Link (Tabindex, Accesskey, Relation) A Document_Link has 3 properties which are not covered by Zend_Navigation by default. These are tabindex, accesskey and relation. Since the Zend_Navigation container contains instances of Pimcore\Navigation\Page\Uri, which extend Zend_Navigation_Page_Uri, these additional properties are available and accessible through their according getters. Consequently, they can be regarded in an individual (partial) view script for the navigation, but will be ignored by the default render() methods. Using the Navigation Helper with (Sub-) Sites For example:

83 $navstartnode = $this->document->getproperty("navigationroot"); if(!$navstartnode instanceof Document\Page) { if(site::issiterequest()) { $site = Site::getCurrentSite(); $navstartnode = $site->getrootdocument(); else { $navstartnode = Document::getById(1); <?= $this->pimcorenavigation($this->document, $navstartnode)->menu()->rendermenu(null, [ "maxdepth" => 1, "ulclass" => "nav navbar-nav" ]);?> Using partials generate customized Navigation For example, generate bootstrap 3.0 style navigation:

84 <?php $navstartnode = $this->document->getproperty("navigationroot"); if(!$navstartnode instanceof Document\Page) { if(site::issiterequest()) { $site = Site::getCurrentSite(); $navstartnode = $site->getrootdocument(); else { $navstartnode = Document::getById(1);?> <nav class="navbar-inverse navbar-static-top" role="navigation"> <div class="container-fluid"> <div class="navbar-header"> <button type="button" class="navbar-toggle" data-toggle="collapse" data-target="#bs-navbar-collapse-1"> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span> </button> </div> <div class="collapse navbar-collapse" id="bs-navbar-collapse-1"> <ul class="nav navbar-nav"> <?php $mainnavigation = $this->pimcorenavigation()->getnavigation($this->document, $navstartnode);?> <?php foreach ($mainnavigation as $page) {?> <?php $page Zend\Navigation\Page\Mvc */?> <?php // here need to manually check for ACL conditions?> <?php if (!$page->isvisible()!$this->navigation()->accept($page)) { continue;?> <?php $haschildren = $page->haspages();?> <?php if (!$haschildren) {?> <li> <a href="<?php echo $page->gethref()?>"> <?php echo $this->translate($page->getlabel())?> </a> </li> <?php else {?> <li class="dropdown"> <a href="<?= $page->gethref();?>"><?php echo $this->translate($page->getlabel())?></a> <ul class="dropdown-menu"> <?php foreach ($page->getpages() as $child) {?> <?php if(!$child->isvisible()!$this->navigation()->accept($child)) { continue;?> <li> <a href="<?php echo $child->gethref()?>"> <?php echo $this->translate($child->getlabel())?> </a> </li> <?php?> </ul> </li> <?php?> <?php?> </ul> </div> </div> </nav>

85 Adding custom items to navigation In the following example we're adding news items (objects) to the navigation using the callback. <?= $this->pimcorenavigation($this->document, $startnode, null, function ($page, $document) { if($document->getproperty("templatetype") == "news") { $list = new \Pimcore\Model\Object\News\Listing; $list->load(); foreach($list as $news) { $detaillink = $this->url([ "id" => $news->getid(), "text" => $news->gettitle(), "prefix" => $this->document->getfullpath() ], "news", true); $page->addpage([ "label" => $news->gettitle(), "id" => "object-". $news->getid(), "uri" => $detaillink ]); )->menu()->rendermenu(null, [ "ulclass" => "nav bs-sidenav", "expandsiblingnodesofactivebranch" => true ]);?> Cacheable / High-Performance Navigation (since 3.0.6) The navigation tree / container (Zend_Navigation_Container) is automatically cached by pimcore and improves significantly the performance of navigations. To benefit from the cache it's absolutely necessary to don't use Pimcore\Model\Document objects directly in the navigation templates / partial scripts, because this would result in loading all the documents again in the navigation. But sometimes it's necessary to get some properties or other data out of the documents in the navigation to build the navigation as it should be. For that we've introduced a new parameter for the pimcorenavigation view helper, which acts as a callback and allows to map custom data onto the navigation page item. <?php $mainnavigation = $this->pimcorenavigation($this->document, $mainnavstartnode, null, function ($page, $document) { $page->setcustomsetting("mycustomproperty", $document->getproperty("mycustomproperty")); $page->setcustomsetting("sublistclass", $document->getproperty("sublistclass")); $page->setcustomsetting("title", $document->gettitle()); $page->setcustomsetting("headline", $document->getelement("headline")->getdata()); );?> Later in the template of the navigation you can use the mapped data directly on the page item object:

86 <?php foreach( $this->container as $page ){?> <li class="<?php if( $page->getactive( true ) ){?>active<?php?>"> <a href="<?= $page->geturi()?>" target="<?= $page->gettarget()?>"><?= $page->getlabel()?></a> <ul class="<?= $page->getcustomsetting("sublistclass")?>" role="menu"> <?php $this->template( "/navigation/partials/main.php", [ "container" => $page->getpages() ] );?> </ul> </li> <?php?> Using this method will dramatically improve the performance of your navigation. Dynamic key for the navigation cache Sometimes it's necessary to manually set the key for the navigation cache. $this->pimcorenavigation($this->document, $mainnavstartnode, null, null, "yourindividualkey"); Disabling the navigation cache You can disable the navigation cache by setting the 5th argument to FALSE $this->pimcorenavigation($this->document, $mainnavstartnode, null, null, false); // or $this->pimcorenavigation()->getnavigation($this->document, $navstartnode, null, null, false); FAQ A document does not show up in the navigation. Why? Please make sure that the documents and its parent documents are published and that the document it self as well as all it's parents have a navigation name set. Neither the document itself nor one of it's parent documents may have activated "Exclude From Navigation" in their properties. (Document properties -> system property) Why is the navigation not appearing? See the above question. If none of the documents have a navigation title set the render function will simply return nothing. Why is the homepage not appearing in the navigation? The homepage will not appear in the navigation by default. You can add the homepage (and any other page) manually: $navigation->addpage(array( 'order' => -1, // put it in front of all the others 'uri' => '/', //path to homepage 'label' => 'home', //visible label 'title' => 'Homepage' //tooltip text ));

87 If you retrieve the "home" document (which always has the ID 1) you can also retrieve its navigation properties so that they can be edited from the Pimcore admin interface like all the other documents. $home = Document::getById(1); $navigation->addpage(array( 'order' => -1, // put it in front of all the others 'uri' => '/', //path to homepage 'label' => $home->getproperty('navigation_name'), //visible label 'title' => $home->getproperty('navigation_title'), //tooltip text 'active' => $this->document->id == $home->id //active state (boolean) )); Document Tree The following sites describe useful features within the document tree: Hardlinks for documents Copy documents and rewrite relations to new documents Copy documents and rewrite relations to new documents While copying a sub tree of documents, it might be useful to rewrite relations to documents, which are copied to, to the newly created copies. E.g. in order to create an English version of the website, copy the whole sub tree /de to /en. Imagine the document /de/service has a link to /de/info. Up to now in the copied document /en/service the link will be also point to /de/info. But with the new paste method 'updating references', this link will be automatically rewritten to /en/info. Items which will be rewritten: Tags/Editables: WYSIWYG Links, Link, Renderlet, Snippet, Href Properties: Document Hardlinks for documents Hardlinks for documents work similar to hardlinks in Linux file systems. One position within the document tree can link to another sub tree. As a result exciting navigation trees become possible without additional editing efforts due to copies of documents. Assets Assets are files that can be managed through the Pimcore CMS. The most common assets are images. In Pimcore you can organize assets in folders. Some file types, like images can be edited directly in Pimcore, and can be used to create thu mbnails. Other kinds of common assets are PDF or MS Word documents which people can download from the website.

88 Asset Lists Please see object lists and document lists, the same principle can be used with an Asset\Listing. $list = new \Pimcore\Model\Asset\Listing(); $list->setcondition("..."); $list->setorderkey("filename"); $list->setorder("desc"); $list->load(); Custom Settings (Properties) Custom Settings/Properties can be added programmatically to every asset. This is mostly used for plugins or something similar. <?php $asset = Asset::getById(2345); $settings = $asset->getcustomsettings(); $settings["mysetting"] = "this is my value this can be everythin also an array or an object not only a string"; $asset->setcustomsettings($settings); $asset->save();?> Image Thumbnails This topic is described in detail here: Thumbnails Video Thumbnails pimcore is able to convert videos to the most used formats used in the web automatically. It is also possible capture a custom preview image out of the video. To use all these functionalities it is required to install FFMPEG on the server. It is also required to configure the path to FFMPEG and to the PHP-CLI binary in the system settings. Configure Video Thumbnails As mentioned before, you have to install FFMPEG on the server. It isn't included in the pimcore installation package due licensing issues (proprietary codecs, etc. ). But there are static builds for FFMPEG available for Linux, Windows and OSX: Linux 64bit static builds (including qt-faststart): (at least Version 2.0.1) Windows builds: Install FFMPEG and qt-faststart (on Debian 64bit) Run all this commands as root

89 cd ~ wget -O ffmpeg.tar.xz tar -Jxf ffmpeg*.tar.xz rm ffmpeg*.tar.xz mv ffmpeg-* /usr/local/ffmpeg ln -s /usr/local/ffmpeg/ffmpeg /usr/local/bin/ ln -s /usr/local/ffmpeg/ffprobe /usr/local/bin/ ln -s /usr/local/ffmpeg/qt-faststart /usr/local/bin/ ln -s /usr/local/ffmpeg/qt-faststart /usr/local/bin/qtfaststart That's it! The ffmpeg binary is now in here: /usr/local/bin/ffmpeg ToDo's in System Settings Examples - Image Snapshots $asset = Asset::getById(123); if($asset instanceof Asset\Video) { // get a preview image thumbnail of the video, resized to the configuration of "mythumbnail" echo $asset->getimagethumbnail("mythumbnail"); // get a snapshot (image) out of the video at the time of 10 secs. (see second parameter) using a dynamic image thumbnail configuration echo $asset->getimagethumbnail(array("width" => 250, "frame" => true), 10); Examples - Video Transcoding

90 $asset = Asset::getById(123); if($asset instanceof Asset\Video) { $thumbnail = $asset->getthumbnail("myvideothumbnail"); // returns an array if($thumbnail["status"] == "finished") { p_r($thumbnail["formats"]); // transcoding finished, print the paths to the different formats /* OUTPUTS: Array( "mp4" => "/website/var/tmp/video...mp4", "webm" => "/website/var/tmp/video...webm" ) */ else if ($thumbnail["status"] == "inprogress") { echo "transcoding in progress, please wait..."; else { echo "transcoding failed :("; Asset Document Thumbnails (PDF, docx, odf,...) INFO This feature requires Ghostscript and LibreOffice on the server This feature allows you to create an image thumbnail of nearly any document format, like doc(x), ppt(x), pdf, xls(x), odt, ods, odp and many others. You can of course use existing image-thumbnail configurations to create a thumbnail of your choice. Examples $asset = Asset::getById(123); if($asset instanceof Asset\Document) { // get a thumbnail of the first page, resized to the configuration of "mythumbnail" echo $asset->getimagethumbnail("mythumbnail"); // get the thumbnail for the third (see second parameter) page using a dynamic configuration echo $asset->getimagethumbnail(array("width" => 230, "contain" => true), 2); // get the thumbnail URL for all pages, but do not generate them immediately (see third parameter) - the thumbnails are then generated on request $thumbnailurls = array(); for($i=1; $i<=$asset->getpagecount(); $i++) { $thumbnailurls[] = $asset->getimagethumbnail("mythumbnail", $i, true); Asset (Localized) Metadata

91 Integrated functionalities and default fields There may be default fields on an asset, that means that this fields have a special meaning and will be used somewhere else. Image asset There are 3 default fields, namely "title", "alt" and "copyright". The contents of this fields will be used as default "alt" and "title" attribute on any generated <img> tag generated by pimcore for the corresponding image. This includes for example: // image editable on documents <?= $this->image("myimage", array("thumbnail" => "xyz"));?> // thumbnail html generator <?= $asset->getthumbnail("xyz")->gethtml();?> <?= $object->getmyimage()->getthumbnail("xyz")->gethtml();?> The "copyright" field will be appended to every "title" and "alt" attribute separated by. Examples Getting data $asset = Asset::getById(123); // get the title for the current language (Zend_Locale in Zend_Registry) $asset->getmetadata("title"); // get the English title $asset->getmetadata("title", "en"); // if there's no title for "en" but one without a language this will be returned (fallback mechanism). Setting data // Set the English title $asset->addmetadata("title", "input", "the new title", "en");

92 Data Objects In the context of a CMS using documents containing data very often is not enough, because they make it hard to work with structured content or data input from external systems. In order to efficiently work with structured content in the context of product information management (PIM), content needs to be grouped into predefined units of data. In pimcore this is accomplished with data objects. Data objects are literally objects in the sense of object oriented programming. They can be defined through a user friendly graphical user inteface (GUI), but nevertheless in the background a plain php class is created, which can profit from inheritance and can be utilized and accessed programmatically. Data objects can be instantiated and filled in pimcore or they can be served from external systems like CRM, ERP, PIM or asset management systems. The following code snippet indicates how to access, create and modify an object programmatically: $myobject = Object\Myclassname::getById(167); $myobject->getname(); $myobject->getdescription(); //... or you can modifiy data directly with PHP $myobject->setname("my Name"); $myobject->save(); // it's also possible to get an object by an foreign ID $city = Object\City::getByZip(5020,1); // you can also get an object by id where you don't know the type $object = Object::getById(235); // or obtain an object by path $object = Object::getByPath("/path/to/the/object"); // Create a new object $newobject = Object\Myclassname::create(array( 'name' => 'New Name', 'description' => 'Some description' )); $newobject->setkey(\pimcore\file::getvalidfilename('new Name')); $newobject->setparentid(123); $newobject->save(); How object classes are defined and how objects can be listed and batch processed can be found in the following sections about classes, lists and external system interaction Please Note: When using your generated classes in the code, the classname always starts with a capital letter. Example: Classname: product PHP-Class: Object\ Product Object Classes To get started with data objects, classes must be defined. Defining a class consists of defining the attributes of the object and the layout for data within the object. Layoutwise object properties can be grouped into panels, which incorporate the layout areas north, east, west, south and center and additionally they can be positioned into tab panels. This allows logical structuring of object attributes into smaller units of data belonging together. It depends on the use case how data should be grouped and structured. Common applications are tabs/groups for different languages or logical groups like basic data, media, sales data, etc. To define a class, the menu Settings/Objects/Classes needs to be used the pimcore toolbar menu. The class name has to be a valid PHP class name. After creating a new class, the class attributes and layout can be built.

93 Class attributes are defined from a set of predefined data types. These data types define not only the type of data such as text, number, image, reference to another object etc. but also how data input can be achieved and how data is accessed. Each data type comes with an input widget. For instance, the image data input comes with a drop area to which a user can drag and drop an image. The entire list of data types is indicated below Name Underlying data type Input widget checkbox Pimcore\Model\Object\Class\Data\Checkbo x checkbox country Pimcore\Model\Object\Class\Data\Country combo box with predefined country list from Zend_Locale date Pimcore\Model\Object\Class\Data\Date calendar date selector date & time Pimcore\Model\Object\Class\Data\Datetime calendar date selector + combo box for time fieldcollections Pimcore\Model\Object\Class\Data\Fieldcoll ections A collection of fields geopoint Pimcore\Model\Object\Class\Data\Geopoint google maps widget to find longitude/latitude geobounds geopolygon Pimcore\Model\Object\Class\Data\Geobou nds Pimcore\Model\Object\Class\Data\Geopoly gon google maps widget to define geographical bounds google maps widget to define a geographical area href Pimcore\Model\Object\Class\Data\Href reference to a pimcore document, object or asset image Pimcore\Model\Object\Class\Data\Image drop area & preview for a pimcore asset input Pimcore\Model\Object\Class\Data\Input text input field keyvalue language Pimcore\Model\Object\Class\Data\KeyValu e Pimcore\Model\Object\Class\Data\Languag e key/value pairs combo box with predefined language list from Zend_Locale link Pimcore\Model\Object\Class\Data\Link link selector with link target multihref Pimcore\Model\Object\Class\Data\Multihref collection of references to pimcore documents, objects, assets multiselect nonownerobjects Pimcore\Model\Object\Class\Data\Multisele ct Pimcore\Model\Object\Class\Data\Nonown erobjects combo box with multiple select object relations which are owned by a different object numeric Pimcore\Model\Object\Class\Data\Numeric spinner field for number input objects Pimcore\Model\Object\Class\Data\Objects collection of pimcore object references password Pimcore\Model\Object\Class\Data\Passwor d password field select Pimcore\Model\Object\Class\Data\Select combo box slider Pimcore\Model\Object\Class\Data\Slider number input with slider widget (min - max slider) table Pimcore\Model\Object\Class\Data\Table table input textarea Pimcore\Model\Object\Class\Data\Textarea textarea wysiwyg Pimcore\Model\Object\Class\Data\Wysiwyg text area with formatting options through a WYSIWYG editor

94 user Pimcore\Model\Object\Class\Data\User combo box to select from all existing pimcore users (available since build 716) In the user settings the object dependencies of each user are shown in the second tab panel. All objects which reference the selected user are listed in a grid view. If one needs to find out which objects hold a reference to a specific user, the Pimcore\Tool\Admin::getObjectsReferencin guser($userid) method can be used to find all referencing objects. All data types are wrapped in an object derived from Pimcore\Model\ Object\Class\Data. These data type objects provide getters and setters and they define the input widget in the frontend. Data type objects are displayed in the first column of the table above. The second column indicates the underlying data type class and the third column outlines the input widget used in pimcore to fill in, edit and display data objects. Object class names, fields and layout components can be translated to different languages in the pimcore admin. Please see Translations for more information how these components are translated. Data Fields Besides the name, which is the name of the object's property and the title, which is shown in the GUI, an object field has the general configuration options listed below. The title can be translated for different system languages. Please see the article about Translations to find out how to add object field translations. mandatory: Makes the field mandatory and does not allow saving the object when it is empty not editable: Does not allow a change of this field's value in pimcore backend (data change can only be done programmatically) invisible: The field is not visible in pimcore visible in grid view: Determines if the field's data column is shown in the object grid view, or hidden (meaning it has to be activated manually) visible in search result: Determines if the field's data column is shown in the search results grid, or hidden (meaning it has to be activated manually) indexed: puts an index on this column in the database Moreover, each data field can have a tooltip, which is shown when the mouse hovers over the input field. The layout settings allow to apply custom CSS to any object field.

95 WARNING Please note that renaming a field means the loss of data from the field in all objects using this class. Date, Date & Time, Time Date, Date & Time The date and date & time object fields are represented by a calender widget in the pimcore GUI In the database it's data is saved as unix timestamp and thereby stored in an INT data column. Programmatically these data types are represented by a Zend_Date Object. Time The time data field is the same drop down list of day times as in the date & time field. It's stored as a string in a VARCHAR(5) column in the database and can be set programmatically by simply passing a string like for example "11:00" to the field's setter. Geographic Fields - Point, Bounds, Polygon There are three different geo data types available in pimcore: Geopoint, Geobounds and Geopolygon. The country select box also belongs to the context of geo widgets, but it is rather a select input widget and therefore it is listed with the other select widgets. Geopoint

96 The geopoint consists of two coordinates: latitude and longitude. In the pimcore GUI there is the same geopoint selector widget as shown above. It allows to find coordinates for a geographic point easily. In the database the values are stored in two columns which are called latitude and longitude. Programmatically the data for this field is represented by Pimcore\Model\Object\Data\Geopoint. To set a geopoint programmatically, a new Pimcore\Model\Object\Data\Geopoint has to be instantiated: $longitude = ; $latitude = ; $point = new \Pimcore\Model\Object\Data\Geopoint($longitude,$latitude); $object->setpoint($point); $object->save(); Geobounds Geobounds represent a geographic area defined by a north eastern point and a south western point. In the pimcore GUI the input widget as shown above is available. In the database there are 4 columns with coordinates to hold the data of geobounds. Programmatically both points are Pimcore\Model\Object\Data\Geopoints and they are wrapped by the Pimcore\Model\Object\Data\Geopoints Object. The following code

97 snippet shows how to set Geobounds: use Pimcore\Model\Object\Data\Geopoint; $northeast = new Geopoint( , ); $southwest = new Geopoint( , ) $object->setbounds(new Object_Data_Geobounds($northEast,$southWest)); $object->save(); Geopolygon The geopolygon is the third in the row of geo widgets. It allows to define a geographic area by setting an arbitrary amount of geo points. In the database these points are stored in a single column of the data type LONGTEXT in the form of a serialized array of Pimcore\Model\Object\Data\Geopoints. To set geopolygon data programmatically, an array of Geopoints has to be passed to the setter: use Pimcore\Model\Object\Data\Geopoint; $data = array( new Geopoint( , ), new Geopoint( , ), new Geopoint( , ) ); $object->setpolygon($data); $object->save(); Href, Multihref, Objects - Relations, Dependencies and Lazy Loading Href, Multihref and Object Data Fields Dependencies and Lazy Loading Objects with Metadata Save Objects with Metadata Unpublished relations Href, Multihref and Object Data Fields Href, multihref and objects are pure relation data types, which means they represent a relation to an other pimcore element (document, asset, object). The href and multihref data types can store relations to any other pimcore element. In the object field definition there is the possibility

98 to configure which types and subtypes of elements are allowed. The configuration screen for restrictions is shown below. The difference between href and multihref is, that a href represents a :1 relation, whereas a a multihref can be a :n relation. The objects field allows relations to one or more objects, but no other elements, therefore the restriction settings for objects are limited to object classes. Multihref and objects are grid widgets in the UI. The width and height of the input widget can be configured in the object class settings. For a href only the width can be configured, since it is represented by a single drop area. Lazy Loading is explained further below in the section about relations and lazy loading. The input widgets for all three relation data types are represented by drop areas, which allow to drag and drop elements from the tree on the left to the drop target in the object layout. The href constitutes a single drop area, whereas multihref and objects are grid widgets containing rows of data. In addition to the drag and drop feature, elements can be searched and selected directly from the input widget. In case of objects it is even possible to create a new object and select it for the objects widget. These pure relation types are stored in a separate database table called object_relations_ ID. In the according object_~id~ database view, which is used for querying data, the relations fields are summarized as a comma separated list of IDs of related elements. Therefore, if one needs to create an object list with a filter condition on a relation column this can be achieved as follows: $relationid = 345; $list = new \Pimcore\Model\Object\Example\Listing(); $list->setcondition("mymultihref like '%,".$relationid.",%'"); $objects=$list->load(); In order to set a href data field, a single pimcore element needs to be passed to the setter. With multihref and objects an array of elements is passed to the setter: use Pimcore\Model\Object; use Pimcore\Model\Document; use Pimcore\Model\Asset; $myhrefelement = Document::getById(23); $mymultihrefelements[] = Asset::getById(350): $mymultihrefelements[] = Object::getByPath("/products/testproduct"); $myobjectselements[] = Object\Product::getById(98); $myobjectselements[] = Object\Product::getById(99); $object->sethref($myhrefelement); $object->setmultihref($mymultihrefelements); $object->setobjects($myobjectselements); $object->save(); Dependencies and Lazy Loading There are several object data types which represent a relation to an other pimcore element. The pure relation types are

99 Href MultiHref Objects Furthermore, the following data types represent a relation, but they are not reflected in the object_relation_.. tables, since they are by some means special and not pure relations. (One could argue that the image is, but for now it is not classified as a pure relation type) Image Link Wysiwyg All of these relations produce a dependency. In other words, the dependent element is shown in both element's dependencies tab and pimcore issues a warning when deleting an element which has dependencies. Whenever an object is loaded from database or cache, all these related objects are loaded with it. Especially with MultiHrefs and Objects it is easy to produce a huge amount of relations, which makes the object or an object list slow in loading. As a solution to this dilemma, multihref and object data types can be classified as lazy loading attributes in the class definition. Object attributes which are lazy, are only loaded from the database/cache when their getter is called. In the example above this would mean, that the multihref data is only loaded when calling $object->getmultihref(); otherwise the attribute ($object->multihref) remains null. In order to remove all elements from this object's multihref field, the setter can be called with null or an array: $object->setmultihref(array()); //that would have the same result $object->setmultihref(null); Internally the setter sets the value to an empty array, regardless if an empty array or null is passed to it. Be Careful - Use Getters and Setters! $object->multihref = null; Will not work to clear the list of elements in the multihref when lazy loading ist activated. If the value of an object or multihref data type is null, for pimcore this means that the data of this field has not been loaded an that it is not to be touched when saving the object. Objects with Metadata This data type is an extension to the objects data type. To each assigned object additional metadata can be saved. The type of the metadata can be text, number, selection or a boolean value. A restriction of this data type is that only one allowed class is possible. As a result of this restriction, it is possible to show data fields of the assigned objects. Which metadata columns are available and which fields of the assigned objects are shown has to be defined during the class definition.

100 The shown class definition results in the following object list in the object editor. The first two columns contain id and name of the assigned object. The other four columns are metadata columns and can be edited within this list. All the other functionality is the same as with the normal objects data type. To access the metadata programmatically have a look at following code snipped. use Pimcore\Model\Object; $object = Object::getById(73585); //getting list of assigned objects with metadata (array of Object\Data\ObjectMetadata) $objects = $object->getmetadata(); //get first object of list $relation = $objects[0]; //get relation object $relationobject = $relation->getobject(); //access meta data via getters (getter = key of metadata column) $metatext = $relation->gettext(); $metanumber = $relation->getnumber(); $metaselect = $relation->getselect(); $metabool = $relation->getbool(); //setting data via setter $relation->settext("metatext2"); $relation->setnumber(5512); $object->save(); Save Objects with Metadata Programmatically save metadata

101 use Pimcore\Model\Object; //load your object (in this object we save the metadata objects) $object = Object::getById(73585); //create a empty array for your metadata objects $objectarray = []; //loop throu the objectlist (or array...) and create object metadata foreach( $yourobjectslist as $yourobject ){ //create the objectmetadata Object $objectmetadata = new Object\Data\ObjectMetadata('metadata', ['text', 'number'], $object); //set into the metadata field (named text) the value "Metadata" $objectmetadata->settext('metadata'); //set into the metadata field (named Number) the value 23 $objectmetadata->setnumber(23); //set your object relation to the metadata $objectmetadata->setobject( $yourobject ); //add to the empty "objectarray" array $objectarray[] = $objectmetadata; //set the metadataarray to your object $object->setmetadata($objectarray); // now save all $object->save(); Unpublished relations Related items that are unpublished are normally not returned. You can disable this behavior like this: //also include unpublished relations form now on Object\AbstractObject::setHideUnpublished(false); //get a related object that is either published or unpublished $relationobject = $relation->getobject(); //return to normal behavior Object\AbstractObject::setHideUnpublished(true); Localized Fields The datatype "Localized Fields" is a container which can be filled with selected datatypes and layouts. The advantage of this is to make it very easy to translate fields to the configured languages. Define your localized fields First of all you have to configure your localized fields and layouts, this can be simply done in the class editor.

102 If not already configured, please specify the valid languages for your website. You can do this in Settings -> System -> General

103 Then the result in your object editor will look like this:

104 Now pimcore generates automatically the input widgets for every configured language.

105 By default, tabs are used if the number of languages does not exceed 15. This limit can be changed via the field settings. Accessing the data Accessing the data is very easy:

106 // with global registered locale $object = Object::getById(234); $object->getinput1(); // will return the en_us data for the field "input1" // get specific localized data, regardless which locale is globally registered $object->getinput1("de") // will return the German value for the field "input1" Setting data is as simple as getting the data: $object = Object::getById(234); $object->setinput1("my Name", "fr") // set the French value for the field "input1" WARNING Please note that moving a field from outside (normal object field) into the localizedfield container means the loss of data from the field in all objects using this class. Non-Owner Objects Non-Owner objects are the counter part to the objects field. They allow to display and edit relations which are owned by a remote object. This is best explained with an example: Let's say there is a product, which has an object field called "accessories". The straight forward way of establishing a relation is to open a product and assign it it's accessories by dragging and dropping other products into the accessories field. If

107 you now also want to be able to go to the accessory product and define which other products this is an accessory of, you'd have to set up a non-owner field to do that. The non-owner field needs to be configured with the remote class name and the field name of the objects field in the remote class. Having done that, you can not only assign accessories to a product, but you can also define of which other products the current one is an accessory of. The non-owner objects do not have a database column or any data storage at all. They are stored in the remote object and merely represent a different way of establishing data relations from within the dependent object. When an object is added to a non-owner field, this means the object owning the relation is modified implicitly. If the owning object is open in pimcore as well, pimcore will warn you that you are about to modify an object that is already open. If the owning object is already open in the UI, it will have to be reloaded, before the new relation established by a remote object, becomes visible. What about getters and setters for this object field? Non Owner objects are a pure pimcore admin feature, they don't play any role in scripting or services. Since non owner objects are owned by the remote object, they can only be set through the remote owner. Also the getter has been omitted because non-owner objects are not exposed through exporters or webservices. The best way to "get" non owner objects would be to use the getrelationdata() method of objects: $def = $object->getclass()->getfielddefinition("mynonownerobjectfield"); $refkey = $def->getownerfieldname(); $refid = $def->getownerclassid(); $nonownerrelations = $object->getrelationdata($refkey,false,$refid); Number Fields - Numeric, Slider Both numeric data types are stored as a number in a DOUBLE column in the database. To set numeric data, a number must be passed to the according setter. The two fields merely differ in their GUI input widgets and the fact that the slider has a min/max value and step size, which the numeric field does not have. Numeric The numeric data field can be configured with a default value. In the GUI it is represented by a spinner field. Slider In the GUI a slider can be used as a horizontal or vertical widget. It needs to be configured with a min and max value, the increment step and

108 decimal precision. Other Fields - Image, Image with Hotspots, Checkbox, Link Checkbox A checkbox field can be configured to be checked by default when a new object is created. This can be achieved by checking "Default value" in the object field settings. In the UI a checkbox is displayed as a simple checkbox. It is stored in a TINYINT column in the database with the value 0 or 1. In order to set a checkbox value, a bool value needs to be passed to the according setter of the object: $object->setcheckbox(true); Image An image field is stored in an INT column in the database. It holds the ID of the referenced Asset_Image. Unlike other object relation types, an image relation is not stored in the relations table (this has historic reasons), but it creates a dependency in the dependencies table. To set an object's image field programmatically, an Asset_Image must be passed to the according setter.

109 $image = Asset\Image::getByPath("/examples/example1.jpg"); $object->setimage($image); $object->save(); The image field is represented in the UI by an image drop area. The image drop area's width and height can be configured in the class settings as follows: In the frontend The get a thumbnail of an image field, just call getthumbnail() on the returned asset object. <?php if ($object->getmyimage() instanceof Asset\Image) {?> <img src="<?= $object->getmyimage()->getthumbnail("mythumbnailname")?>" /> <?php?> Since $object->getimage() just returns an asset object, you can of course use all other thumbnail features of Asset_Image. See here. Image Advanced (supporting Hotspots/Markers/Cropping) This data type is an advanced extension to the image data type which allows defining hotspots, markers and cropping on the assigned image. The hotspots are defined by a name and are stored as an array with the attributes name, top, left, width and height whereas the values top, left, width, height are stored as percentages of the image dimensions. Get Hotspots To access the hotspots programmatically, following code snipped can be used.

110 $hotspotimage = $object->gethotspot1(); // name of the field in pimcore is "hotspot1" in this case (class definition) //get the assigned Asset\Image $image = $hotspotimage->getimage(); //get an array of all defined hotspots $hotspots = $hotspotimage->gethotspots(); The content of $hotspots could look like: Array ( [0] => Array ( [name] => hotspot1 [top] => [left] => [width] => [height] => ) ) [1] => Array ( [name] => hotspot2 [top] => [left] => [width] => [height] => ) This information can be used in the frontend to visualize the hotspots. Get Markers For markers this is completely the same procedure: $hotspotimage = $object->gethotspot1(); // name of the field in pimcore is "hotspot1" in this case (class definition) //get an array of all defined marker $marker = $hotspotimage->getmarker(); The content of $marker could look like:

111 Array ( [0] => Array ( [top] => [left] => ) ) [1] => Array ( [top] => [left] => ) Get the cropped image To get the cropped image you have to use the getthumbnail() method: $hotspotimage = $object->gethotspot1(); // name of the field in pimcore is "hotspot1" in this case (class definition) echo $hotspotimage->getthumbnail(); // this example returns the image cropped with the original dimensions echo $hotspotimage->getthumbnail("mycustomthumbnail"); // this example returns the cropped thumbnail according to the configuration (similar to document's $this->image()) Thumbnail of image of course you can use the above code regardless if the image is cropped or not: $hotspotimage->getthumbnail("mycustomthumbnail"); // $hotspotimage contains no cropping information, the thumbnail is returned as usual (see $assetimage->getthumbnail("..."); ) Populate the advanced image type $image = Asset::getById(123); $advancedimage = new Object\Data\Hotspotimage(); $advancedimage->setimage($image); //... $object->setmyadvancedimage($advancedimage); Link

112 In the UI a link is displayed as text. Its details can be edited by clicking on the button next to the link text. In the object class definition there are no special configurations available for an object field link. The link object field has its own data class which is Pimcore\Model\Object\Data\Link. In order to set a link programmatically an Pimcore\Model\Object\Data\Link object needs to be instantiated and passed to the setter: $l = new Object\Data\Link(); $l->setpath(" $l->settitle("pimcore.org"); $object->setlink($l); In the database the link is stored in a TEXT column which holds the serialized data of an Object_Data_Link. In the frontend (template) you can use the following code to the the html for the link <?php $object = Object::getById(234);?> <ul> <li><?= $object->getmylink()->gethtml();?></li> </ul> Select Fields - Select, Multiselect, User, Country, Language

113 There are 7 different select widgets available. Except the Multiselect widgets, all of them are portrayed by an input field with a drop-down list of options. The database column type is VARCHAR for all select data types. The configured value (not the display value!) is stored in the database. In the case of multiselect, values are stored as a comma separated list. In order to set a select field's value programmatically, the value is simply passed to the setter. To set the values of a multiselect field, an array of values is passed to the setter. $object->setselect("1"); $object->setmultiselect(array("1","2")); $object->setlanguage("en"); $object->setcountry("au"); $object->setuser(1); $object->save(); If one needs to find out what options are available for a select field. This can be done by getting the field definition as follows: $fd = $object->getclass()->getfielddefinition("multiselect"); $options = $fd->getoptions(); If one needs to find out what options are available for a select field inside an ObjectBrick. This can also be done by getting the field definition of the brick as follows: $fd = $brick->getdefinition()->getfielddefinition("multiselect"); $options = $fd->getoptions(); The display name values can be obtained as follows:

114 $o = Object::getById(49); $values = Object\Service::getOptionsForMultiSelectField($o, "multiselect"); // for a multiselect data field $values1 = Object\Service::getOptionsForSelectField($o, "select"); // for a select data field For select and multiselect the options can be defined with a value and display value in the class definition Country and language have fixed option values. For the language field the options can be limited to available system languages. The country and language select field are also available as multi select fields. The user field has fixed values as well. It allows to select a user from all available pimcore system users. Thereby a system user can be associated an object. This is explained in detail in the best practice about extending a pimcore user. Structured Data Fields Structured Data Fields are not supported inside localized fields and cannot be nested (Fieldcollection in Object Bricks,...) Types Key Value Pairs Structured Data Fields - Classification Store Structured Data Fields - Fieldcollections Structured Data Fields - Objectbricks Structured Data Fields - Structured Table Structured Data Fields - Table Key Value Pairs It allows you to add an arbitrary number of key/value pairs to your object with the restriction that each key can only be added once to that object. Keys are defined and managed in a global list and can be organized into groups (optional). Key Definition Open the key/group definition tab via the Settings Options KeyValue Config menu. Adding a group Groups are used to group keys together, for example describing certain aspects of a feature set. Add a group by clicking on the Add button. A description can be entered directly in the description grid column.

115 Adding a key Switch to the Key definition tab. You will see a list of all defined keys: ID: the internal database id Name: The name of the key Description: A user-friendly description of the key (optional but recommended) Type: The datatype of the value, can be either text, number, bool or select Detailed Configuration action column. Currently used for select datatype allowing you to enter possible options. See below. Unit: Additional optional field which can be used to enter a dimension unit for example. GID: the group id if associated with a group Group: the group description (as entered in the group configuration) if associated with a group Search action column: Opens the group selection dialog Remove action column: Removes the key from the global list. A key can be added by clicking on the Add button. Note that the key name must be unique. Double-click on description, type or unit cell if you want to add a description, change the data type or specify the dimension unit, respectively. For the select data type you also have to provide a list of options. Click on the Detailed Configuration button which will open the options editor.

116 Click on the Magnifier class if you want the key to be part of a group.

117 Either double-click on a group or select a group and confirm using the Apply button. This can be undone by opening the group selection dialog again and closing it using the Apply button without a selection. Adding a KeyValue field to your class Note: there can be only one KeyValue field per object. The field s name is predefined and cannot be changed.

118 There are certain special settings which are primarily used for the grid editor. Key column width: The width of the key name/key description column. Group column width: The width of the group/group description column. Value column witdh: The width of the value column. Max Height: The maximum height of the grid. Adding KeyValue pairs to the object Click on the plus sign to add one or more key/value pairs to your object. This will open the group/key selection dialog. Pick one or more keys which should be added to your object. Selecting a group will add all keys within that group. Double click inside the value column to enter a value. The actual behavior depends on the data type declared in the key configuration. Grid Column Editor Unlike other complex data types, the Search, Edit and Export grid column configuration can be configured on a key basis. Open the grid column configuration dialog. Either double click on keyvalue pairs or drag&drop the keyvalue field which will then open the key selection dialog allowing you to choose one or more keys. Similar to before a group can be selected which will then add all keys of that group to the grid.

119 Programming with KeyValue pairs The key definition consists at least of a key name and its type. $keyconfig = new Object\KeyValue\KeyConfig(); $keyconfig ->setname( keyname ); $keyconfig ->settype("text"); $keyconfig ->save(); Supported types are: "text" "number" bool (yes or no) select For select you have to specify a list of possible values which is expected as a json-encoded list of possible options. An option consists of text represented to the user and the internal value used for storing the actual choice. $options = array( array("key" => "option1", "value" => "1"), array("key" => "option2", "value" => "2") ); $keyconfig ->setpossiblevalues(json_encode($options)); $keyconfig ->save(); $keyid = $keyconfig->getid(); Setting a description: $keyconfig ->setdescription( very nice description ) Associate with a group: $keyconfig->setgroup(9); // expecting the group s id Creating a new group: $groupconfig = new Object\KeyValue\GroupConfig(); $groupconfig->setdescription("nice description"); $groupconfig->setname("groupname"); $groupconfig->save(); $groupid = $groupconfig->getid(); // use this id to associate a key with a group Setting an object's key/value pairs: The following example shows how to add two keyvalue pairs to an object assuming that the key id is already known. The key id can be retrieved from Pimcore\Model\Object\KeyValue\KeyConfig by calling getid();

120 $keyconfig1 = Object\KeyValue\KeyConfig::getByName( keyconfig ); key config by name and retrieve the id $keyid1 = $keyconfig->getid(); // look up the $keyconfig2 =... $keyid2 = //The key value data field expects a list of key id / value pairs, so... $pairs = array();$pair1 = array(); $pair1["key"] = $keyid1; $pair1["value"] = some value ; $pairs[] = $pair1; $pair2 = array(); $pairs[] = $pair2; // now create a new KeyValue object, set the object id and pass in the key/value pairs. $keyvaluedata = new Object\Data\KeyValue(); $keyvaluedata->setobjectid($obj->getid()); $keyvaluedata->setproperties($pairs); $obj->setkeyvaluepairs($keyvaluedata); // Finally, save your object $obj->save(); // // Working with existing Key-Value Pairs // $object = Object\Article::getById(62883); // all following getters return an instance of Object\Data\KeyValue\Entry // magic getter for getting entry by key name $object->getkeyvaluepairs()->getbaa351005(); // extended magic getter for getting entry by key name in combination with group name // useful in case of not unique key names $object->getkeyvaluepairs()->getwithgroupnamebaa351005(" _(eclass-6.0)"); // getting entry by key name $object->getkeyvaluepairs()->getentrybykeyid(871); // setting single value for a key - all existing values with that key are removed $object->getkeyvaluepairs()->setvaluewithkeyid("1366", "NEW VALUE 2")); // setting multivalent value for a key - all existing values with that key are removed $object->getkeyvaluepairs()->setvaluewithkeyid("1366", array("new VALUE 2", "NEW VALUE 3")));

121 Structured Data Fields - Classification Store EXPERIMENTAL, since or build 3529 The classification store has quite some similarities with the KeyValue datatype, the way how the keys can be added to the object differs as well as the variety of supported datatypes. The most important facts: Inheritance is supported An object can have more than one classification store Localization is supported (optional, can be configured in the class definition) The classification store introduces the concept of a fallback language All simple datatypes (e.g. textarea, date, etc are supported) Takes advantage of the built-in mechanism for the field definition + data editing (validation, etc) Keys can be organized in groups A key can belong to several groups Individual keys currently cannot be added to the object. Instead, the corresponding groups added The allowed groups can be restricted via the class definition Key definition Select type Configure sort order for object editor, keys with lower value are listed first

122 Click on the configuration button on the right for detailed settings Note that not all settings are respected (e.g. "indexed") Group definition and key assignment Use the group editor to define and organize keys into groups Similar to keys a sort order can be specified Groups with lower sort order are displayed first A key can belong to more than one group It is not necessary for the group name to be unique Use the grid on the right side to manage the keys belonging to the selected group Collection definition Groups can optionally be organized into collections A collection is simply a container which allows to add several groups at once, there is no actual logic behind it Class definition Localization can be enabled, by default only the "default" language is available Allowed groups can be restricted by providing a comma-separated list of group ids There can be more than one classification store

123 Inheritance In contrast to localized fields fallback and inherited values are first resolved in a horizontal way. If no value can be found on the same level, the parent level is scanned in the same language order. As mentioned before, there is the concept of a default language which is just an additional pseudo language which acts as the last resort. Consider the following example and let s assume that English is the fallback language for German. We request the German value for the object at level 3. Since the only value can be found on level 1 for the default language the tree is traversed as depicted. API

124 // setter, group id = 1, key id id = 2, language = de $object->getclassificationstore2()->setlocalizedkeyvalue(1, 2, "thevalue", "de"); // getter, group id = 1, key id id = 2, language = de $value = $object->getclassificationstore2()->getlocalizedkeyvalue(1, 2, "de"); // get the list of active groups $store = $object->getclassificationstore2(); $groups = $store->getactivegroups(); // get all values as associative array [groupid][keyid][language] => value $allvalues = $store->getitems(); Object Editor Groups can be added/removed via the add/remove buttons (see screenshot) Keys are displayed in the specified sort order. If the order is equal then the keys are sorted by creation date Structured Data Fields - Fieldcollections

125 Fieldcollection Object field collections are predefined sets of data and layout fields, that can be added to objects at an arbitrary amount. An object field collection is very similar to an object itself, but with the difference that a field collection can not contain relation data types. It has a "class" or in this case "field definition" which needs to be made first, and then different field collection definitions can be used to add sets of fields to an object. So with some restrictions you could say, a field collection is an object within an object. When adding a field collection field to an object's class definition, the developer needs to specify the allowed field definition types for this field. The user can then decide which and how many of the available field definitions shall be added to the object. Field definition data is stored in a separate table for each field definition and object class. The naming convention for these tables is: object_collection_collection-name_object-id. Such a table contains all the field data, the concrete object's id, field name and index of of the field collection within the field collection data field. In order to fully understand the data structure of objects and field collections, it is best to enter some example data and have a look at the tables created by pimcore. Of course, field collection data can be set programmatically as well. The following code snippet illustrates how this can be achieved. Let's say there is an object class "collectiontest" and a fieldcollection called "MyCollection". There is an object field called "collectionitems" which is of the type field collection. $object = new Object\Collectiontest(); $object->setparentid(1); $object->setuserowner(1); $object->setusermodification(1); $object->setcreationdate(time()); $object->setkey(uniqid(). rand(10, 99)); $items = new Object\Fieldcollection(); for ($i = 0; $i < 5; $i++) { $item = new Object\Fieldcollection\Data\MyCollection(); $item->setmyinput("this is a test ". $i); $items->add($item); $object->setcollectionitems($items); $object->save(); Inheritance WARNING Is not possible with this datatype. Structured Data Fields - Objectbricks With Objectbricks, objects can be extended without changing the class definition. This is especially useful when storing product data. Often there is a base set of attributes, which all products have. But then there are lots of attributes, which only a subset of your products have. Take the example of a car accessories dealer. Brakes have different attributes than tires, rims or navigation systems. The old-fashioned way to deal with this use case is, to define a product class which contains all possible attributes from all product types. This would work fine, but most of the attributes will be empty and the object editor would be quite unmanageable. The new way is to use Objectbricks. The product class itself has only attributes all products have. This might be attributes like name, article number, manufacturer, price, etc. In addition to that, for each product group there is an Objectbrick. The Objectbrick for brakes has attributes like diameter, material. The Objectbrick for tires has dimension, type, maximum speed and so on. By creating a tire product object, the tire Objectbrick is added and so this tire product has all the tire attributes.

126 To one object a number of Objectbricks can be added, but just one instance per Objectbrick type. This is the main difference to Fieldcollections. Because only one instance per Objectbrick can be added to an object, Objectbricks fully support inheritance. Each attribute of an Objectbrick can be overwritten in child objects. Despite this flexibility, the database model in the background stays clean and well structured. This is because the attributes of an Objectbrick have to be defined like these of Fieldcollections. Definition of an Objectbrick As mentioned before, Objectbricks themselves are defined the same way as objects and Fieldcollections are and support the same data types as Fieldcollections.

127 To allow adding an Objectbrick to an object, two things have to be done: There is a new data type Objectbricks for classes. This data type defines, where Objectbricks can be added. A field of this data type has to be added to the object class. In the Objectbrick definition, the object class and the desired field has to be added to the allowed classes.

128 Retrieving Objectbricks via code By saving the object class, for each Objectbrick field of this class there is an own data class created with getters for each allowed Objectbrick. For our example, this data class would look for example like this. The getter $product->getbricks() returns an instance of this class filled with the Objectbricks of the $product. By calling a Objectbrick type getter, the Objectbrick class with its attribute getter is returned. //Snippet 1 - receiving data of a Objectbrick $product = Object\Product::getById(4); $tiretype = $product->getbricks()->gettire()->gettiretype(); Setting data works the same way as retrieving data. For all getters there are corresponding setters. By saving an object, all bricks are saved too. //Snippet 2 - setting data of a Objectbrick $product = Object\Product::getById(4); $product->getbricks()->gettire()->settiretype("winter"); $product->save(); //Snippet 3 - adding a new Objectbrick to an object $product = new Object\Product(); $product->setkey("testproduct"); $product->setparent(object\product::getbyid(4)); $product->setname("testproduct"); $tirebrick = new Object\Objectbrick\Data\Tire($product); $tirebrick->settiretype("allyear"); $product->getbricks()->settire($tirebrick); $product->save(); Deleting ObjectBricks via code //Snippet 4 - remove all ObjectBricks from an ObjectBrick field $product->getbricks()->delete($product); $product->save(); //Snippet 5 - remove a particular ObjectBrick from an ObjectBrick field $productbricks = $product->getbricks(); $tirebrick = $productbricks->gettire(); if ($tirebrick) { $tirebrick->setdodelete(true); $product->save(); Querying for Objectbrick data Data of Objectbricks can be queried in the same way as data of fieldcollections. The Objectbricks have to be added to the Object_List object and then the Objectbrick data can be queried in the condition like in the sample below.

129 //Snippet 6 - querying for Objectbrick data $productlist = Object\Product::getList(array( /* add here all Objectbricks you need in the condition */ "objectbricks" => array("tire","brake"), /* in the condition access Objectbrick attributes with OBJECTBRICKNAME.ATTRIBUTENAME */ "condition" => "Tire.dimension > 200" )); If you want to obtain a list of objects which have a specific Objectbrick you can query for the "fieldname" value in the condition statement. //Snippet 7 - return all Products that have the Objectbrick "Tire" $productlist = Object\Product::getList(array( /* add here all Objectbricks you need in the condition */ "objectbricks" => array("tire"), /* in the condition access Objectbrick attributes with OBJECTBRICKNAME.ATTRIBUTENAME */ "condition" => "Tire.fieldname!= ''" )); Structured Data Fields - Structured Table Structured Table Similar to the table widget, the structured table can hold structured data. But there are a few fundamental differences: The rows and columns are predefined and named. The data type per column can be defined. Possible data types are text, number and boolean. The data of a structured table can be accessed via getters and setters and is stored in a structured way in the database. An example of a structured table in the object editor is shown below: The definition in the class definition of the table above would look like:

130 Via code, the data of this field can be accessed as shown in the following code snippets: $structureddata = $object->getexample(); //Delivers an Object\Data\StructuredTable object with an associated array for the rows and columns p_r($structureddata); //Delivers an associated array of row CommunityEdition with all columns p_r($structureddata->getcommunityedition()); //Delivers an associated array of row CommunityEdition with all columns p_r($structureddata->getcommunityedition support()); //Delivers an associated array of row CommunityEdition with all columns p_r($structureddata->setcommunityedition support("forum")); //Alternave way of setting data to a structured table $data = array(); $data['communityedition']['opensource'] = true; $structureddata->setdata($data); Based on the definition of above, following database columns are generated (FIELDNAME ROWNAME#COLUMNNAME): example CommunityEdition#OpenSource example CommunityEdition#Price example CommunityEdition#Support example EnterpriceEdition#OpenSource example EnterpriceEdition#Price example EnterpriceEdition#Support example StandardEdition#OpenSource example StandardEdition#Price example StandardEdition#Support Within these columns the data is stored. Because of that, queries can be executed on each cell of the structured table. Structured Data Fields - Table

131 Table The table widget can hold structured data in the form of an array. The input widget for table data is a table with variable rows and columns as shown below. The data is stored in an array, which needs to be flattened for storage in the database. For this purpose columns are separated with a " " and rows are distinguished with line breaks. The database field for a table is a TEXT column. For example, the data shown in the screen above would be stored as: eins zwei drei vier fünf sechs The input widget can be preconfigured with default data or a fixed amount of rows and columns. The default amount of rows and columns, as well as the default data, can be changed later when the data is entered, this can not be prevented with predefined settings. In order to set table data programmatically, an array needs to be passed to the setter as shown in the code snippet below:

132 $object->settable(array(array("eins", "zwei", "drei"), array(1, 2, 3), array("a", "b", "c"))); Text Input Fields - Input, Password,Textarea, WYSIWYG Input The input field is a simple text input field. It's data is stored in a VARCHAR column in the database. The display width and database column length can be configured in the object class definition To set the value of an input field, the string value needs to be passed to the setter. $object->setinput("some Text"); $object->save(); Password The password field is basically the same as the input field with hidden input characters. It's column length can not be changed, since passwords are always stored as MD5 Hash (32 characters). If a string shorter than 32 characters is passed to the setter, it is assumed that it is a plain text password, so pimcore creates a MD5 Hash of that password and stores it in the database. If a string with 32 characters is passed to the setter, pimcore assumes that a hash was given and stores the string without further hashing in the database. The maximum length of a plain text password is 30 characters. Textarea The textarea is an input widget for unformatted plain text. It is stored in a TEXT column in the database. Setting it's value works the same as for the input field. The width and height of the input widget can be configured in the object field definition. WYSIWYG The WYSIWYG (What You See Is What You Get) input field is identical with the textarea field except for the fact that it's input widget allows formatting of text and can even hold images and links (references to assets and documents). If images and documents are used in a WYSIWYG widget, they create a dependency for the current object. To insert an image, assets can be dragged to a WYSIWYG widget. In order to create a link, a document needs to be dragged and dropped on selected text in the WYSIWYG widget.the text is stored as HTML

133 Toolbar - Configuration - Example { name: 'basicstyles', groups: [ 'basicstyles', 'list', 'links'], { name: 'blocks', { name: 'links' More examples and config options for the toolbar and toolbargroups can be found at s/plugins/toolbar/toolbar.html Please refer to the CKeditor 4.0 Documentation

134 Video Field Code Example <?php $object = Object::getById(1234); print_r($object->getmyvideo());?> Will produce the following output depending on the content:

135 # ASSET VIDEO Pimcore\Model\Object\Data\Video Object ( [type] => asset [data] => Pimcore\Model\Asset\Video Object ( [type] => video [id] => ) [poster] => Pimcore\Model\Asset\Image Object ( [type] => image [id] => ) ) [title] => My Title [description] => My Description # YouTube Video Pimcore\Model\Object\Data\Video Object ( [type] => youtube [data] => pae_ff8tv-g [poster] => [title] => My Title [description] => My Description ) # Vimeo Video Pimcore\Model\Object\Data\Video Object ( [type] => vimeo [data] => [poster] => [title] => My Title [description] => My Description ) Display Video using document's video tag

136 <?php $object = Object::getById(1234); $v = $object->getmyvideo(); $videodata = $v->getdata(); if($videodata) { $video = new Document\Tag\Video(); $video->setoptions(["thumbnail" => "myvideothumb"]); // specify your thumbnail here - IMPORTANT! $video->type = $v->gettype(); $video->id = ($videodata instanceof Asset)? $videodata->getid() : $videodata; $video->title = $v->gettitle(); $video->description = $v->getdescription(); if($v->getposter()) { $video->poster = $v->getposter()->getid(); echo $video->frontend(); Setting data to the video data type <?php // asset video with poster image $object = Object::getById(789); $assetvideo = Asset::getById(123); $assetimage = Asset::getById(456); $videodata= new Object\Data\Video(); $videodata->setdata($assetvideo); $videodata->settype("asset"); $videodata->setposter($assetimage); $videodata->settitle("my Title"); $videodata->setdescription("my Description"); $object->setmyvideo($videodata); $object->save(); Layout Elements To structure object data layout-wise, there are 3 panel types and 4 other layout elements available. Data fields are always contained in a panel. Panels can be nested and thereby a data input interface tailored to the users's needs can be designed. The three available panel types are: Panel - a plain panel holding fields Region - a region panel able to hold nested panel's in its regions north, east, west and south Tabpanel - a panel holding further nested panels as tabs Moreover, within a panel fields can be put into the following layout Components Accordion Fieldset And last but not least there are two extra layout elements: Button - with a custom handler Text - to add minimally formatted text to an object layout. This can hold descriptions and hint's which don't fit into a field's tooltip

137 Pimcore uses Ext JS layout components for all object layout elements. For a deeper understanding of the layout elements, please have a look at the Ext JS documentation pages and examples. Object Lists Once data is available in a structured manner, it can not only be accessed more conveniently but also be filtered, sorted, grouped and displayed intuitively by the use of an object list. Moreover, data can be exported very easily not only programmatically but also through the pimcore object csv export. Object lists are a simple way to retrieve objects from pimcore while being able to filter and sort data along that process. Object lists also come with a built-in paginator that simplifies the display of results in a paged manner. When working with object lists, user defined routes come in handy while implementing a object detail views. User defined routes allow directing requests to certain detail pages, even though the request does not portray the path of a document, but matches a certain route. An object list class is created automatically for each class defined in pimcore. Objects for the class "myobject" are retrieved through a list as in the following example:

138 $entries = new Object\Myclassname\Listing(); $entries ->setoffset($offset); $entries ->setlimit($perpage); $entries ->setorderkey("date"); $entries ->setorder("desc"); $entries ->setcondition("name LIKE?", ["%bernie%"]); // use prepared statements! Mysqli only supports? placeholders // or $entries ->setcondition("name LIKE :name", ["name" => "%bernie%"]); // With PDO_Mysql you can use named parameters //if necessary you can of course custom build your query $entries ->setcondition("name LIKE ". $entries->quote("%bernie%")); // make sure that you quote variables in conditions! foreach ($entries as $entry) { $entry->getname(); // there is also a shorthand eg.: $items = Object\Myclassname::getList([ "offset" => $offset, "limit" => $perpage, "orderkey" => "date", "order" => "desc" ]); // order by multiple columns $items = Object\Myclassname::getList([ "offset" => $offset, "limit" => $perpage, "orderkey" => ["date", "name"], "order" => "desc" ]); // with different directions $items = Object\Myclassname::getList([ "offset" => $offset, "limit" => $perpage, "orderkey" => ["name", "date"], "order" => ["asc","desc"] ]); // with random order $items = new Object\PhoneProduct\Listing(); $items->setorderkey("rand()", false); foreach ($items as $item) { echo $item. "<br />"; // output the path of the object // with subselect in order $items = new Object\PhoneProduct\Listing(); $items->setorderkey("(select id FROM sometable GROUP BY somefield)", false); Using prepared statement placeholders and variables The syntax is similar to that from the Zend Framework described here: adapter.select.fetchall

139 $entries = new Object\Myclassname\Listing(); $entries->setcondition("name LIKE?", "%bernie%"); $entries->load(); foreach... // using more variables / placeholders $entries = new Object\Myclassname\Listing(); $entries->setcondition("name LIKE? AND date >?", ["%bernie%", time()]); $entries->load(); foreach... // using named placeholders (recommended) - only with PDO Mysql Adapter $entries = new Object\Myclassname\Listing(); $entries->setcondition("name LIKE :name AND date > :date", ["name" => "%bernie%", "date" => time()]); $entries->load(); Conditions on localized fields $entries = new Object\Myclassname\Listing(); $entries->setlocale("en"); // string or instance of Zend_Locale $entries->setcondition("name LIKE :name", ["name" => "%term%"]); // name is a field inside a localized field container This will only search the EN value of the field "name". Disable Localized Fields in listings Sometimes you don't want to have localized data on the listings (condition & order by). For this particular case you can disable localized fields on your listing (the objects in the list will still include the localized fields). Conditions and order by statements on localized fields are then not possible anymore. $entries = new Object\Myclassname\Listing(); $entries->setignorelocalizedfields(true); $entries->load(); Get Objects matching a value of a property Often it's very useful to get a list of objects or a single object where a property is matching exactly one value. This is especially useful to get an object matching a foreign key, or get a list of objects with only one condition. $result = Object\ClassName::getByMyfieldname($value, [int $limit, int $offset]); If you set no limit, a list object containing all matching objects is returned. If the limit is set to 1 the first matching object is returned directly. Only published objects are return. Alternatively you can pass an array as second parameter which will be applied.

140 $result = Object\ClassName::getByMyfieldname($value, ['limit' => 1,'unpublished' => true]); Examples: // get a list of cities in Austria $list = Object\City::getByCountry("AT"); foreach ($list as $city) { // do something with the cities $city->getzip();... // get a city by zip $city = Object\City::getByZip(5020, 1); $city->getzip(); // do something with the city // get the first 10 cities in Austria $list = Object\City::getByCountry("AT", 10); foreach ($list as $city) { // do something with the cities $city->getzip(); Get an Object List including unpublished Objects Normally object lists only give published objects. This can be changed by setting a lists "unpublished" property to true. Example: $list = Object\News::getList(["unpublished" => true]); or $list = new Object\News\Listing(); $list->setunpublished(true); $list->load(); Filter Objects by attributes from Field Collections To filter objects by attributes from field collections, you can use following syntax (Both code snippets result in the same object list). $list = new Object\Collectiontest\Listing(); $list->addfieldcollection("mycollection", "collection"); $list->addfieldcollection("mycollection"); $list->setcondition("`mycollection~collection`.myinput = 'hugo' AND `MyCollection`.myinput = 'testinput'"); or

141 $list = Object\Collectiontest::getList([ "fieldcollections" => [ ["type" => "MyCollection", "fieldname" => "collection"], ["type" => "MyCollection"] ], "condition" => "`MyCollection~collection`.myinput = 'hugo' AND `MyCollection`.myinput = 'testinput'" ]); You can add field collections to an list object by specifying the type of the field collection and optionally the fieldname. The fieldname is the fieldname of the field collection in the class definition of the current object. Once field collections are added to an object list, you can access attributes of field collections in the condition of the object list. The syntax is as shown in the examples above FIELDCOLLECTIONTYPE~FIELDNAME.ATTRIBUTE_OF_FIELDCOLLECTION, or if you have not specified a fieldname FIELDCOLLECTION.ATTRIBUTE_OF_FIELDCOLLECTION. The object list of this example only delivers objects of the type Collectiontest, which have +) an Fieldcollection of the type MyCollection and the value testinput in the attribute myinput and +) an Fieldcollection in the field collection of the type MyCollection and the value hugo in the attribute myinput Working with Zend_Paginator Action public function testaction() { $list = new Object\Simple\Listing(); $list->setorderkey("name"); $list->setorder("asc"); $paginator = \Zend_Paginator::factory($list); $paginator->setcurrentpagenumber( $this->_getparam('page') ); $paginator->setitemcountperpage(10); $this->view->paginator = $paginator; View <?php foreach($this->paginator as $item) {?> <h2>- <?= $item->getname();?></h2> <?php?> <br /> <!-- pagination start --> <?= $this->paginationcontrol($this->paginator, 'Sliding', 'includes/paging.php', [ 'urlprefix' => $this->document->getfullpath(). '?page=', 'appendquerystring' => true ]);?> <!-- pagination end --> Partial Script (includes/paging.php)

142 <div class="pagination" style="width:100%"> <div style="float:left;width:28%"> </div> <div style="float:right;width:70%;"> <!-- First page link --> <?php if (isset($this->previous)):?> <a href="<?= $this->url(['page' => $this->first]);?>">start</a> <?php else:?> <span class="disabled">start</span> <?php endif;?> <!-- Previous page link --> <?php if (isset($this->previous)):?> <a href="<?= $this->url(['page' => $this->previous]);?>">< Previous</a> <?php else:?> <span class="disabled">< Previous</span> <?php endif;?> <!-- Numbered page links --> <?php foreach ($this->pagesinrange as $page):?> <?php if ($page!= $this->current):?> <a href="<?= $this->url(['page' => $page]);?>"><?= $page;?></a> <?php else:?> <?= $page;?> <?php endif;?> <?php endforeach;?> <!-- Next page link --> <?php if (isset($this->next)):?> <a href="<?= $this->url(['page' => $this->next]);?>">next ></a> <?php else:?> <span class="disabled">next ></span> <?php endif;?> <!-- Last page link --> <?php if (isset($this->next)):?> <a href="<?= $this->url(['page' => $this->last]);?>">end</a> <?php else:?> <span class="disabled">end</span> <?php endif;?> Page <?= $this->current;?> of <?= $this->last;?> </div> </div> Access and modify internal object list query Since rev (a976c7900b0eff2b37679a60031c7ec3c05480e6) its possible to access and modify the internal query from every object list. The internal query is based on Zend_Db_Select. // get all news with ratings that is stored in a not pimcore related table $list = new Pimcore\Model\Object\News\Listing(); // join another table $list->getquery()->join( [ 'rating' => 'plugin_rating_ratings' ], 'rating.ratingtargetid = object_'. $list->getclassid(). '.o_id', '' );

143 Related Forum Topics Get objects by relation How to use Zend Paginator with Object Lists External System Interaction Import Whenever interaction with other systems is required, data objects are the vital components of data exchange. Pimcore data objects can be created and filled programmatically in order to realize batch imports. The following example indicates the creation of a new object of the class "myclass" $object = new Object\Myclass(); $object->setcreationdate(time()); $object->setuserowner($user->getid()); $object->setusermodification($user->getid()); $object->setpublished(true); $object->setmyattribute("this is a test"); $object->setkey(1); $object->setparentid(1); $object->save(); Thus, with very few lines of codes importer scripts can be implemented to populate data objects. Please have a look at our example importer script in Best Practices - Object Import. Export Export of data objects can be achieved programmatically or through pimcore CSV export. The UI export can be found when clicking on an object folder and selecting the Search, Edit & Export Tab. Memory Issues Note If you're using / creating very much objects you should call the pimcore garbage collector after every cycle to prevent memory issues! // just call this static method Pimcore::collectGarbage(); WARNING: This will flush the entire Zend_Registry! To avoid this, you can pass an array with keys (indexes) which should stay in the registry eg. Pimcore::collectGarbage(array("myImportantKey","myConfig")); //extend the list of protected items You can also add items to the static list of globally protected keys by passing them to \Pimcore::addToGloballyProtectedItems(array(" myveryimportantkey", "mysuperimportkey",...)); This list is maintained as long as the process exists. You can remove protected keys again by calling \Pimcore::removeFromGloballyProtectedItems(array(" myveryimportantkey",...)); You can pass in a string instead of an array if you only want to supply a single key. Inheritance Data Inheritance A very important feature in connection with PIM is data inheritance. Data inheritance means, that objects of the same class can inherit data from their parent objects in the object tree. One use case is the storage of product data. Imagine, you have a group of products which have many attributes in common and differ in just

144 a few attributes (for example size, color,...). So you can create a parent product which stores all the common attributes. Then you add child products and specify attributes in which the products differ (size, color,...). All other attributes they inherit from the common parent product. Data inheritance has to be enabled in the class definition like in the screen below: If data inheritance is enabled and an attribute of an object is empty, pimcore tries to get this attribute from a parent object. This works only, if the parent object has the same class as the child object. Data inheritance between different classes is not supported. In the pimcore admin, inherited values are visualized as in the screen below: they are gray and a bit transparent, and have a green marker in the upper left corner. With click on this corner, one can open the source object of this specific attribute. To get the inherited values in the backend via code, you have to use the getter-methods of the attributes. By accessing the attributes directly, you will not get the inherited values. Bear in mind The complex data type field collections do not support inheritance. Class Inheritance

145 Pimcore data objects support inheritance, just as any php object does. In pimcore the class from which a specific data class inherits can be changed. By default a data class inherits from Pimcore\Model\Object\Concrete, but if required otherwise, a data class can extend a different parent class. If the parent class should be changed, this needs to be specified in the class definition as shown in the screen below: Be Careful This is a very advanced feature and should only be used by very experienced developers who know what they are doing and what consequences it might have when the parent class is changed from Pimcore\Model\Object\Concretev to something else. In order to maintain all pimcore functionalities, it has to be ensured that the special class used in the example above extends Pimcore\Model\Object\Concrete and that it's methods don't override and clash in unexpected ways with existing methods of Pimcore\Model\Object\Concrete or any magic functions of Pimcore\Model\Object\Concrete or it's parent classes. Hooks available when using class inheritance Currently there's one hook available. Hooks can be defined as simple methods in the extended class. Method Arguments Description pregetvalue($key) $key (the name of the property) This method is called in the getter. This hook makes it possible to modify data before returning it to the caller. Example: Please see the image above how to extend from a custom class. namespace Website\Object; use Pimcore\Model; class Special extends Model\Object\Concrete { public function pregetvalue($key) { if($key == "mycustomproperty") { return strtolower($object->mycustomproperty); Custom Icons Objects can be displayed in pimcore with custom icons. This makes objects distinguish themselves visually based on the class they are based on. In the object tree the user can see on the first sight what an object should represent. The example below shows how custom icons are assigned to a class (Extras/Object/Classes) and how they are displayed in the object tree. It is easy for the user to see immediately which objects are of the type "football".

146 Icons that come along with pimcore by default can be found in PIMCORE_DOCUMENT_ROOT/pimcore/static/img/icon. Icon sizes Icons need to be 16x16 pixels high and wide. Bigger images are shown resized to 16x16 in Class View and Object Tree, but are shown full size in tabs as background-image. Locking fields Sometimes it's useful that a field cannot be modified/deleted in the class editor. Especially if a class is created by a plugin. pimcore offers the possibility to lock a field programmatically, you can call the method setlocked() on every Pimcore\Model\Object\Class\Data object. Example The following example will lock every field inside the class with the ID 7. $class = Object\ClassDefinition::getById(7); $fields = $class->getfielddefinitions(); foreach ($fields as $field) { $field->setlocked(true); $class->save(); Object Variants The best way to show the use and function of object variants is via an use case: Your goal is to store lots of products in pimcore. Many of these products are variants of each other, for example a yellow t-shirt, a blue t-shirt, a red t-shirt etc. Most of the t-shirts' attributes have the same values and they just differ in color and ean code. One way to archive this is to make a generic t-shirt object and then create for each variant a child object within the tree. This approach works fine, but if you have dozens or even hundreds of variants, your object tree becomes quite big and confusing.

147 This is where object variants come in. Basically they are just objects which aren't shown in the object tree. In the tree, you just create the generic t-shirt. For each variant of this t-shirt, you create an object variant, which is not shown in the object tree but in an own tab within the object editor. So, you can create hundreds of object variants without blowing your object tree. As the normal object grid, the object variant grid supports paging, filtering, hiding of columns and visualization of inherited values. So even a big number of variants should be manageable. Create and organize Object Variants To use object variants, they have to be activated in the class definition first. Object variants only make sense, if inheritance is activated. Therefore inheritance is a requirement for object variants.

148 Once they are activated, the object editor has an additional tab 'Variants'. There, all variants of the current object are shown in a grid. Via buttons object variants can be created, opened and deleted. To create object variants via code, just create an normal object, set as parent the generic t-shirt and set the object type to Object_Abstract::OBJECT_TYPE_VARIANT. $objectx = new Object\Product(); $objectx->setparent(object\product::getbyid(362603)); $objectx->setkey("variantname"); $objectx->setcolor("black"); $objectx->settype(object\abstractobject::object_type_variant); $objectx->save(); Query Object Variants Get all Object Variants of an object Getting all variants of an object is quite simple. Just call getchilds and pass the wanted object types as an array. If only variants should be returned use following line. $objectx->getchilds(array(object\abstractobject::object_type_variant)); By default, getchilds delivers objects and folders but no variants.

149 Object Variants in Object Lists Similar to getchilds, the object list objects now have an object type property, which defines the object types to deliver. Per default objects and folders are delivered. To deliver object variants, use one of the following code snippets: $list = new Object\Product\Listing(); $list->setobjecttypes(array(object\abstractobject::object_type_variant)); $list->load(); or Object\Product::getList(array( "objecttypes" => array(object\abstractobject::object_type_variant) )); Object Preview The object preview allows you to configure a preview URL for your objects, you can do that in the basic configuration of your class (see screen below). This URL can be a normal detail-page of your object, for example reachable via a custom route. You can define placeholders in your preview URL, for example the object ID or some custom properties of the class. You can use every property defined in the class even the properties which are added automatically by pimcore, for example o_id, o_key,... Placeholders have the same syntax as you already know from the custom routes, just put a % in front of the property name. When opening the preview tab in an object, the placeholders are replaced with the current values of the object and the URL is opened in the tab. Only if you configure a preview URL in the class configuration, the preview tab is shown! Example The above configuration (/news/%name_%o_id{_) will result in the URL /news/my+news+title_ (or whatever the object id is). Object Tree The following sites describe useful features within the object tree: Custom icons and style in object-tree Custom icons and style in object-tree (since 1.4.2)

150 It is possible to define custom icons and styles for objects in the object tree. In order to do so, overwrite the method getelementadminstyle of Object_Abstract by using the class mapping functionality (Extending pimcore) and return your own implementation of Element_AdminStyle. Possible properties to define: Element css class Element icon element icon class Extend the object class and overwrite the getelementadminstyle: public function getelementadminstyle() { if (!$this->o_elementadminstyle) { $this->o_elementadminstyle = new Website_OnlineShop_AdminStyle($this); return $this->o_elementadminstyle; Custom Implementation of Element_AdminStyle class Website_OnlineShop_AdminStyle extends Element_AdminStyle { public function construct($element) { parent:: construct($element); if($element instanceof Website_OnlineShop_Product) { if($element->getproducttype() == "concrete") { $this->elementicon = '/pimcore/static/img/icon/tag_green.png'; $this->elementiconclass = null; else if($element->getproducttype() == "family") { $this->elementicon = '/pimcore/static/img/icon/tag_yellow.png'; $this->elementiconclass = null; else if($element->getproducttype() == "virtual") { $this->elementicon = '/pimcore/static/img/icon/tag_blue.png'; $this->elementiconclass = null; Example result Custom Layouts

151 It is possible to create customized layouts based on the master definition and override the settings concerning the visual aspects of the layout and data components. It is also possible to make a field editable although it is marked as non-editable in the master layout. Custom layouts are available for all admin users and can be made available to standard users through the workspace settings. In order to define a custom layout, open the Custom Layout Editor through the Configure Custom Layouts button in the class editor. You can define as many layouts as you want. In the left panel you will see the master definition, in the middle the custom layout you are currently editing and on the right the specific settings for the selected field. You are able to modifiy all visual aspects of the field. Other settings concerning the data aspects are locked. You can drag and drop elements from the master layout to the custom layout tree, or you can add layout components using the context menu. Note that there is no need to add all data elements from the master layout to the custom layout. You can choose just as many you need. This does not have any impact on your data! In the object editor, the layout can then be chosen via the reload button. Note that any data changes will be lost. Admin users will notice an extra Layout called Master (Admin Mode) which is basically the same as the Master Layout execpt that invisible fields are shown and non-editable fields are made editable again. As already mentioned above, custom layouts can be made available to standard users through the workspace settings. If no layout is selected for the given class then the data will be presented using the master layout. Otherwise, the user will have the choise between the selected layouts.

152 Reports & Marketing Table of Contents Google Analytics Integration of the tracking code If you just want pimcore to integrate the tracking code into all html pages delivered by pimcore only the "Track-ID" is required by the configuration, there is no need to enter your Google Account.

153 About the data visualization Pimcore offers an advanced integration of Google Analytics. This module is fully included in pimcore and uses directly the Google Analytics Data Export API without any data proxy or something else between. Your Google credentials are only stored locally in your pimcore installation for the direct use with the Data Export API. Using PHP to add additional code to the tracking code There are 3 places where you can inject additional and dynamic code into the standard tracking code inserted by pimcore. The 3 places are the same as in the report configuration, namely "beforeinit", "beforepageview" and "beforeend". To add code to this injection point you can simple use the code below anywhere in your code (action, view,...) \Pimcore\Google\Analytics::addAdditionalCode("var foo = 'default';"); // the default injection point is "beforeend" \Pimcore\Google\Analytics::addAdditionalCode("var foo = 'beforeinit';", "beforeinit"); \Pimcore\Google\Analytics::addAdditionalCode("var foo = 'beforepageview';", "beforepageview"); \Pimcore\Google\Analytics::addAdditionalCode("var foo = 'beforeend';", "beforeend"); This will produce a result like this:

154 <script> (function(i,s,o,g,r,a,m){i['googleanalyticsobject']=r;i[r]=i[r] function(){ (i[r].q=i[r].q []).push(arguments),i[r].l=1*new Date();a=s.createElement(o), m=s.getelementsbytagname(o)[0];a.async=1;a.src=g;m.parentnode.insertbefore(a,m) )(window,document,'script','// var foo = 'beforeinit'; ga('create', 'UA '); var foo = 'beforepageview'; if (typeof _gaqpageview!= "undefined"){ ga('send', 'pageview', _gaqpageview); else { ga('send', 'pageview'); var foo = 'default'; var foo = 'beforeend'; </script> Setup Google Analytics Reporting with OAuth2 Service Accounts Since pimcore uses the OAuth2 authentication for the Google Analytics reporting integration. That means that it is no longer required to specify the plain text credentials in the configuration. It makes it also easier to control the permissions independent from the used Google Account, this is especially useful for agencies and bigger companies, having a huge amount of GA profiles in their accounts. Following a step-by-step guide, how to setup the GA reporting in pimcore. Create a project in the Google API Console Goto Click on the projects navigation item to show all projects Click 'Create Project' A popup will appear, enter a name for your project and a project id. Click the 'Create' button to finish. Enable the Google Analytics API After your project has been created you will be able to select it from the list of projects on the main dashboard (above), click its name to be taken to your new projects settings. Enable the Google Analytics API by: Click API's & auth Click API's Enter 'Analytics' in the search box Click the button marked 'OFF' to enable Once Google Analytics has been enabled it will move to the 'Enabled APIs' Section. Enable API Access through OAuth To enable OAuth, we need to create a new set of credentials (i.e a keypair) Click the Credentials Navigation menu

155 Click the 'Create new Client ID' button Select 'Service Account' in the popup menu that appears on screen. Select P12 as the key type Click the 'Create Client ID' button to finish. Now download your private key to your local machine and then copy it in your pimcore installation at the following path: /website/var/config/google-api-private-key.p12 You will now see a new set of credentials for this service account appear in the next window. Make note of these for use in the next sections below Allow the new Service Account access to Google Analytics Now that you have a service account you must now allow it access to your google analytics account. This can be done via the same process as sharing your Google Analytics account access with other people. To Access google analytics navigate to: Select the correct account to administer Click the 'User Management' navigation option Add a new Analytics user From the user management page in Google Analytics, add a new user: Enter the ' ' credentials that were created in the API console page (bottom of screenshot) The example shows us select the 'Edit' level of access for the user, you can select any option you think appropriate. Click the 'Add' button. Configure Google credentials in Pimcore Open the pimcore admin system settings menu (left screenshot) and copy credentials from the google API console (right screenshot) In Pimcore, click Settings -> System Expand the "Google Credentials & API Keys" section Enter the "Client ID" and the " address" in the correct fields provided by pimcore. Ensure that the message "Your Google API private key is installed correctly" is shown below. If not, please put your private key (you downloaded before) to the following location on your pimcore installation: /website/var/confi g/google-api-private-key.p12 NOTE: It is recommended to clear all Pimcore cache's at this point

156 Configure Reports and Marketing In Pimcore, go to "Settings" -> "Reports & Marketing" to complete marketing and reports setup. Troubleshooting General Building URL's Cache Custom Cache Backends Output-Cache Custom Reports (previously SQL Reports) Custom Routes (Static Routes) Extending pimcore Class-Mappings - Overwrite pimcore models Custom persistent models Event API (EventManager) Events Working with Events Extend pimcore using composer Hook into the startup-process Google Custom Search Engine (Site Search ~ CSE) Integration Logging Magic Parameters Newsletter Notes & Events Placeholders Object Text Properties Predefined Properties Static Helpers System Settings Tag & Snippet Management Targeting & Personalization Managing Global Targeting Rules and Personas (Target Groups) Conditions Actions Document personalization Assign Personas based on visited pages Interacting with the targeting & personalization engine Targeting: Tips for developers UUID Support Versioning Website Settings Working with Sites (Multisite) Adaptive Design / Device Helper / Tool Application Logger Reference: Database Structure, Fields, Relations - "How are objects stored?" Console / CLI Building URL's URL's are built automatically as long as you're creating them in the admin UI. But sometimes it is necessary to build them directly in the template, for some reasons. The Zend Framework provides a so called view helper to easily create URL's in views. pimcore just uses/extends this functionality, so it behaves just like in any other ZF application. Creating URL's to Documents

157 The default route is also responsible to assembe the URL's for documents. Examples Simple link to different document: <a href="<?= $this->url(array("document" => Document::getById(2)), "default", true);?>">test-link</a> Link: /about Link to same document (the request came from) adding parameters: <a href="<?= $this->url(array("key" => "value"));?>">test-link</a> Link:?key=value Link to different document adding parameters <a href="<?= $this->url(array("document" => Document::getById(2), "key" => "value"), "default", true);?>">test-link</a> Link: /about?key=value Creating URL's to Static Routes / Objects This is described in detail here. Cache Pimcore and the cache Pimcore uses extensively caches, for differently types of data. The primary cache is a pure object cache, every element (document, asset, object) in pimcore is cached as it is (serialized objects). Every cache item is tagged with dependencies so the system is able to evict dependent objects if the referenced object changes. The second cache is the output cache, which you can use either as pure page cache (configurable in system settings), or as in-template cache (see more at template helpers). The third cache is used for add-ons like the glossary, translations, database schemes, and so on. The behavior of the caches is controlled by the add-on itself. All of the described caches are utilizing the Pimcore\Model\Cache interface to store their objects. Pimcore\Model\Cache just wraps a Zend_Cache instance. Element cache workflow (Asset, Document, Object)

158 Contents Using the cache for your application The pimcore cache is a wrapper of the Zend_Cache, all functionalities are wrapped in this class Pimcore\Model\Cache. You can use this functionality for your own application, and also to control the behavior of the pimcore cache (be careful!). Example of custom usage in an action $lifetime = 99999; $uri = " $cachekey = md5($uri); if(!$data = \Pimcore\Model\Cache::load($cacheKey)) { $httpclient = \Pimcore\Tool::getHttpClient(); $httpclient->seturi($uri); try { $response = $httpclient->request(); if($response->issuccessful()) { $data = $response->getbody(); Pimcore\Model\Cache::save( $data, $cachekey, array("output","tag1","tag2"), $lifetime); catch (Exception $e) { die("something went wrong,... sorry");

159 Overview of functionalities // disable the cache globally \Pimcore\Model\Cache::disable(); // enable the cache globally \Pimcore\Model\Cache::enable(); // invalidate caches using a tag \Pimcore\Model\Cache::clearTag("mytag"); // invalidate caches using tags \Pimcore\Model\Cache::clearTags(array("mytag","output")); // clear the whole cache \Pimcore\Model\Cache::clearAll(); // disable the queue and limit and write immediately \Pimcore\Model\Cache::setForceImmendiateWrite(true); Disable the cache for a single request Sometimes it's useful to deactivate the cache for testing purposes for a single request. You can do this by passing the parameter nocache. Note: This is only possible if you have enabled the DEBUG MODE in Settings -> System For example: This will disable the entire cache, not only the output-cache, to disable only the output-cache you can add this parameter:?pimcore_outputf ilters_disabled=true You can find more "magic parameters" here. If you want to disable the cache in your code, you can use: \Pimcore\Model\Cache::disable(); This will disable the entire cache, not only the output-cache. WARNING: Do not use this in production code! It is also possible to just disable the ouput-cache in your code, read more here. Setup a custom caching-backend see: Custom Cache Backends Custom Cache Backends Pimcore uses by default the Pimcore\Cache\Backend\MysqlTable backend for caching. This backend isn't very powerful and speedy particulary when there is a huge amount of items to cache. You can use every implementation of Zend_Cache_Backend which supports tags, you can also use your own cache backend. Because the memcache backend shipped with ZF doesn't support tags, pimcore offers a special implementation of the memcache backend (Pimcore\Cache\Backend\Memcached), which stores the tags into a MySQL table. If you want to use this implementation just have a look at the following example: Enable a custom cache To enable a custom cache backend you have to create a new file: /website/var/config/cache.xml, there is already a example cache configuration in /website/var/config/cache.xml.example Example for the pimcore memcache backend: memcache backend

160 <?xml version="1.0"?> <zend-config xmlns:zf=" <backend> <type>\pimcore\cache\backend\memcached</type> <custom>true</custom> <options> <!--<tags_do_not_switch_to_innodb>true</tags_do_not_switch_to_innodb>--> <!--<compatibility>true</compatibility>--> <servers> <host>localhost</host> <port>11211</port> <persistent>true</persistent> </servers> </options> </backend> </zend-config> mongodb backend <?xml version="1.0"?> <zend-config xmlns:zf=" <backend> <type>\pimcore\cache\backend\mongodb</type> <custom>true</custom> <options> <dbname>dbname</dbname> <!-- the following is optional - no need to include this if not necessary --> <optional> <db>dbname</db> <username>dbuser</username> <password>dbpassord</password> </optional> </options> </backend> </zend-config> custom file backend <?xml version="1.0"?> <zend-config xmlns:zf=" <backend> <type>file</type> <options> <cache_file_umask>0755</cache_file_umask> <cache_dir>/www/pimcore/www/website/var/cache</cache_dir> </options> </backend> </zend-config> redis backend Note: Requires the phpredis PHP Extension and the Redis Key-Value Store. Precompiled Binaries for Debian (and Debian-based distributions) can be found at the dotdeb Repositories. If you use phpredis as Session-Storage, keep in Mind that db 0 is already in use.

161 <?xml version="1.0"?> <zend-config xmlns:zf=" <backend> <type>\pimcore\cache\backend\redis2</type> <custom>true</custom> <options> <server> </server> <port>6379</port> <persistent>1</persistent> <database>1</database> <use_lua>1</use_lua> </options> </backend> </zend-config> Recommended Redis configuration (redis.conf): maxmemory 1gb # depending on your data maxmemory-policy volatile-lru save "" Setup a custom cache frontend (e.g. for memcache with id-prefix) You can also define a custom cache frontend, this can be also done in the cache.xml In this case we configure the memcache backend (with tagging support) which is part of the pimcore distribution. <?xml version="1.0"?> <zend-config xmlns:zf=" <frontend> <type>core</type> <options> <cache_id_prefix>pimcore_dev</cache_id_prefix> </options> </frontend> <backend> <type>\pimcore\cache\backend\memcached</type> <custom>true</custom> <options> <compatibility>true</compatibility> <servers> <host>memcachehost1</host> <port>11211</port> <persistent>true</persistent> </servers> <servers> <host>memcachehost2</host> <port>11211</port> <persistent>true</persistent> </servers> </options> </backend> </zend-config> Output-Cache

162 Overview Configure the output-cache Please Note The output-cache is disabled by default if you're logged in in the admin interface or in the case the debug mode (settings -> system -> debug) is on. The output-cache only works with GET request, he takes the whole response (only for the frontend) including the headers from a request and stores it into the cache. The next request to the same page (hostname and request-uri are used to build the checksum/hash identifier) will be served directly by the cache. You can check if a request is served by the cache or not checking the response headers of the request. If there are X-Pimcore-Cache-??? (marked orange below) headers in the response they the page is coming directly from the cache, otherwise not. If you have specified a lifetime, the response also contains the Cache-Control and the Expires header (perfect for HTTP accelerators like Varnish,... ). You can find the settings for the output-cache in the system-settings (Settings->System).

163 Enable Lifetime Exclude Patterns Disbale Cookie Tick to generally enable the output-cache. You can optionally define a lifetime (in seconds) for the output-cache, if you don't do, the cache is evicted automatically when there is a modification in the administration, if there is a lifetime the item stays in the cache even when it is changed until the TTL is over. The lifetime is useful if you have embedded some items which are not directly in the cms, like rss feeds, or twitter messages over the API. It is also highly recommended to specify a lifetime on high traffic websites so that the frontend (caches) isn't affected by changes in the admin-ui, otherwise on every change in the admin-ui the whole output-cache is flushed, what can have drastic effects to the server environment. You can define some exclude patterns where the cache doesn't affect. The patterns have to be valid regular expressions (including delimiters). Type one pattern in each line. You can define an additional cookie-name which disables the cache. The cookie "pimcore_admin_sid" (used for the pimcore admin ui) ALWAYS disables the output-cache to make editor's life easier ;-) Disable the output-cache in your code Sometimes it is more useful to deactivate the output-cache directly in the code, for example when it's not possible to define an exclude-regex, or for similar reasons. In this case you can use the following snippet to deactivate the output-cache for the current process/request: $front = Zend_Controller_Front::getInstance(); $front->unregisterplugin("pimcore\controller\plugin\cache"); You can put this snippet anywhere in your code! Disable the output-cache for a single request Just add the parameter?pimcore_outputfilters_disabled=true to the URL. Disable the output-cache with a cookie and a bookmarklet Per default the disable-cookie in the system settings is set to pimcore_admin_sid That means that if your're logged into pimcore (have a session-id cookie) you will always get the content live and not from the cache. Bookmarklet If you have the cookie pimcore_admin_sid in your system configuration you can use the following bookmarklet to disable the output-cache without having an active admin session in an other tab. To use the bookmarklet, just drag the following Link into your bookmark toolbar (any browser):

164 Custom Reports (previously SQL Reports) The module "Custom Reports" allows developers to quickly create some custom reports, charts or flat exports using different datasources. Currently only the adapter for MySQL-databases is available. Some example use cases: Statistics on object data (sales/product, stock list,...) Workflow helpers (objects that need to be updated or completed eg. missing data after the ERP import,...) Custom CSV exports for further processing (MS Excel, (Open/Libre)-Office, SAP, Navision,... ) Create a report/export This is pretty simple. Go to "Settings" -> "Reports & Marketing" then select the tab "Custom Reports". Example:

165 In this example the query (in this case pretty useless) returns 2 columns. The table "Column Configuration" is updated in real time, so you can instantly see your changes on the query. In the column configuration your're able to set a custom label text, the column width in the grid and a filter type (all are optional) for each of the returned columns. Furthermore you can select if the column should be included in the export, displayed in the grid and if the column should be sortable in the grid. Chart Settings (optional) Optional you can choose between different chart types (pie, line, bar). Depending on the selected chart type you need to define data columns for the different axis of the chart. If a chart is defined, it is displayed at the top of the report view. Following a short description of the general settings:

166 Custom Routes (Static Routes) Custom routes are used to define a URL-pattern for a specific action. For example you have a newslist, which is generated out of a object list, and you want to give the news a detail page. Since objects are not reachable via the web and they have no template assigned you can use the custom routes to create this. A configuration for a newslist could look like this: In the pattern you can define a regex, and the column "Variables" you can specify comma-separated the keys of the placeholders in the pattern regex. This is how you can access the values of the variables (placeholders) you specified in the custom route: $this->getparam('yourkey'); The default variables can be accessed also the same way. Create URL's out of Custom Routes You can use the normal Zend_View helpers to generate a valid URL out of a custom route. Simple Example: In the definition you have to define a name for the route and a reverse entry. The reverse entry is a string which can be handled by the PHP function vsprintf. The syntax for the formatting string can be found here. In the template you can use the common URL view helper:

167 <?= $this->url(array("arg1","arg2"),"news")?> The resulting URL will be /news/arg1_arg2 Advanced usage with named placeholders and optional parts It's also possible to use variables inside the reverse route as placeholders, which are filles automatically if they are available via the route. You can define a placeholder in the reverse pattern with %THE_NAME, and it's also possible to define an optional part, to do so just enbrace the part with curly braces { (see example below). The above example matches for the following URL's: /some-example/some~random~text_45 /some-example/this+is+some+random+text_998_category_776 As you can see the category part is optional. Example 1: source url = /some-other-url <?= $this->url(array( "text" => "This is some random text", "id" => 998, "categoryid" => 776, "getexample" => "some value" ), "example" )?> Since there is no parameter available out of the route pattern you have to set every parameter + there is one parameter which is not in the reverse route, so that will be added as a normal GET parameter Output will be: /some-example/this+is+some+random+text_998_category_776?getexample=some+value Example 2: source url = /some-example/some~random~text_45 <?= $this->url(array( "categoryid" => 776 ), "example" )?> The parameters text and id are available via the route pattern, so the will be added automatically if you don't specify them. Output will be: /some-example/this+is+some+random+text_45_category_776 Example 3: source url = /some-example/some~random~text_45

168 <?= $this->url(array( "id" => 776 ), "example" )?> Output will be: /some-example/this+is+some+random+text_776 Dynamic controller / action / module out of the route pattern Since pimcore pimcore supports dynamic values for the controller, action and the module. NOTE: This works only with named placeholders! It works similar to the reverse route, you can place your placeholders directly into the controller. The following screen should explain the way how it works: Advanced Usage - Direct the Request to a Plugin In the grid for the custom routes there is a hidden column "module", which can be shown by right clicking on a column head and enabling this column. When this column is filled, pimcore routes the request to a different module than the standard module (which ist website). Enter the name (= folder name) of the Plugin to which you want to route the request. Fill the other columns (route, controller, action...) as always. Site Support It's possible to generate URL's pointing to a different site inside pimcore. To do so set the option "site". Here are some examples: Linking to the site with the ID 3 <?php // using the Site object echo $this->url(array( "id" => 4, "text" => "some-text", "site" => \Pimcore\Model\Site::getById(3) ), "news"); // using the ID echo $this->url(array( "id" => 4, "text" => "some-text", "site" => 3 ), "news"); // using one of the hostname assiged to the site echo $this->url(array( "id" => 4, "text" => "some-text", "site" => "subsite.example.com" ), "news");?> Linking back to the main-site

169 <?= $this->url(array( "id" => 4, "text" => "some-text", "site" => 0 ), "news");?> Using URL helper for query string URL generation Sometimes it is useful to generate a link with just a query string. You can do so by using "false" as the 2nd parameter (instead of a routes name). <?= $this->url(["foo" => "bar"], false)?> // ==> /?foo=bar Responding 404 status code Sometimes you want to trigger a correct 404 error within your controller/action (addressed by a custom route), for example when a requested object (in the route) doesn't exist anymore. This can be done anytime and anywhere in your code with: throw new \Zend_Controller_Router_Exception("the requested object doesn't exist anymore"); This code triggers the error handler which shows the error page with the correct 404 status code. Example: public function testaction() { $object = Object::getById($this->getParam("id")); if(!$object (!$object->ispublished() &&!$this->editmode &&!$this->getparam('pimcore_object_preview') &&!$_COOKIE['pimcore_admin_sid'] ) ) { throw new \Zend_Controller_Router_Exception("the requested object doesn't exist anymore"); Related Forum Topic(s) Controllers without Documents and Static Routes Extending pimcore Contents Class-Mappings - Overwrite pimcore models Usage Example Since version it's possible to map a custom class to a Pimcore model. That means that you can use your own classes inside Pimcore, but an example will be more explanatory:

170 // define a custom class, for example: namespace Website; use Pimcore\Model\Object; class Product extends Object\Product { public function mycustomgetter () { return true; // and optionally a related list namespace Website\Product; class Listing extends \Pimcore\Model\Object\Product\Listing { public function mycustomgetter () { return true; Now create a mapping. To do so, create a XML file in /website/var/config/classmap.xml containing a simple mapping. (an example file is usually already present in the same directory) In the node names the \ is replaced by a _ and you can omit the namemspace Pimcore\Model. So \Pimcore\Model\Object\Product will get Object_Product, in the value normal backslashes are used. <?xml version="1.0"?> <zend-config xmlns:zf=" <Object_Product>Website\Product</Object_Product> <Object_Product_Listing>Website\Product\Listing</Object_Product_Listing> </zend-config> Where to put the files Because they are models the recommended place for class-mappings is the folder /website/models/. Because we are using the namespace "Website" in this example the file should be placed at /website/models/website/product.php Instead of the "Website" namespace you can use any other namespace that is registered. After registering the namespace have to make sure the right class is loaded, either by including it manually or placing it somewhere in the include path. Register a new namespace To add your own namespace place the following code in /website/var/config/startup.php: $autoloader = Zend_Loader_Autoloader::getInstance(); $autoloader->registernamespace('mynamespace'); After this you can use classes with a name like "Mynamespace" or " Mynamespace_Product" as your models. Using the class mapping After that, wherever you retrieve an Object_Product you will get an Website_Product, also the following Website_Product_List will contain Website_Product

171 $myproduct = Object::getById(234); $myproductlist = Object\Product::getList(array("limit" => 10)); if ($myproduct instanceof \Website\Product) { // yes that will be true ;-) if ($myproductlist instanceof \Website\Product\Listing) { // yes that will be also true ;-) WARNING Pimcore also uses the mapping internally. Because of that you can also hook into the core of Pimcore. Be carefully if you want to overwrite methods like save(), update(), delete() or any other method which is already defined by pimcore. So far it's only read-only that means that the mapping only take effect by the getters like Document::getById() or Asset::getList(),.. and so on. Don't forget to clear the cache after you change the configuration. Supported Types Until now this feature is only provided by classes based on Element\ElementInterface like Asset, Document, Object and their list implementations. Following a bigger example: <?xml version="1.0"?> <zend-config xmlns:zf=" <!-- Objects --> <Object_Product>Website\ObjectProduct</Object_Product> <Object_Product_List>Website\ObjectProductList</Object_Product_List> <Object_Folder>Website\ObjectFolder</Object_Folder> <Object_List>Website\ObjectList</Object_List> <!-- Assets --> <Asset_Folder>Website\AssetFolder</Asset_Folder> <Asset_Image>Website\AssetImage</Asset_Image> <Asset_List>Website\AssetList</Asset_List> <!-- Documents --> <Document_Page>Website\DocumentPage</Document_Page> <Document_Snippet>Website\DocumentSnippet</Document_Snippet> <Document_Link>Website\DocumentLink</Document_Link> <Document_Folder>Website\DocumentFolder</Document_Folder> <Document_List>Website\DocumentList</Document_List> </zend-config> Make an object class extend another class This is technically not a class-mapping, but is another way to add your own methods to an object class. If you want to have certain (static) methods available in your object class you can define a "Parent class" in the classes' basic configuration. The class will then extend the parent class.

172 Custom persistent models Write custom persistent models This code depends on Pimcore build 1638 but should work fine on older releases, too. When to use custom models The pimcore objects are very flexible but shouldn't be use to store all types of data. For example it doesn't make sense to implement a rating-, comments- or a complex blog system on top of the pimcore objects. Sometimes people also implementing really interesting things just to get a unique object key or try to build n to n relationships. This produce really ugly code, could be very slow, is hard to refactor and you will have a lot of pain if you have to merge multiple installations. Database In this example i will show you how you can save a custom model in the database. At first create the database structure for the model, for this example i'll use a very easy model called vote. it just has an id, an username (just a string) and a score. If you want to write a model for a Plugin you have to create the table(s) during the installation. CREATE TABLE `votes` ( `id` int(11) NOT NULL AUTO_INCREMENT, `username` varchar(255) DEFAULT NULL, `score` int(5) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=MyISAM AUTO_INCREMENT=44 DEFAULT CHARSET=utf8 Please mind that this is just a generic example, you also could create other and more complex models. Model Now you have to implement the model. To make it easy the model is stored into the Website library. You also could locate it into a Plugin library. Just make sure that the autoloader can locate it.

173 # website/lib/website/model/vote.php <?php namespace Website\Model; class Vote extends \Pimcore\Model\AbstractModel { public $id; public $username; public $score; public function save() { return $this->getresource()->save(); public static function getbyid($id) { $obj = new self; $obj->getresource()->getbyid($id); return $obj; // just getters/setters public function setscore($score) { $this->score = $score; public function getscore() { return $this->score; public function setusername($username) { $this->username = $username; public function getusername() { return $this->username; public function setid($id) { $this->id = $id; public function getid() { return $this->id; For every field in the database we need a corresponding property and a Setter/Getter. This is not really necessary, it just depends on your resource, just read on and have a look at the save method in the resource. The save and getbyid methods just call the corresponding resource methods. The getresouce method looks for the nearest resouce. It just appends _Resouce to the classname, if the class exists you are ready to use the resouce. if the class doesn't exists, it just continue searching using the next namespace. Small example: Website\Model\Vote looks for Website\Model\Vote\Resouce, Website\Model\Resouce, Website\Resouce. Resource Now we are ready to implement the Resouce: #website/lib/website/model/vote/resource.php <?php

174 namespace Website\Model\Vote; class Resource extends \Pimcore\Model\Resource\AbstractResource { protected $tablename = 'votes'; public function getbyid($id = null) { if ($id!= null) $this->model->setid($id); $data = $this->db->fetchrow('select * FROM '.$this->tablename.' WHERE id =?', $this->model->getid()); if(!$data["id"]) throw new Exception("Object with the ID ". $this->model->getid(). " doesn't exists"); $this->assignvariablestomodel($data); public function save() { $vars = get_object_vars($this->model); $buffer = array(); $validcolumns = $this->getvalidtablecolumns($this->tablename); if(count($vars)) foreach ($vars as $k => $v) { if(!in_array($k, $validcolumns)) continue; $getter = "get". ucfirst($k); if(!is_callable(array($this->model, $getter))) continue; $value = $this->model->$getter(); if(is_bool($value)) $value = (int)$value; $buffer[$k] = $value; if($this->model->getid()!== null) { $this->db->update($this->tablename, $buffer, $this->db->quoteinto("id =?", $this->model->getid())); return; $this->db->insert($this->tablename, $buffer); $this->model->setid($this->db->lastinsertid()); public function delete() { $this->db->delete($this->tablename, $this->db->quoteinto("id =?", $this->model->getid()));

175 Please mind that this is just a very easy example resource. You also could do more complex stuff like implementing joins, save dependencies or whatever you want. Using the Model Now you can use your Model in your Servicelayer. $vote = new \Website\Model\Vote(); $vote->setscore(3); $vote->setusername('foobar!'.mt_rand(1, 999)); $vote->save(); Testing the Model soon... Event API (EventManager) Using Events Events can be used to hook into many functionalities such as saving an object, asset or document and can be used to change or extend the default behavior of pimcore. The most common use-case for events is using them in a plugin, but of course you can use them also anywhere in your code or when hooking into the startup process. You can attach a handler at any time in your code by using the following code: \Pimcore::getEventManager()->attach("object.postAdd", function (\Zend_EventManager_Event $e) { $object = $e->gettarget(); $object->getid(); //... ); Click here to learn more about attaching listeners to an event. IMPORTANT INFO Pimcore::getEventManager() returns an instance of Zend_EventManager_EventManager and therefore it provides the full set of functionalities that the ZF provides. For details have a look at Examples The following example shows how to register events for assets, documents and objects where the event-handler is in a custom class.

176 <?php namespace Website\Custom; use Pimcore\Model; class Extension { public function handlecreate (\Zend_EventManager_Event $e) { $element = $e->gettarget(); if($element instanceof Model\Asset) { // do something with the asset else if ($element instanceof Model\Document) { // do something with the document else if ($element instanceof Model\Object\AbstractObject) { // do something with the object public function handledelete(\zend_eventmanager_event $e) { $element = $e->gettarget(); // do something with the element $extension = new Website_Custom_Extension(); foreach (["asset","object","document"] as $type) { Pimcore::getEventManager()->attach($type. ".postadd", array($extension, "handlecreate")); Pimcore::getEventManager()->attach($type. ".postdelete", array($extension, "handledelete")); The following example shows how to deal with event parameters and a static callback method. <?php namespace Website\Auth; class Handler { public static function logout (\Zend_EventManager_Event $e) { $user = $e->getparam("user"); // user is now an instance of User // do something with the user Logger::info("User with ID ". $user->getid(). " left the pimcore admin interface"); \Pimcore::getEventManager()->attach("admin.login.logout", array("\website\auth\handler", "logout")); This example show how to use an anonymous callback and a specific priority (87)

177 <?php $mycontrollerplugin = new \Website\Controller\Plugin\MyCustomPlugin(); $mycontrollerplugin->somemethod(); \Pimcore::getEventManager()->attach("system.startup", function (\Zend_EventManager_Event $e) use ($mycontrollerplugin) { $frontcontroller = $e->gettarget(); $frontcontroller->registerplugin($mycontrollerplugin);, 87); Available Events System / General Name Target Parameters Description system.startup Zend_Controller_Front This event is fired on startup, just before the MVC dispatch starts. system.shutdown - This event is fired on shutdown (register_shutdown_function) system.maintenance Pimcore\Model\Schedule\Mana ger\procedural Pimcore\Model\ Schedule\Manager\Daemon - Use this event to register your own maintenance jobs, this event is triggered just before the jobs are executed system.http-website-var-link (string) path ( Experimental) This event is fire when a /website/var/ link is generated. This event can be used to rewrite the path that is used on the website, e.g. for thumbnails,... A possible usecase is to store everything in /website/var/ on Amazon AWS S3 Since system.console.init Pimcore\Console\Application See Console / CLI Document Name Target Parameters Description document.preadd Pimcore\Model\Document - document.postadd Pimcore\Model\Document - document.preupdate Pimcore\Model\Document (bool) saveversiononly saveversiononly is set if method saveversion() was called instead of save() document.postupdate Pimcore\Model\Document (bool) saveversiononly saveversiononly is set if method saveversion() was called instead of save() document.predelete Pimcore\Model\Document - document.postdelete Pimcore\Model\Document - Object Name Target Parameters Description

178 object.preadd Pimcore\Model\Object\Abstract Object - object.postadd Pimcore\Model\Object\Abstract Object - object.preupdate Pimcore\Model\Object\Abstract Object (bool) saveversiononly saveversiononly is set if method saveversion() was called instead of save() object.postupdate Pimcore\Model\Object\Abstract Object (bool) saveversiononly saveversiononly is set if method saveversion() was called instead of save() object.predelete Pimcore\Model\Object\Abstract Object - object.postdelete Pimcore\Model\Object\Abstract Object - Asset Name Target Parameters Description asset.preadd Pimcore\Model\Asset - asset.postadd Pimcore\Model\Asset - asset.preupdate Pimcore\Model\Asset (bool) saveversiononly saveversiononly is set if method saveversion() was called instead of save() asset.postupdate Pimcore\Model\Asset (bool) saveversiononly saveversiononly is set if method saveversion() was called instead of save() asset.predelete Pimcore\Model\Asset - asset.postdelete Pimcore\Model\Asset - Object Class Name Target Parameters Description object.class.preadd object.class.preupdate Pimcore\Model\Object\ClassDef inition Pimcore\Model\Object\ClassDef inition - - Object KeyValue Group Configuration Name Target Parameters Description object.keyvalue.groupconfig.pr eadd object.keyvalue.groupconfig.p ostadd object.keyvalue.groupconfig.pr eupdate object.keyvalue.groupconfig.p ostupdate object.keyvalue.groupconfig.pr edelete object.keyvalue.groupconfig.p ostdelete Pimcore\Model\Object\KeyValu e\groupconfig Pimcore\Model\Object\KeyValu e\groupconfig Pimcore\Model\Object\KeyValu e\groupconfig Pimcore\Model\Object\KeyValu e\groupconfig Pimcore\Model\Object\KeyValu e\groupconfig Pimcore\Model\Object\KeyValu e\groupconfig

179 Object KeyValue Key Configuration Name Target Parameters Description object.keyvalue.keyconfig.pre Add object.keyvalue.keyconfig.post Add object.keyvalue.keyconfig.pre Update object.keyvalue.keyconfig.post Update object.keyvalue.keyconfig.pre Delete object.keyvalue.keyconfig.post Delete Pimcore\Model\Object\KeyValu e\keyconfig Pimcore\Model\Object\KeyValu e\keyconfig Pimcore\Model\Object\KeyValu e\keyconfig Pimcore\Model\Object\KeyValu e\keyconfig Pimcore\Model\Object\KeyValu e\keyconfig Pimcore\Model\Object\KeyValu e\keyconfig Admin Interface Name Target Parameters Description admin.login.login.failed Zend_Controller_Action (string) username,(string) password Use $e-gettarget()->setuser($user); if you want to login a user in your callback admin.login.logout Zend_Controller_Action (User) $user admin.login.index.authenticate Zend_Controller_Action (string) username,(string) password Fired at the beginning of /admin/login/index if there is no valid user. Use $e-gettarget()->setuser($user); if you want to login a user in your callback admin.login.login.authenticate Zend_Controller_Action (string) username,(string) password Fired at the beginning of /admin/login/login before any other authentication steps are taken Use $e-gettarget()->setuser($user); if you want to login a user in your callback admin.controller.preinit Zend_Controller_Action - Fired at the beginning of Pimcore\Controller\Action_Adm in::init() - only fired once (2.3.1) admin.controller.postinit Zend_Controller_Action - Fired at the end of Pimcore_Co ntroller_action_admin::init() - only fired once (2.3.1) admin.object.get.presenddata Admin_ObjectController (Pimcore\Model\Tool\Admin\Ev entdatacontainer) returnvaluecontainer, (Pimcore\Model\Object\Abstract Object) object Fired at the end of Admin_ObjectController::get() - (since build 3277) admin.object.treegetchildsbyid.presenddata Admin_ObjectController (Pimcore\Model\Tool\Admin\Ev entdatacontainer) returnvaluecontainer Fired at the end of Admin_ObjectController::treeG etchildsbyid() - (since build 3277)

180 admin.class.objectbricklist.pre SendData Admin_ClassController (Pimcore\Model\Tool\Admin\Ev entdatacontainer) returnvaluecontainer, (int) objectid Fired at the end of Admin_ClassController:objectbr icklist() - (since build 3300) Events Using Events Attaching Events (Subscribe to an event) Registering a new event (Fire an event) Available Events System / General Document Object Asset Object Class Object KeyValue Group Configuration Object KeyValue Key Configuration Admin Interface Using Events Events can be used to hook into many functionalities such as saving an object, asset or document and can be used to change or extend the default behavior of pimcore. The most common use-case for events is using them in a plugin, but of course you can use them also anywhere in your code or when hooking into the startup process. You can attach a handler at any time in your code by using the following code: \Pimcore::getEventManager()->attach("object.postAdd", function ($e) { $object = $e->gettarget(); $object->getid(); //... ); Click here to learn more about attaching listeners to an event. IMPORTANT INFO Pimcore::getEventManager() returns an instance of Zend_EventManager_EventManager and therefore it provides the full set of functionalities that the ZF provides. For details have a look at Attaching Events (Subscribe to an event) The following example shows how to register events for assets, documents and objects where the event-handler is in a custom class.

181 <?php namespace Website\Custom; use Pimcore\Model; class Extension { public function handlecreate ($e) { $element = $e->gettarget(); if($element instanceof Model\Asset) { // do something with the asset else if ($element instanceof Model\Document) { // do something with the document else if ($element instanceof Model\Object\AbstractObject) { // do something with the object public function handledelete($e) { $element = $e->gettarget(); // do something with the element $extension = new Website_Custom_Extension(); foreach (["asset","object","document"] as $type) { Pimcore::getEventManager()->attach($type. ".postadd", array($extension, "handlecreate")); Pimcore::getEventManager()->attach($type. ".postdelete", array($extension, "handledelete")); The following example shows how to deal with event parameters and a static callback method. <?php namespace Website\Auth; class Handler { public static function logout ($e) { $user = $e->getparam("user"); // user is now an instance of User // do something with the user Logger::info("User with ID ". $user->getid(). " left the pimcore admin interface"); \Pimcore::getEventManager()->attach("admin.login.logout", array("\website\auth\handler", "logout")); This example show how to use an anonymous callback and a specific priority (87)

182 <?php $mycontrollerplugin = new \Website\Controller\Plugin\MyCustomPlugin(); $mycontrollerplugin->somemethod(); \Pimcore::getEventManager()->attach("system.startup", function ($e) use ($mycontrollerplugin) { $frontcontroller = $e->gettarget(); $frontcontroller->registerplugin($mycontrollerplugin);, 87); Registering a new event (Fire an event) \Pimcore::getEventManager()->trigger("website.user.register", $this, ['firstname' => $post['firstname']]); Available Events System / General Name Target Parameters Description system.startup Zend_Controller_Front This event is fired on startup, just before the MVC dispatch starts. system.shutdown - This event is fired on shutdown (register_shutdown_function) system.maintenance Pimcore\Model\Schedule\Mana ger\procedural Pimcore\Model\ Schedule\Manager\Daemon - Use this event to register your own maintenance jobs, this event is triggered just before the jobs are executed Document Name Target Parameters Description document.preadd Pimcore\Model\Document - document.postadd Pimcore\Model\Document - document.preupdate Pimcore\Model\Document - document.postupdate Pimcore\Model\Document - document.predelete Pimcore\Model\Document - document.postdelete Pimcore\Model\Document - Object Name Target Parameters Description object.preadd object.postadd object.preupdate Pimcore\Model\Object\Abstract Object Pimcore\Model\Object\Abstract Object Pimcore\Model\Object\Abstract Object - - -

183 object.postupdate object.predelete object.postdelete Pimcore\Model\Object\Abstract Object Pimcore\Model\Object\Abstract Object Pimcore\Model\Object\Abstract Object Asset Name Target Parameters Description asset.preadd Pimcore\Model\Asset - asset.postadd Pimcore\Model\Asset - asset.preupdate Pimcore\Model\Asset - asset.postupdate Pimcore\Model\Asset - asset.predelete Pimcore\Model\Asset - asset.postdelete Pimcore\Model\Asset - Object Class Name Target Parameters Description object.class.preadd object.class.preupdate Pimcore\Model\Object\ClassDef inition Pimcore\Model\Object\ClassDef inition - - Object KeyValue Group Configuration Name Target Parameters Description object.keyvalue.groupconfig.pr eadd object.keyvalue.groupconfig.p ostadd object.keyvalue.groupconfig.pr eupdate object.keyvalue.groupconfig.p ostupdate object.keyvalue.groupconfig.pr edelete object.keyvalue.groupconfig.p ostdelete Pimcore\Model\Object\KeyValu e\groupconfig Pimcore\Model\Object\KeyValu e\groupconfig Pimcore\Model\Object\KeyValu e\groupconfig Pimcore\Model\Object\KeyValu e\groupconfig Pimcore\Model\Object\KeyValu e\groupconfig Pimcore\Model\Object\KeyValu e\groupconfig Object KeyValue Key Configuration Name Target Parameters Description object.keyvalue.keyconfig.pre Add object.keyvalue.keyconfig.post Add object.keyvalue.keyconfig.pre Update Pimcore\Model\Object\KeyValu e\keyconfig Pimcore\Model\Object\KeyValu e\keyconfig Pimcore\Model\Object\KeyValu e\keyconfig - - -

184 object.keyvalue.keyconfig.post Update object.keyvalue.keyconfig.pre Delete object.keyvalue.keyconfig.post Delete Pimcore\Model\Object\KeyValu e\keyconfig Pimcore\Model\Object\KeyValu e\keyconfig Pimcore\Model\Object\KeyValu e\keyconfig Admin Interface Name Target Parameters Description admin.login.login.failed Zend_Controller_Action (string) username,(string) password Use $e-gettarget()->setuser($user); if you want to login a user in your callback admin.login.logout Zend_Controller_Action (User) $user admin.login.index.authenticate Zend_Controller_Action (string) username,(string) password Fired at the beginning of /admin/login/index if there is no valid user. Use $e-gettarget()->setuser($user); if you want to login a user in your callback admin.login.login.authenticate Zend_Controller_Action (string) username,(string) password Fired at the beginning of /admin/login/login before any other authentication steps are taken Use $e-gettarget()->setuser($user); if you want to login a user in your callback admin.controller.preinit Zend_Controller_Action - Fired at the beginning of Pimcore\Controller\Action_Adm in::init() - only fired once (2.3.1) admin.controller.postinit Zend_Controller_Action - Fired at the end of Pimcore_Co ntroller_action_admin::init() - only fired once (2.3.1) admin.object.get.presenddata Admin_ObjectController (Pimcore\Model\Tool\Admin\Ev entdatacontainer) returnvaluecontainer, (Pimcore\Model\Object\Abstract Object) object Fired at the end of Admin_ObjectController::get() - (since build 3277) admin.object.treegetchildsbyid.presenddata Admin_ObjectController (Pimcore\Model\Tool\Admin\Ev entdatacontainer) returnvaluecontainer Fired at the end of Admin_ObjectController::treeG etchildsbyid() - (since build 3277) admin.class.objectbricklist.pre SendData Admin_ClassController (Pimcore\Model\Tool\Admin\Ev entdatacontainer) returnvaluecontainer, (int) objectid Fired at the end of Admin_ClassController:objectbr icklist() - (since build 3300) Working with Events Events can be used to hook into many functionalities such as saving an object, asset or document and can be used to change or extend the default behavior of pimcore. The most common use-case for events is using them in a plugin, but of course you can use them also anywhere in your code or when hooking into the startup process. You can attach a handler at any time in your code by using the following code:

185 \Pimcore::getEventManager()->attach("object.postAdd", function ($e) { $object = $e->gettarget(); $object->getid(); //... ); Click here to learn more about attaching listeners to an event. Extend pimcore using composer pimcore is 100% compatible to composer and it's very easy to use additional libraries installed via composer ( ) in pimcore. Example In this example we want to use some components of Symfony2 within our pimcore project. Let's start: Go to the document-root of your project and just follow the usual composer setup: Create composer.json and define your dependencies: { "require": { "symfony/symfony": "2.4.*@dev" Download and install composer: curl -ss php Install dependencies: php composer.phar install That's it! Now you can use all components of Symfony in your pimcore project, no need to do anything further, it just works! Now you can use the installed libraries anywhere in your code. In this very basic example we use the Stopwatch component in an action:

186 <?php use Pimcore\Controller\Action; use Symfony\Component\Stopwatch\Stopwatch; class TestController extends Action { public function stopwatchtestaction() { $stopwatch = new Stopwatch(); // Start event named 'eventname' $stopwatch->start('eventname'); //... some code goes here $event = $stopwatch->stop('eventname'); print_r($event); exit;... Hook into the startup-process It is possible to hook into the startup process of pimcore. This is useful if you want to add some custom ZF routes to the controller front or to add a controller plugin without creating a plugin. To use the hook create a file called startup.php in /website/var/config/ or just rename the existing startup.php.example to startup.php. This file is included at the end /pimcore/config/startup.php This hook is part of the general startup process, so this affects all modules, sapis (CLI, FCGI, mod_php,...) and of course even the admin interface. Examples You can find some examples in /website/var/config/startup.php too. Add a custom controller plugin $front = \Zend_Controller_Front::getInstance(); $front->registerplugin(new \Website\Controller\Plugin\Custom(), 700); Add a custom event handler (since 2.2.0) - hook into pimcore without using a plugin \Pimcore::getEventManager()->attach("object.postAdd", function ($e) { $object = $e->gettarget(); $object->getid(); // do something with the object ); Add a custom Zend Framework route

187 $front->addmoduledirectory('modulename'); $router = $front->getrouter(); $routecustom = new \Zend_Controller_Router_Route( 'custom/:controller/:action/*', array( 'module' => 'custom', 'controller' => 'default', 'action' => 'default' ) ); $router->addroute('custom', $routecustom); $front->setrouter($router); //init the autoloader \Pimcore::initAutoloader(); Add a custom module

188 <?php /* * Overview of the module directory * * + modules * - company * - controllers * - lib * - Company * - models * - views * * + plugins * + static * + website */ if (!defined("website_module_path")) define("website_module_path", PIMCORE_DOCUMENT_ROOT. "/modules"); $front = \Zend_Controller_Front::getInstance(); $front->addmoduledirectory(pimcore_document_root. "/modules"); // Company $autoloader = \Zend_Loader_Autoloader::getInstance(); $autoloader->registernamespace('company'); set_include_path(implode(path_separator, array( WEBSITE_MODULE_PATH. '/Company/lib', get_include_path(), ))); $resourceloader = new \Zend_Application_Module_Autoloader(array( 'namespace' => 'Company', 'basepath' => WEBSITE_MODULE_PATH. "/company", )); $router = $front->getrouter(); $routecompany = new \Zend_Controller_Router_Route( 'company/:controller/:action/*', array( 'module' => 'company', "controller" => "default", "action" => "default" ) ); $router->addroute("company", $routecompany); Google Custom Search Engine (Site Search ~ CSE) Integration Introduction pimcore provides a simple interface to the Google Custom Search Engine (Site Search) which makes it easy to integrate a search engine into your website. Further information about Google CSE/Site Search

189 Setup in pimcore Create and configure a new search engine at - for more information please visit: msearch/ Test your search engine first using the iframe version provided by Google (click on "Preview" -> left navigation). If your results are as expected, go back to the "Basics" (-> left navigation) Note the ID of your search engine - you'll need it later in the code (parameter 'cx' ): If you are finished go to click on "Services" and register for the "Custom Search API" service... then create a "Simple API Access" - Key in the API Console (click "API Access" on the left): Take this key and put it into the pimcore system settings:

190 That's it! Now you have to create an action/view,... see below. Code Example Action public function cseaction() { $this->enablelayout(); if($this->_getparam("q")) { try { $page = $this->_getparam('page'); if(empty($page)) { $page = 1; $perpage = 10; $result = \Pimcore\Google\Cse::search($this->_getParam("q"), (($page-1) * $perpage), null, array( "cx" => " xxxx :baoxxxx9mii" ), $this->_getparam("facet")); $paginator = \Zend_Paginator::factory($result); $paginator->setcurrentpagenumber($page); $paginator->setitemcountperpage($perpage); $this->view->paginator = $paginator; $this->view->result = $result; catch(exception $e) { // something went wrong: eg. limit exceeded, wrong configuration,... Logger::err($e);

191 View <div> <form action="" method="get"> <input name="q" type="text" value="<?= htmlentities($this->getparam("q"), ENT_QUOTES)?>" /> <input name="submit" type="submit" value="search" /> </form> <?php if ($this->paginator) {?> <?php $facets = $this->result->getfacets();?> <?php if(!empty($facets)) {?> <div> Facets: <?php foreach ($facets as $label => $anchor) {?> <a href="<?= $this->url(array('facet' => $label, "page" => null));?>"><?= $anchor?></a> <?php?> </div> <?php?> <?php foreach ($this->paginator as $item) {?> <!-- see class Pimcore\Google\Cse\Item for all possible properties --> <div style="<?php if($item->gettype() == "promotion") {?>background:green;<?php?>"> <?php if($item->getimage()) {?> <!-- if an image is present this can be simply a string or an internal asset object --> <div> <?php if($item->getimage() instanceof Asset) {?> <img width="90" src="<?= $item->getimage()->getthumbnail("contentimages")?>" /> <?php else {?> <img width="90" src="<?= $item->getimage()?>" /> <?php?> </div> <?php?> <div> <h2> <a href="<?= $item->getlink()?>"> <!-- wrap hardlinks --> <?php $doc = $item->getdocument(); if( $item->getdocument() instanceof \Pimcore\Model\Document\Hardlink ){ $doc = \Pimcore\Model\Document\Hardlink\Service::wrap($item->getDocument());?> <!-- if there's a document set for this result use the original title without suffixes... --> <!-- the same can be done with the description and every other element relating to the document --> <?php if($doc && $doc->gettitle()) {?> <?= $doc->gettitle()?> <?php else {?> <?= $item->gettitle()?> <?php?> </a>

192 </h2> <p> <?= $item->gethtmlsnippet()?> </p> <small><?= $item->gethtmlformattedurl();?></small> </div> <span></span> </div> <?php?> <?= $this->paginationcontrol($this->paginator, "Sliding", "includes/paging.php");?> <?php else if ($this->getparam("q")) {?> <div> Sorry, something seems to went wrong... </div> <?php else {?> <div> Type your keyword and press search </div>

193 <?php?> </div> Partial View Script (includes/paging.php) <div class="pagination" style="width:100%"> <div style="float:left;width:28%"> </div> <div style="float:right;width:70%;"> <!-- First page link --> <?php if (isset($this->previous)):?> <a href="<?= $this->url(array('page' => $this->first));?>">start</a> <?php else:?> <span class="disabled">start</span> <?php endif;?> <!-- Previous page link --> <?php if (isset($this->previous)):?> <a href="<?= $this->url(array('page' => $this->previous));?>" rel="prev">< Previous</a> <?php else:?> <span class="disabled">< Previous</span> <?php endif;?> <!-- Numbered page links --> <?php foreach ($this->pagesinrange as $page):?> <?php if ($page!= $this->current):?> <a href="<?= $this->url(array('page' => $page));?>"><?= $page;?></a> <?php else:?> <?= $page;?> <?php endif;?> <?php endforeach;?> <!-- Next page link --> <?php if (isset($this->next)):?> <a href="<?= $this->url(array('page' => $this->next));?>" rel="next">next ></a> <?php else:?> <span class="disabled">next ></span> <?php endif;?> <!-- Last page link --> <?php if (isset($this->next)):?> <a href="<?= $this->url(array('page' => $this->last));?>">end</a> <?php else:?> <span class="disabled">end</span> <?php endif;?> Page <?= $this->current;?> of <?= $this->last;?> </div> </div> Logging There are many different kinds of logs in pimcore. All logs are located under /website/var/log/ debug.log This is definitely one of the most important logs and also the default logging location.

194 You can configure the log levels in the pimcore admin UI in "Settings -> System -> Debug". For development purposes it's recommenced to turn on all log-levels. If you turn on the DEV-MODE also the SQL-profiler logs to this location. The log file will be rotated and compressed if it gets larger than 200 MB. The archived logs will be kept for 30 days. usagelog.log In this log you can find every action done within the pimcore admin ui. Example Entry T18:26:30+02:00 : 2 admin page save {"task":"publish","id":"4","data":"{\"headtitle\":{\"data\":\"get ting started\",\"..." Explanation Value (from the example above) T18:26:30+02:00 Description Timestamp 2 User-ID admin page save Module (MVC) Controller (MVC) Action (MVC) {"task":"pub... Request Parameters (shortened & censored) redirect.log Sometimes it's necessary to debug redirects, for example when a redirect ends in an infinite loop. In this log you can see every request where a redirect takes action. Example T19:49:43+02:00 : > /basic-examples Source: /asdsad/redirectsource/asd dbprofile-*.log Contains DB profiles for a single request. See Magic Parameters for more details. Writing your own log files You can add your own logging functionality using Pimcore's log writer. You can call a static function like this: Custom log entry \Pimcore\Log\Simple::log($name, $message); The $name variable defines the filename of the log file, "mylog" will write a file to website/var/log/mylog.log (extension is added automatically). If the file does not yet exist it will be created on the fly. The message is the line thgat will be written to the log. A date and time will also be prepended automatically to each log entry. Magic Parameters Pimcore supports some "magic parameters" which can be added to every request. Parameter Description

195 controller/action/template/module pimcore_document pdid nocache pimcore_outputfilters_disabled pimcore_log You can call a controller/action/template directly without a route eg.: n&template=my/template.php Sometimes it's useful to call a page directly by its ID, you can do that for example with: nt=345 Setting this parameter disables every kind of cache. eg.: Disables all outputfilters, incl. the output-cache. But this doesn't disable the internal object cache. Enables verbose logging (including database queries) to a seperate log file only for this particular request called with this parameter. If no value is set to this parameter the log file can be found here: /w ebsite/var/log/request-[y-m-d_h-i-s].log If a value is given, the value will be part of the log files name: /website/var/log/request-[n AME].log This parameter only works if DEBUG MODE is on. (this is also the successor of the parameter pimcore_dbprofile in earlier versions) pimcore_show_template_paths pimcore_disable_host_redirect Shows the template files which are included as HTML comments. This parameter only works if DEBUG MODE is on. Disables the "redirect to main domain" feature. This is especially useful when using pimcore behind a reverse proxy Newsletter Intro pimcore provides a basic newsletter framework. The main advantage is that you can send completely customized/personalized newsletters by using all the data you have in the system (products,...). The content of the is rendered individually for every recipient (the user object is available in the action and view), this gives you the absolute freedom for your content. The newsletter framework is just a wrapper for existing functionalities in pimcore. This makes it easy to use and gives you all the advantages pimcore offers you. The newsletter content is assembled in an " " document in the "Document" module. So your newsletter template is just a simple action and view, in the view you can use all the available functionalities you know from the other document types (pages, snippets,...). As mentioned before, this document is rendered individually for every user, that makes it possible to include content depending on the user data. Your "mailing list" is stored in objects, there are special data types for exactly this use case, more on that later in "Basic Setup". Basic Setup Class Definition (your "Mailing List") First you need a class for your user data. There are special data types (in section "CRM") which are all required to use the class with the newsletter framework. So your class definition should look like this:

196 The purpose of the fields gender, firstname, lastname and should be clear ;-) newsletteractive and newsletterconfirmed are used to save the state of the user (also used by the frontend framework). newsletteractive tells the newsletter framework whether to send the newsletter to this user or not. newsletterconfirmed is used for double opt-in (provided by the frontend framework). Only if newsletteractive and newsletterconfirmed are ticked the user in the object receives the newsletter. newsletterconfirmed cannot be set in the admin interface, the reason is simple: the frontend framework logs every activity to "Notes & Events" including the IP etc. from the user, so it's possible to track every modification the user has made to his profile. Now if the editor is able to change this important setting the audit trail is pitted and it's not clear why the user receives the newsletter. Of course it's possible to change the value programmatically (eg. in importers). Newsletter Frontend Framework Once you have setup your data class, it's possible to use the newsletter frontend framework. This simple framework allows you to create a hassle free subscribe/confirm/unsubscribe workflow. Example Controller <?php use Website\Controller\Action; use Pimcore\Tool\Newsletter; use Pimcore\Model\Document; use Pimcore\Model\Object; class NewsletterController extends Action { public function subscribeaction () { $newsletter = new Newsletter("crm"); // replace "crm" with the class name you have used for your class above (mailing list) $params = $this->getallparams(); $this->view->success = false; if($newsletter->checkparams($params)) { try { $params["parentid"] = 75257; // folder where we want to save our subscribers //$params["parent"] = Object::getByPath("/newsletter/subscribers"); // different way $user = $newsletter->subscribe($params); // user and document

197 // parameters available in the gender, firstname, lastname, , token, object // ==> see mailing framework $newsletter->sendconfirmationmail($user, Document::getById(3076), array("additional" => "parameters", "key" => "value")); // replace "3076" with the ID of the document you want to use as confirmation // do some other stuff with the new user //$user->setsomecustomfield(true); //$user->save(); $this->view->success = true; catch (\Exception $e) { public function confirmaction() { $this->view->success = false; $newsletter = new Newsletter("crm"); // replace "crm" with the class name you have used for your class above (mailing list) if($newsletter->confirm($this->getparam("token"))) { $this->view->success = true; public function unsubscribeaction() { $newsletter = new Newsletter("crm"); // replace "crm" with the class name you have used for your class above (mailing list) $unsubscribemethod = null; $success = false; if($this->getparam(" ")) { $unsubscribemethod = " "; $success = $newsletter->unsubscribeby ($this->getparam(" ")); if($this->getparam("token")) { $unsubscribemethod = "token"; $success = $newsletter->unsubscribebytoken($this->getparam("token")); $this->view->success = $success; $this->view->unsubscribemethod = $unsubscribemethod;

198 Views subscribe.php <?php if(!$this->success) {?> <?php if($this->getparam("submit")) {?> Sorry, something went wrong, please check the data in the form and try again! <br /> <br /> <?php?> <form action="" method="post"> <label for="gender">gender</label> <select id="gender" name="gender"> <option value=""></option> <option value="male" <?php if($this->getparam("gender") == "male") {?> selected="selected"<?php?>><?= $this->translate("male");?></option> <option value="female" <?php if($this->getparam("gender") == "female") {?> selected="selected"<?php?>><?= $this->translate("female");?></option> </select> <br /> <label for="firstname">firstname</label> <input id="firstname" name="firstname" type="text" value="<?= $this->getparam("firstname");?>" /> <br /> <label for="lastname">lastname</label> <input id="lastname" name="lastname" type="text" value="<?= $this->getparam("lastname");?>" /> <br /> <label for=" "> </label> <input id=" " name=" " type="text" value="<?= $this->getparam(" ");?>" /> <br /> <input type="submit" name="submit" value="submit" /> </form> <?php else {?> <h2>success, Please check your mailbox!</h2> <?php?> confirm.php

199 <?php if(!$this->success) {?> <h2>sorry, something went wrong, please sign up again!</h2> <?php else {?> <h2>thanks for confirming your address</h2> <?php?> unsubscribe.php <?php if(!$this->success) {?> <?php if ($this->unsubscribemethod) {?> <?php if ($this->unsubscribemethod == " ") {?> Sorry, we don't have your address in our database. <?php else {?> Sorry, your unsubscribe token is invalid, try to remove your address manually: <?php?> <?php?> <form action="" method="post"> <label for=" "> </label> <input id=" " name=" " type="text" /> <br /> <input type="submit" name="submit" value="submit" /> </form> <?php else {?> <h2>unsubscribed</h2> <?php?> Confirmation The confirmation (see $newsletter->sendconfirmationmail() above) is a simple document. In this document the following placeholders are available: and: %Text(firstname); %Text(lastname); %Text(token); %Text( ); %Text(gender); %Object(object,{"method" : "xxxx"); (details see here) Example:

200 Sending Newsletter

201 Create a Newsletter ( ) Document To send a newsletter you need an document, which is used as content for the newsletter (nothing special to consider). In this document the following placeholders are available: and: %Text(firstname); %Text(lastname); %Text(token); %Text( ); %Text(gender); %Object(object,{"method" : "xxxx");(details see here) Example

202 Send a Test Open "Extras" -> "Reports & Marketing" -> "Newsletter"... create a new newsletter

203 .. done! Sending newsletter from the command-line To send newsletters using a scheduler (cron-job,...) you can use the command-line interface: php pimcore/cli/send-newsletter.php NAME-OF-NEWSLETTER

204 Notes & Events Notes & Events are primarly used to log changes or events on elements independently from the versioning. This includes changes made by marketers, editors, automated importers / synchronisations,... simply everything that has nothing to do with the data itself but is important to know. Use-cases: An importer (CLI-script) that adds information to objects which changes were made,... Marketers / SEOs adding information which changes were made on documents like "optimized for keyword xyz..." There are really nearly endless possibilities what to do with Notes & Events. Create Notes & Events using the API use Pimcore\Model; $object = Model\Object::getById(4); $note = new Model\Element\Note(); $note->setelement($object); $note->setdate(time()); $note->settype("erp_import"); $note->settitle("changed availabilities to xyz"); $note->setuser(0); // you can add as much additional data to notes & events as you want $note->adddata("mytext", "text", "Some Text"); $note->adddata("myobject", "object", Object_Abstract::getById(7)); $note->adddata("mydocument", "document", Document::getById(18)); $note->adddata("myasset", "asset", Asset::getById(20)); $note->save(); And this is how the entry looks like: Placeholders Pimcore Placeholders are useful when you have a text (e.g. a wysiwyg editor) and you want to embed dynamic values within this text. Most of the time you will use Placeholders in combination with Documents and the Pimcore\Mail Class. The Syntax of a Placeholder is "%PLACEHOLDERNAME(PARAMETERKEY,JSON-CONFIG);" PLACEHOLDERNAME PARAMETERKEY JSON-CONFIG This is the last Part of the Placeholder class name E.g.: To use the Placeholder Pimcore\Placeholder\Object you would type %Object(key,params); This is the key of the parameter-array that you pass as second parameter to the "replaceplaceholders" method. You can pass a Json-Config to the Placeholder. (It depends on the Placeholder if the config is used)

205 Example usage Lets assume you have a text "Thank you for the order of "PRODUCTNAME"" and you want replace "PRODUCTNAME" with the real Product name. The following code-snippet replaces the Placeholder "%Object(...);" with the Product name. $text = 'Thank you for the order of "%Object(object_id,{"method" : "getname");"'; $placeholder = new \Pimcore\Placeholder(); $params = array('object_id' => 73613, 'locale' => 'de_de'); echo $replaced = $placeholder->replaceplaceholders($text,$params); More detailed: The replaceplaceholders() method accepts 3 parameter: First one Second Third The text that contains the placeholders which sould be replaced or a document (the document is rendered to HTML) The dynamic parameter for replacement A document. This parameter is usefull when you pass a plain text and you need values from a document in the placeholder object. In the placeholder object you can access the document with $this->getdocument() When you call the replaceplaceholders() method, it determines the placeholders and creates the corresponding placeholder object. Create your own Placeholders You can also create your own placeholder. By default it is assumed that your individual placeholders are located in /website/lib/website/placeholder/yourplaceholder.php. If you want to change the default placeholder location you can call "Pimcore\Placeholder::addPlaceholderClassPrefix(...)" to change the location (make sure you set them before any individual placeholder is called). E.g. "\Pimcore\Placeholder::addPlaceholderClassPrefix('Website\Tool\');" -> the placeholder location would be "/website/lib/website/tool/yourplaceholder.php" Now we assume that we want to create a simple Placeholder that replaces the data of a person. Therefore we create a new class that extends "Pimcore\Placeholder\AbstractPlaceholder". Our class has to implement at least 2 methods "gettestvalue()" and "getreplacement()". Note: The gettestvalue() is currently not in use but in upcoming releases it will be used for test replacements. The "getreplacement()" method has to return the value that should be used for the replacement of the placeholder. The "gettestvalue()" method should return a value for testing purpose.

206 <?php namespace Website\Placeholder; use Pimcore\Placeholder; class Person extends Placeholder\AbstractPlaceholder { public function gettestvalue() { $value = ''; switch ($this->getplaceholderkey()) { case 'firstname' : $value = 'Homer'; break; case 'lastname' : $value = 'Simpson'; break; //... return '<span class="testvalue">'. $value. ' </span>'; public function getreplacement() { $value = $this->getvalue(); //$document would contain the document that we passed as third parameter to the replaceplaceholders() method $document = $this->getdocument(); switch ($this->getplaceholderkey()) { case 'firstname' : return ucfirst($value); break; case 'lastname' : return ucfirst($value); break; case 'salutation' : if ($value == 'mr') { return 'Dear mr.'; elseif ($value == 'mrs') { return 'Dear mrs.'; break; Example usage:

207 public function ownplaceholderaction(){ $this->disableviewautorender(); $text = '%Person(salutation); %Person(firstName); %Person(lastName); thank you for your order.'; $placeholder = new \Pimcore\Placeholder(); $params = array('firstname' =>'Pim', 'lastname' => 'Core', 'salutation' => 'mr'); $replaced = $placeholder->replaceplaceholders($text,$params,document::getbyid(1)); echo $replaced; As all Placeholders must extend the class Pimcore\Placeholder\AbstractPlaceholder there are some getters you can use in your Placeholders. Method getplaceholderkey() getvalue() getplaceholderconfig() getparams() getparam($key) getplaceholderstring() getlocale() / getlanguage() getemptyvalue() Description Returns the key from the dynamic parameters. E.g. If you take a look at the "Create your own Placeholder" example. When the placeholder "%Person(salutation);" is processed the key would be "salutation" When the placeholder "%Person(firstName);" is processed the key would be "firstname"... Returns the value from the dynamic parameter. Returns the JSON-Config of the placeholder (if it was set), otherwise returns an empty JSON-Config object Returns the array that you passed to the replaceplaceholders() as second parameter. Returns a specific parameter form the array that you passed to the replaceplaceholders() as second parameter. Returns the placeholder string (raw text) Returns the current locale / language If the getreplacement() method returns an empty value the "getemptyvalue()" is called and the value which is returned by the "getemptyvalue()" method is used for the replacement. Object The Object Placeholder is used to replace values that are stored in a Object. key config allowed config required The id of the object Yes Yes Valid config parameter: Parameter callmethod locale Description The method of the object which sould be called (the call is localized) The Locale to use (optional) Example Usage:

208 public function objectplaceholderaction() { $this->disableviewautorender(); $text = 'Thank you for the order of "%Object(object_id,{"method" : "getname");"'; $placeholder = new \Pimcore\Placeholder(); $params = array('object_id' => 73613, 'locale' => 'de_de'); $replaced = $placeholder->replaceplaceholders($text, $params); echo $replaced; Text The Text Placeholder is used to replace the placeholder with the passed value. key value config available string string no Example Usage: public function textplaceholderaction(){ $this->disableviewautorender(); $text = 'Hello %Text(firstName); %Text(lastName);!'; $placeholder = new \Pimcore\Placeholder(); $params = array('firstname' => 'Bart', 'lastname' => 'Simpson'); $replaced = $placeholder->replaceplaceholders($text, $params); echo $replaced; //Will be: Hello Bart Simpson! Properties Every document can have custom properties. These can be managed from the tab "Properties" in the CMS for every document and snippet. The editor can add new properties themselves or select existing ones from a list of predefined properties. The property must have a unique name (per document) which can be used to access it through code as shown in the example below. Example Retrieving properties of a document: //retrieve the value of a property named "foobar" $value = $this->document->getproperty('foobar'); //retrieve an array with all properties for this document $list = $this->document->getproperties(); Predefined Properties

209 With predefined properties you can help/show the editors working within your pimcore installation which properties are available for their use. You can also define default values for each defined property to improve the productivity of your editors. "Predefined" does not mean that the value of the predefined property is available for every document. To add global properties which are available everywhere use Website Settings instead. Example Configuration How to configure Mandatory fields are: Name Key Type Content-Type Details to each field Name The name is not the key of the property itself (which you use in actions and templates), it's just the friendly name shown in the selection. Key This field is the key which you use in your code to retrieve the contents of the property. For example: $document->getproperty("key"); Type Specifies the type of the content which is allowed in the property. Available types are: text document asset

210 object bool (checkbox) select Value Here you can define a default value for this property which is added automatically to the property when it is added to a element. This field is optionally. See the example configuration above for details. Configuration This field is used to configure a property. Until now this is used only by the property-type "select". See the example configuration above for details. Content-Type Defines for which element-type (document, asset or object) the property should be available. (mandatory) Inheritable Inheritable or not. Static Helpers Pimcore offers some static helpers: Pimcore\Model\Document\Service::render() You can use this helper to render a page outside of a view, for example to send mails. Example: Note Each defined field can be overwritten in the element after it was added. $optionalparams = array('foo' => 'bar', 'hum'=>'bug'); $uselayout = true; $content = Document\Service::render(Document::getById(2), $optionalparams, $uselayout); echo $content; Advanced: If you are using this method in a batch job then you may want to clear parameters from the view upon, for example: foreach($users as $user) { $myoptions = array('recentlyvieweditems' => $user->getrecentviews()); $content = Document\Service::render(Document::getById(2), $myoptions, true); //clear only the $myoptions out of the view $viewhelper = \Zend_Controller_Action_HelperBroker::getExistingHelper("ViewRenderer"); if($viewhelper && $viewhelper->view!== null) { foreach ($myoptions as $key => $value) { if ($viewhelper->view->$key) unset($viewhelper->view->$key); //dosomethingwithcontent i.e. mail it System Settings

211 Debug Mode The Debug Mode is useful if you're developing a website / application with pimcore. With debug-mode on, errors and warnings are displayed directly in the browser, otherwise they are deactivated and the error-controller is active (Error Page). You can restrict the debug mode to an (or multiple) IP address(es), so that it is only active for requests from a specific remote address. If you are using Pimcore\Mail to send s and the Debug Mode is enabled, all s will be sent to the debug receivers defined in "Settings" -> "System" -> " Settings" -> "Debug addresses". In addition a debug information is attached to the which shows you to who the would be sent if the debug mode is disabled. To check anywhere in your own code if you are working in debug-mode, you can make use of the PIMCORE_DEBUG constant. DEV-Mode The development mode enables some debugging features. This is useful if you're developing on the core of pimcore or when creating a plugin. What exactly does the dev mode: Loading the source javascript files (uncompressed & commented) Disables some caches (Webservice Cache,...)... and some more little things EU Cookie Policy Notice You can specify your own texts and add your custom detail link using the "Shared Translations". Just search for "cookie-" in Shared Translations, then you get listed the predefined keys for the cookie texts and links: There is a convenience function which allows any pimcore system component or plugin to use a preconfigured Zend_Mail instance based on the pimcore system settings' conifguration. //returns Zend_Mail $mail = Pimcore\Tool::getMail($recipients,$subject); For any plugin or website applications it might be convenient to use this mail configuration instead of having to care for these settings themselves. Tag & Snippet Management The "Tag & Snippet Management" helps you to keep track of your 3rd party code snippets on your website (conversion codes, tracking codes,...). The functionality is very similar to other tag managers out there (eg. gmanager.html, and many others). The main advantage of the build-in tag manager is that you can insert the code anywhere you want on the website and you don't have to

212 care about how the code works in detail (synchronous, async,...). You can even insert more than one code at once, this is very useful if you have scripts which need code in the <head> section and at the end of the <body> section. Use cases: Tracking Codes (Google Adwords Conversion Code, Clicktale, Crazy Egg,...) 3rd Party Content (Facebook Widgets, Twitter Widgets,...) and for anything else which shouldn't be directly in the code/views ;-) How does it work It's easy ;-) Just specify your conditions and add one or more "tags" (code snippets). The code snippets will be put into the HTML code when all conditions met. Example: Targeting & Personalization How it works Pimcore s on-site behavioral targeting engine is using an innovative technological approach which makes it possible that no personal data is transferred from the visitor to the server and that no visitor s private data is stored on the server. All the rules and actions added in the backend are evaluated and ran in frontend by visitor s browser (Javascript). As everything happens on visitor s side, no personal data is sent to the server. If explicitly needed we can access the matching Persona in the backend, but not directly visitor's data. The javascript needed for the targeting functionality is automatically injected to the frontend HTML by a Front Controller Plugin, when the targeting functionality is in use. Additionally all rules and configs off all personas are also injected into <head>, so browser can evaluate and execute them in the frontend. Pimcore s targeting system needs to log some of the user s activity (clicked links, matching rules, events and page views) in order to make all of its functionality possible. All the tracked data is saved only on the client side (HTML5 Local storage with key pimcore_targeting_user) without using cookies! GET parameters: After the rules are evaluated, Pimcore redirects to the same URL with any of these three GET params added to identify further actions in the backend or frontend (values of params are one or more comma delimited IDs of Personas). Eg.

213 _ptr redirect action (GET) When this parameter is available, frontend JS will trigger redirect to the URL set in the backend _ptc programmatically redirect action (GET) This parameter is set when Programatically action is enabled. Developer can respond to this parameter either in the backend (PHP) or in the frontend (Javascript) _ptp persona variant of document page (GET) This parameter loads the personalized version of the document Content Managing Global Targeting Rules and Personas (Target Groups) Conditions Actions Document personalization Assign Personas based on visited pages Interacting with the targeting & personalization engine Targeting: Tips for developers Managing Global Targeting Rules and Personas (Target Groups) Global targeting rules global targeting rule s conditions are evaluated on every visit - actions are called based on scope of rule 15 conditions can be evaluated: Conditions rules tie together conditions, personas and actions global targeting rule can assign a Persona to a visitor scope Hit Actions are called on every request when conditions are met Session Actions are called only once per session. New session is started if more than 30 minutes has passed since the last action User Actions are called only once per user when conditions are first met Target groups (personas) entry conditions are checked only at the first visit of the user

214 has a subset of global targeting rule s conditions document content can be overridden based on persona personas can be assigned to visitors in following ways visitor passes persona s conditions document page assigns a persona to a visitor when visited: document page > settings > associate target group (personas) global targeting rule assigns persona PHP script assigns persona to see assigned personas put this into your developer tools console: pimcore["targeting"]["user"]["personas"] threshold - used for getting the primary persona and to specify which personified variant of the page to display. If the number of times a persona is assigned is below the threshold the persona will not be used as primary Primary persona A persona can be assigned to a visitor multiple times. The amount of assignations and Persona's threshold determine which persona is the Primary persona. This persona also defines which personalized variant of document to display. Persona can be Primary only if the number of assignations is equal or bigger than the threshold of the persona. Example: visitor visits three sites that assign a persona VisitorOfLaptopProducts. This persona has a threshold of 3 set. On 4th visit this will become the primary persona and we can display in sidebar additional deals on laptop products as this will now be the most relevant data to display. The primary persona can be retrieved in frontend by: pimcore.targeting.user.persona Please note: The persona s conditions are evaluated and assigned only once per session. To assign a persona multiple times, use Global Targeting Rules or assign them programatically. Conditions Combining conditions Pimcore makes it possible to create very complex conditions with little effort. You can combine conditions with basic boolean operators: AND, OR & AND NOT. Additionally you can combine subconditions with parentheses. URL (RegExp) - only in Global Targeting Rules Regex of URLs to match. (use single backslash to escape question mark if needed \?) Browser

215 Country Language Event - only in Global Targeting Rules Check if event exists in activity log. Events are added by Rule's Actions. Geographic point You can enter coordinates manually or use the built-in map search:

216 Referring Site Regex of referrer's URL. (use single backslash to escape question mark if needed \?) Search Engine Checks if visitor came to site from Search Engine - Google, Bing, Yahoo!. This condition is similar to Referring Site condition, so you can check for other search engines by using the Referring Site condition. Visited page before - only in Global Targeting Rules Checks if user visited certain page before - Regex of the page URL. (use single backslash to escape question mark if needed \?) Visited n-pages before - only in Global Targeting Rules Condition is true if user visited at least the number (set in condition) of pages before. Time on site - only in Global Targeting Rules Condition is true if user is on the site for at least the duration set. Link clicked - only in Global Targeting Rules

217 Checks if user clicked on a certain link before - Regex of the link's href attribute. (use single backslash to escape question mark if needed \?) Pimcore's targeting system adds event listener to all links on the page, so you can add unique IDs to href attribute of links to recognize their origin. Number of links clicked - only in Global Targeting Rules Condition is true if user clicked at least the number of links set in the condition. Hardware Platform Hardware detection is based on the User Agent. Operating System Operating system check is based on the User Agent. Personas - only in Global Targeting Rules Checks if user has the Persona assigned. Actions Redirect

218 Redirects to document (drag and drop) or any other URL. Programmatically Enables developers to handle this action in code. Interacting with the targeting & personalization engine Note: This option is turned on just by expanding this section. Event Event key and value are saved to user's (local storage) activity log. Another Global Targeting Rule can then respond to this rule in combination with any of the other conditions. Code-Snippet Injects any code to frontend HTML. Anything that can be interpreted in the browser can be injected - JavaScript, CSS, HTML. Element - body, head or any other element (use CSS selector) Insert position - inject the code in the beginning, the end of the element or completely replace element's contents. Associate Target Group (Personas) Assigns the selected Persona to the visitor.

219 Document personalization It is extremely easy to create per-persona customized document variants by overriding only the elements you need to change. All other elements are inherited from base document. 1. Choose a Persona 2. Right click on the content block you want to override and click Override 3. Personalize the content. You need to edit only overridden blocks, all the other content will be inherited from the general document. Preview personalized variants of document You can view different variants of document in the frontend by using Pimcore widget - button on the bottom right. Note: The personified document will not display if Persona's threshold is greater than 1.

220 In above case the browser is redirected to (10 being the id of Persona "visitornewyork"). Assign Personas based on visited pages We can assign Personas based on the document pages user visits. This allows for advanced content personalization. Use case example: user visits page with Books product listing - booksinterest persona is assigned user visits page with Sport items product listing - sportinterest persona is assigned based on these two personas we can display product suggestions from category Books about sport by creating a rule that has both personas set as condition Persona is assigned each time user visits the page.

221 Interacting with the targeting & personalization engine PHP/Server Expand Programatically section in Rule Actions to turn it ON. After javascript evaluates condition it will refresh and add _ptc GET param with IDs of matching targets. Get ID of Persona in controller $targetingrulesids = json_decode("[". $this->getparam("_ptc"). "]", true); Assign Persona in controller $personaid = 10; $frontcontroller = \Zend_Controller_Front::getInstance(); Pimcore\Controller\Plugin\Targeting $targetingplugin */ if (!$this->view->editmode &!\Pimcore\Tool::isFrontentRequestByAdmin()) { $targetingplugin = $frontcontroller->getplugin("pimcore\\controller\\plugin\\targeting"); $targetingplugin->addpersona($personaid); Javascript/Browser Expand Programatically section in Rule Actions to turn it ON. After javascript evaluates condition it will refresh and add _ptc GET param with IDs of matching targets to the request. Get ID of Persona in frontend var targetingrulesids = window.location.search.match(/(\? &)_ptc\=([^&]*)/)[2].split(","); Targeting: Tips for developers See user's activity log

222 Examine targeting data Pimcore logs targeting actions to console. If you want to further examine targeting data enter this: pimcore.targeting Examine Targeting local storage

223 UUID Support Pimcore provides a toolkit for UUID-support. To activate the UUID-support, an instance identifier has to be set in Settings -> System -> General: Once set, pimcore automatically creates an UUID for each newly created document, asset and object. With the class Tool_UUID you have access to the UUIDs as follows: use Pimcore\Model\Tool; //get UUID for given element (document, asset, object) $uuid = Tool\UUID::getByItem($document); //get element for given UUID $document = Tool\UUID::getByUuid($uuid); //create and save uuid for given element $uuid = Tool\UUID::create($document); Versioning pimcore can version all your content (documents, assets and objects). You can have as much versions as you want. On each change a new version of the element is created. Settings

224 You can configure the versioning behavior in the system settings (Settings -> System->[Documents Assets Objects]) Turn off versioning for the current process Sometimes it is very useful to just deactivate versioning for a process. For example for importers or synchronization with 3rd party systems. You can globally deactivate and activate versioning with the following PHP code directly in your scripts: \Pimcore\Model\Version::disable(); // to disable versioning for the current process \Pimcore\Model\Version::enable(); // to enable Note: With these commands you only deactivate/activate the versioning for the current PHP process. This setting is not saved, and only affects changes on elements which are modified within this process! Website Settings The "Website Settings" gives you the possibility to configure website-specific settings, which you can access in every controller and view. Examples ReCAPTCHA public & private key Locale settings Google Maps API key Defaults... Access the Settings You can access the settings in every controller and view with $this->config. This variable contains a Zend_Config object containing your settings. If you're not in a view or controller you can use Pimcore\Tool\Frontend::getWebsiteConfig() ; to retrieve the configuration. Example configuration Example in a view:

225 <script type="text/javascript" src=" maps%22%2c%22version%22%3a%222%22%7d%5d%7d&key=<?= $this->config->googlemapskey?>"></script> Example in a controller: public function testaction () { $recaptchakeypublic = $this->config->recaptchapublic; Note Pimcore caches Website Settings, it is therefore a good idea to clear the cache after you have finished adding / editing values. Working with Sites (Multisite) You can create subsites in pimcore very easily directly in the context menu: That's basically all, everything else is done in your code. Code Snippets Check if current request is inside a subsite if(\pimcore\model\site::issiterequest()) { /*... */ Working with the navigation helper See Navigation Getting the full path of a document inside a subsite-request

226 $document->getrealfullpath(); // returns the path including the site-root $document->getfullpath(); // returns the path relative to the site-root Getting the root-document of the current site if(\pimcore\model\site::issiterequest()) { $site = \Pimcore\Model\Site::getCurrentSite(); $navstartnode = $site->getrootdocument(); else { $navstartnode = \Pimcore\Model\Document::getById(1); Adaptive Design / Device Helper / Tool The DeviceDetector helper makes it easy to implement the adaptive design approach in pimcore. Device view helper Please read more here. Using it anywhere in your code class TestController extends Action { public function testaction () { $device = \Pimcore\Tool\DeviceDetector::getInstance(); $device->getdevice(); // returns "phone", "tablet" or "desktop" if($device->isdesktop() $device->istablet()) { // do something Force a device type Sometimes it's necessary to force a device type. A typical use case is a "Back to Desktop Version" or vice versa link. To do so, just add the parameter forcedevicetype to your request: /your/link?forcedevicetype=desktop /another/link?forcedevicetype=tablet /a/mobile/link?forcedevicetype=mobile This will set the device to the specified value and pimcore will remember this setting using a cookie (name: forcedevicetype) till the browser session ends. Caching The pimcore output-cache is aware of this feature and just works as expected. If you're using a caching proxy like Varnish you have to take the value of the cookie forcedevicetype into the hash calculation, otherwise there's just one hash for different contents of an URL (phone, tablet, desktop). Application Logger

227 Application Logger (since build 3507) The application logger is a tool developers can use to log certain events and errors within a pimcore powered application. The logs are visible and searchable within the Pimcore backend GUI (Extras => Application Logger): How to create log entries The application logger extends the Zend_Log component and therefore it can be used the same way: Example usage (simple) $logger = new \Pimcore\Log\Log(); $logger->setcomponent("pimcore logger"); $logger->addwriter(new \Pimcore\Log\Writer\Db()); $logger->info('test message'); The \Pimcore\Log\Writer\Db log writer will write the log entries into the database (and therefore the entries will be visible in the GUI mentioned above). You can combine it with other log writers from the Zend_Framework (for example to output it directly into the browser or shell). Take a look at the Zend documentation for more details. Example usage (advanced) $logger = new \Pimcore\Log\Log(); $logger->setcomponent("pimcore logger"); $logger->addwriter(new \Pimcore\Log\Writer\Db(\Zend_Log::INFO)); //additional log writer $logger->addwriter(new \Zend_Log_Writer_Stream('php://output')); // add some additional info as file to the log entry $fileobject = new \Pimcore\Log\FileObject($someInterestingAddtionalData); // add a related object to the log entry (assets + documents are possible too) $relatedobject = \Pimcore\Model\Object\AbstractObject::getById(50833); $logger->info('test message', $relatedobject, $fileobject); Configuration There are some options in the system settings to configure the application logger (within the "Debug" panel):

228 When the "Send log summary per mail" checkbox is activated the defined receivers will receive log entries by mail. The priority can be used to setup which log messages will be contained in the mail. For example errors are more important than just info entries. The archive function automatically creates new database tables to archive the log entries in the form application_logs_archive_*. In the above example log entries will be moved after 30 days to these archive tables. Optionally a different database name for the archive tables can be defined. Reference: Database Structure, Fields, Relations - "How are objects stored?" This page tries to shed some light on the inner workings of Pimcore - especially how data is organized and stored in the database. Objects As soon as a new object class is created in Pimcore, at least three tables are added to the database. The tables have a numerical suffix, denoting the number (id) of the object class. object_query_( id ), object_relations_( id ), object_store_( id ) and an additional database view which is a combination of these. All object class entries - the objects- result in an entry in the objects table. This is the source of the object id and stores general metadata such as the path. object_(id) Database view joining object_store_(id) and objects table View object_query_( id) Table object_relations_( id) Use this table to retrieve data incl. inherited versions. Datentypes with relations are usually stored in a serialized form here, too. Pimcore Object-Lists work with this table. Contains data of fields with relations to objects, assets, etc. Table object_store_( id) Table objects Table This is the main data storage table of an object class. It contains all "flat" data without any relations or external dependencies. Contains an entry for each and every object in the system. The id field is an auto_increment and the source of the primary key for an object. Metadata about an object is stored in this table, too. Simple datafield types Text Table: object_store_(id) Name Datatype Default Comment Input varchar(255) NULL / Textarea longtext NULL / wysiwyg longtext NULL Text with HTML-tags password varchar(255) NULL Passwort - as hash Number

229 Table: object_store_(id) Name Datatype Default Comment Number double/decimal(64,3) NULL Datatype depends on selected precision Slider double NULL / Date Table: object_store_(id) Name Datatype Default Comment Date bigint(20) NULL < 1970 = negative Timestamp Date & Time bigint(20) NULL < 1970 = negative Timestamp Time varchar(5) NULL String - e.g.: "12:00" Select Table: object_store_( id) Name Datatype Default Comment Select varchar(255) NULL Selected value User varchar(255) NULL Pimcore User-ID Country varchar(255) NULL Country code Language varchar(255) NULL Language code Multiselection text NULL String, selected values, separated by "," Countries (Multiselect) text NULL String, selected language-codes, separated by "," Languages (Multiselect) text NULL String, selected language-codes, separated by "," Relations Table: object_relations_( id) & object_meta_data_( id) Structured Datefields of the type Relations are stored in extra tables Datafields are not stored in distinct columns, but the fieldname is in denoted in an extra column fieldname The column 'type' specifies the type of the linked ressource (Object, Document, Asset) The columns src_id and dest_id define the relation / the link between the objects. Column index is used to specify the order of the relations The filedtype 'Objects With Metadata stores the extra data in a table object_meta_data_(id) - the column column specifies the name of the metaitem and data stores the value Name Datatype Default Comment Table Tabledata is stored as a string - serialized.

230 Structured Table Each table cell is stored distinctively; schema: (fieldnam e) (row key)#(column key) Field-Collections Objectbricks see: Special datafields Localized Fields see: Special datafields Key/Value varchar(255) NULL Geographic Table: object_store_( id) Name Datatype Default Comment Geographic Point double NULL Creates two columns: (name) longitude and (name) latitu de Geographic Bounds double NULL Creates four columns: (name) NElongitude, (name) NElati tude, (name) SWlongitude u nd (name) SWlatitude Geographic Polygon longtext NULL Serialized geo-data Other Name Datatype Default Comment Image int(11) NULL ID of the image asset Image Advanced int(11), text NULL Creates a cloumn (name) im age (int) for the image assets id and the Column (name) ho tspots (text). Hotspots are stored serialized. Video text NULL Serialized data Checkbox tinyint(1) NULL Boolean value (1 = true) Link text NULL Serialized data CRM Name Datatype Default Comment Firstname varchar(255) NULL Lastname varchar(255) NULL varchar(255) NULL Gender varchar(255) NULL male/female/unknown/(empty) Newsletter Active tinyint(1) NULL Boolean value (1 = true) Newsletter Confirmed tinyint(1) NULL Boolean value (1 = true) Persona varchar(255) NULL Persona Multiselect text NULL Comma separated list Special data fields Objectbricks

231 Table/View Purpose object_brick_query_( id) Table object_brick_store_(id Main data storage Table Localized fields Table/View object_localized_( id)_(language-code) View object_localized_data_( id) Purpose A database view per language, combining reguar and localized data fields Stores localized field data Tabelle object_localized_query_( id)_( language-code) Access to inherited data Tabelle Fieldcollections Table/View object_collection_(collection-name)_(object-id) Purpose Stores data of the field collections fields and the order (index) Overview: Default Pimcore database tables Table assets assets_metadata assets_metadata_predefined cache cache_tags classes content_analysis content_index custom_layouts dependencies documents documents_doctypes documents_elements documents_ documents_hardlink documents_link Description Assets (Images, etc.), with system metadata Additional user metadata (Metadata tab in the asset panel) Templates for asset metadata serialized data, used by the default Pimcore cache Tag store for cache entries. Used by some cache backends List of all object classes with metadata, but not the actual configuration / class definition SEO-Data / Socialmedia analysis (Marketingpanel in Pimcore) Index für page analytics Definition of the custom layouts for object classes Stores dependencies between ressourcen such as objects, assets, documents List of all documents, folders, links, hardlinks, s and snippets of the document area with meta- and config-data, relations Predefined document types Editables of documents (data), in a serialized form Extra config data Extra config data Extra config data

232 documents_page documents_snippet edit_lock _blacklist _log glossary http_error_log Extra config data Extra config data Tracks which user opened which ressource in the backend Blacklist for -addresses Log for s Words to auto-link in texts. See: ay/pimcore/glossary HTTP error log keyvalue_groups keyvalue_keys keyvalue_translator_configuration locks notes notes_data objects properties properties_predefined recyclebin Liste of all objects with metadata Data from the "properties" tab Definitions of predefined properties Stores deleted ressources redirects sanitycheck schedule_tasks search_backend_data sites staticroutes targeting_personas targeting_rules Stores the Index for the backend search - is a MyISAM Table with fulltext capabilities Configuration: Multisite-Setups Configuration: Static Routes Personas for targeting, see: geting-personalization Rules for targeting, see: ng-personalization tmp_store translations_admin translations_website Backend translations Frontend translations tree_locks users users_permission_definitions users_workspaces_asset users_workspaces_document users_workspaces_object uuids versions website_settings Backend users List of globally assignable user permissions Stores user access permissions for asset folder Stores user access permissions for document folders Stores user access permissions for object folders stores Unique Identifiers - if enabled Liste of object/asset/document versions. Actual data is serialized and written to disk Stores Website Settings Console / CLI

233 Pimcore integrates the Symfony\Console component and provides pimcore/cli/console.php as single entry point to console commands registered to the Symfony\Console application. You can add custom commands either by hooking into an event or by placing your commands in predefined locations. Usage Call the console.php script from the command line to get a list of available commands. To call a command, use console.php <subcommand>. Examples: # get a list of all registered commands $ php pimcore/cli/console.php # or $ php pimcore/cli/console.php list # call the foo:bar command $ php pimcore/cli/console.php foo:bar Implementing own commands Have a look at the Symfony\Console documentation for details how commands are implemented. However, it makes sense to let your command classes extend Pimcore\Console\AbstractCommand to get some defaults like the--ignore-maintenance-mode option and a helper for the Symfony VarDumper Component set up automatically (see below). Registering Commands There are currently 2 methods to add plugins. Either you put your commands into a predefined location where commands are autoloaded from or you hook into the initialization process and add your commands manually. Autoloaded commands The console application tries to autoload commands from a list of given namespaces. Currently the following namespaces are taken into consideration (more are likely to follow): Namespace Pimcore\Console\Command Website\Console\Command Directory /pimcore/lib/pimcore/console/command /website/lib/website/console/command To have your command autoloaded, it must match a couple of prerequisites: 1. It must be placed in one of the namespaces listed above (e.g. Website\Console\Command\AwesomeCommand in /website/li b/website/console/command/awesomecommand.php) 2. The class name must end in Command, e.g. AwesomeCommand 3. The class must inherit Symfony\Component\Console\Command\Command, ideally you achieve this by extending Pimcore\Con sole\abstractcommand Manually registered commands Upon initialization the console application emits the system.console.init event, which you can use to hook into the initialization process and to add your commands. Again, there are 2 ways to do this: Using the ConsoleCommandPluginTrait to add a list of commands Create a plugin and let your plugin class extend Pimcore\Console\AbstractConsoleCommandPlugin (or use the Pimcore\C onsole\consolecommandplugintrait and call the initconsolecommands() method yourself in init() ). Implement the getconsolecommands() method returning an array of commands to register. Handle the system.console.init event manually to have more control See the trait mentioned above for an example on how to handle the event. You'll get the Pimcore\Console\Application object passed as event target and can use its API to add commands. An example:

234 <?php \Pimcore::getEventManager()->attach('system.console.init', function(\zend_eventmanager_event $e) { \Pimcore\Console\Application $application */ $application = $e->gettarget(); // add a namespace to autoload commands from $application->addautoloadnamespace('consoledemoplugin\\console', PIMCORE_DOCUMENT_ROOT. '/plugins/consoledemoplugin/lib/consoledemoplugin/console'); ); // add a single command $application->add(new My\Custom\Namespace\AwesomeCommand()); Helpers provided by Pimcore\Console\AbstractConsoleCommand The AbstractConsoleCommand base class provides helpers which make your life easier. --ignore-maintenance-mode The console application implicitly adds the --ignore-maintenance-mode option found in other scripts. AbstractConsoleCommand che cks for the option and aborts the command if the system is in maintenance mode and the option is not set. dump() and dumpverbose() Better var_dump through VarDumper. Usage: <?php namespace Pimcore\Console\Command; use Pimcore\Console\AbstractCommand; use Pimcore\Console\Dumper; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; class TestCoreCommand extends AbstractCommand { protected function configure() { $this ->setname('awesome:command') ->setdescription('awesome command'); protected function execute(inputinterface $input, OutputInterface $output) { // dump $this->dump($this); // add newlines through flags $this->dump($this, Dumper::NEWLINE_BEFORE Dumper::NEWLINE_AFTER); // only dump in verbose mode $this->dumpverbose($this, Dumper::NEWLINE_BEFORE); writeerror()

235 Shortcut to write an error. Usage: <?php $this->writeerror('oh noes!'); The call above will output ERROR: oh noes! as white text on red background. Web Services Pimcore provides a web service to retrieve and save objects, documents and assets. When the web service feature is enabled in the system settings (by default it is disabled), any admin user can access and utilize the API. For other users the workspace settings apply in the same way as they do in the admin interface. REST Webservice SOAP (discontinued) REST Webservice Permissions Available Calls Get Object By ID Delete Object By ID Create a new object Update existing object Check exististence of Object Get Object Metadata Get List of Classes Get Class By ID Get List of Field Collections Get Field Collection Definition By Key Get List of Field Object Bricks Get Object Brick Definition By Key Get List of Image Thumbnail Configurations Get Image Thumbnail Configuration By Key Get Asset By ID Delete Asset By ID Create New Asset Update Existing Asset Check exististence of Asset Get Document By ID Delete Document By ID Create New Document Update Existing Document Check exististence of Document Search Assets Search Documents Search Objects Get Asset Count Get Document Count Get Object Count Get User Get KeyValue Definition Get Server Info Get Server Time Override HTTP Method Translations Pimcore provides a web service to retrieve and save objects, documents and assets through a RESTful API. When the web service feature is enabled in the system settings (by default it is disabled), any admin user can access and utilize the REST API.

236 When the web service API is enabled, for each admin user his API key is displayed in Settings > Users. Please be aware that the API Key changes when the user changes his/her password. For testing in the browser it's not necessary to add the apikey if you have a valid user session from the admin interface (session authentication). Permissions Unrestricted access is only granted to admin users. For all other users the following restrictions are enforced: Classes permission for the following calls: Classes list Object bricks list Field Collections list Class Definition Object Brick Definition Field collection Definition Key Value Definition Object metadata Asset permission: Asset List Asset Count Document permission Document List Document Count Object permission Object List Object Count Systen Settings Permission Get Server Info Workspace View permission Get Asset Document Object Workspace Delete permission Delete Asset Document Object Workspace Create permission Create Asset Document Object Workspace Save permission Publish Asset Document Object Available Calls The following methods are available for web service calls: Get Object By ID Method: GET URL: Returns: JSON--encoded object data. Delete Object By ID Method: DELETE URL: Returns: JSON-encoded success value Create a new object Method: PUT or POST URL: Request body: JSON-encoded object data in the same format as returned by get object by id for the data segment but with missing id field or id set to 0 Returns: JSON-encoded object id Update existing object Method: PUT or POST URL: Request body: Same as for create object but with object id Returns: JSON-encoded success value Check exististence of Object

237 Method: GET or POST URL for GET: URL for POST Example: { Request body for POST:: comma-seperated list of object ids ids Parameter for GET: comma-seperated list of object ids Returns: JSON-encoded success value and list of object ids and flag indicating whether object exists or not. If optional condense parameter is set to true then only non-existing object ids are returned. "success": true, "data": Unknown macro: {** "4500" Get Object Metadata Method: GET URL: Get List of Classes Method: GET URL: Returns: The JSON-encoded list of classes Get Class By ID Method: GET URL: Returns: The JSON-encoded class definition for the given class Get List of Field Collections Method: GET URL: Returns: The JSON-encoded list of field collection configurations Get Field Collection Definition By Key Method: GET URL: Returns: The JSON-encoded field collection definition for the collection with the given key Get List of Field Object Bricks Method: GET URL: Returns: The JSON-encoded list of object brick configurations Get Object Brick Definition By Key Method: GET URL: Returns: The JSON-encoded object brick definition for the object brick with the given key Get List of Image Thumbnail Configurations Method: GET URL: Returns: The JSON-encoded list of image thumbnail configurations Get Image Thumbnail Configuration By Key

238 Method: GET URL: Returns: The JSON-encoded image thumbnail configuration with the given key Get Asset By ID Method: GET URL: Optional Parameter: light if set to true, the response will not contain the data (base64) of the asset. Returns: JSON-encoded asset data. Delete Asset By ID Method: DELETE URL: Returns: JSON-encoded success value Create New Asset Method: PUT or POST URL: Request body: JSON-encoded asset data in the same format as returned by get asset by id for the data segment but with missing id field or id set to 0 Returns: JSON-encoded asset id Update Existing Asset Method: PUT or POST URL: Request body: Same as for create asset but with asset id Returns: JSON-encoded success value Check exististence of Asset Method: GET or POST URL for GET: URL for POST Request body for POST:: comma-seperated list of asset ids ids Parameter for GET: comma-seperated list of asset ids Returns: JSON-encoded success value and list of asset ids and flag indicating whether asset exists or not. If optional condense parameter is set to true then only non-existing asset ids are returned. Get Document By ID Method: GET URL: Returns: JSON-encoded document data. Delete Document By ID Method: DELETE URL: Returns: JSON-encoded success value Create New Document Method: PUT or POST URL: [ Request body: JSON-encoded document data in the same format as returned by get document by id for the data segment but with missing id field or id set to 0 Returns JSON-encoded document id Update Existing Document

239 Method: PUT or POST URL: Request body: Same as for create document but with object id Returns: JSON-encoded success value Check exististence of Document Method: GET or POST URL for GET: URL for POST Request body for POST:: comma-seperated list of document ids ids Parameter for GET: comma-seperated list of document ids Returns: JSON-encoded success value and list of document ids and flag indicating whether document exists or not. If optional condense parameter is set to true then only non-existing documentids are returned. Search Assets Method: GET URL: ndition=type%3d%27folder%27 Returns: A list of asset id/type pairs matching the given criteria. Parameters: condition: where clause order: sort order (if supplied then also the key must be provided) orderkey: sort order key offset: search offset limit: result limit groupby: group by key Search Documents Method: GET URL: &condition=type%3d%27folder%27 Returns: A list of document id/type pairs matching the given criteria. Parameters: condition: where clause order: sort order (if supplied then also the key must be provided) orderkey: sort order key offset: search offset limit: result limit groupby: group by key Search Objects Method: GET URL: jectclass=myclassname&condition=o_type%3d%27folder%27 Returns: A list of object id/type pairs matching the given criteria. Parameters: condition: where clause order: sort order (if supplied then also the key must be provided) orderkey: sort order key offset: search offset limit: result limit groupby: group by key objectclass: the name of the object class (without "Object\"). Note: If the class does not exist the filter criteria will be ignored! Get Asset Count Method: GET URL: Returns: The total number of assets matching the given criteria. Parameters: condition: where clause

240 Get Document Count groupby: group by key Method: GET URL: Returns: The total number of documents matching the given criteria. Parameters: condition: where clause groupby: group by key Get Object Count Get User Method: GET URL: Returns: The total number of objects matching the given criteria. Parameters: condition: where clause groupby: group by key objectclass: the name of the object class (without "Object\"). Note: If the class does not exist the filter criteria will be ignored! Method: GET URL: Returns: The JSON-encoded user data for the current user Get KeyValue Definition Method: GET URL: Returns: The JSON-encoded Key/Value definition Parameters: condition: where clause Get Server Info Method: GET URL: Returns: The JSON encoded server-info including pimcore version, current time and extension data. Get Server Time Method: GET URL: Returns: The JSON encoded system time. Override HTTP Method The HTTP request method can be overriden by providing a method parameter. Method: any URL: Translations Method: GET URL: Returns: List of translations. Parameters: type: "website" or "admin" (required) key: tranlation key matches param creationdatefrom: timestamp creationdatetill: timstamp

241 Localization Localization within pimcore is very easy, because pimcore follows 100% the ZF patterns. Setting up the languages First of all you have to define the languages which should be available in your pimcore installation, you can configure that in Settings -> System -> Website: Additionally for each language a fallback language can be set. This languages will be available in various modules within pimcore like : Document - Localization (system property for language) Localized Fields for Objects (object localization) Translations (Zend_Translate)... How pimcore deals with locales pimcore offers localization for documents, if you don't want to use that, you can also register the locale manually anywhere in your code. You can do that simply how you would do that in every other ZF application: $locale = new \Zend_Locale("en_US"); \Zend_Registry::set("Zend_Locale",$locale); Now every pimcore and ZF module will respect your registered locale. Outputfilters LESS (CSS Compiler) Install lessc on your server (Debian) LESS (CSS Compiler) pimcore is able to compile your.less files to common css. You can use 2 different less compiler implentations, the default compiler is a lessc port in PHP which doesn't cover all LESS functionality, and the official LESS compiler (lessc). The latter you have to install on your server and tell pimcore the path to the executable. ( read more here) Please always use the full path to the executable eg. /usr/local/bin/lessc and not just lessc. The implementation is very easy, just enable the LESS outputfilter in the system settings, and remove your less.js javascript out of your template. In editmode pimcore will add the less.js to your HTML header, in the frontend the less is compiled to css on the serverside. To use LESS with a css-file, you have to use rel="stylesheet/less" in your link-tag, e.g.:

242 <link href="/static/css/example.css" media="screen" rel="stylesheet/less" type="text/css" > Install lessc on your server (Debian)

243 # get root su root cd ~ # Download latest version of node.js # please replace the url with the newest available version, which you can find here wget # extract node.js install package tar xfvz node-v0.4.7.tar.gz # change working directory cd node-v0.4.7 # configure./configure # compile it make -j4 # install it make install # go back to home cd ~ # download less wget --no-check-certificate # extract tar xfvz cloudhead-less.js-v g853604a.tar.gz mv cloudhead-less.js a lesscss mv lesscss/ /usr/local/ cd /usr/local/bin/ ln -s../lesscss/bin/lessc /******************** NOTES *****************/ When you run "./configure" you might get an error that no g++,c++... compiler is available. You can run "apt-get install build-essential" to solve this. If you get an error that no openssl is not available you can either configure it without them (--without-ssl) or you can install libssl by running: apt-get install libssl-dev

244 Mailing Framework General information The Pimcore Mailing Framework provides an easy way to send/create s with Pimcore. Therefore you have several components: Document\ Pimcore\Mail Placeholder Objects Pimcore provides a "Pimcore\Mail" Class which extends the "Zend_Mail" Class. When you initialize a Pimcore\Mail object, all data from "Settings" -> "System" -> " Settings" are applied automatically. When you enable the debug mode in Settings" -> "System" -> "Debug", all s will be sent to the addresses given in "Settings" -> "System" -> " Settings" -> "Debug Addresses" and the debug information (to whom the would have been sent) is automatically appended. Pimcore provides a "Document " type where you can define the reciptients... (more information here) and "Dynamic Placeholders" (more information here) To send a you just create a " Document" in the Pimcore admin, define the subject, recipients, add Dynamic Placeholders... and pass this document to the Pimcore\Mail object. All nasty stuff (creating valid urls, emedding css, compile less files, rendering the document..) ist automatically handled by the Pimcore\Mail Object. Usage example *Lets assume that we have created a " Document" in the Pimcore admin (/ /my document) which looks like this: To send this document as we just have to write the following code-snippet in our ControllerAction: //dynamic parameters $params = array('firstname' => 'Pim', 'lastname' => 'Core', 'product' => 73613); //sending the $mail = new \Pimcore\Mail(); $mail->addto('[email protected]'); $mail->setdocument('/ /my document'); $mail->setparams($params); $mail->send(); Sending a plain text

245 $mail = new \Pimcore\Mail(); $mail->addto('[email protected]'); $mail->setbodytext("this is just plain text"); $mail->send(); Sending a rich text (HTML) $mail = new \Pimcore\Mail(); $mail->addto('[email protected]'); $mail->addbcc("[email protected]"); $mail->setbodyhtml("<b>some</b> rich text"); $mail->send(); Pimcore\Mail Class The Pimcore\Mail Class extend the Zend_Mail Class and adds some features for the usage with pimcore. When you create a new Pimcore\Mail instance the settings from "Settings" -> "System" -> " Settings" are automatically applied. If the Debug Mode in "Settings" -> "System" -> "Debug" is enabled, all s will be sent to the Debug recipients defined in "Settings" -> "System" -> " Settings" -> "Debug Addresses". Additionaly the debug information (to whom the would have been sent) is appended to the and the Subject contains the prefix "Debug ". The Pimcore\Mail Class automatically takes care of the nasty stuff (emedding css, compiling less files, normalizing urls, replacement of Dynamic Placeholders...). Note that all css files are embedded to the html with a "style" tag because the image paths are also normalised. A text version from the html will automatically be created by Pimcore\Mail. Optionally, you can use "html2text" from Martin Bayer ( for the generation of the text version by calling enablehtml2textbinary() - in some cases, this produces better results for your text mail than the default (php) library. On Debian/Ubuntu you can install it with: apt-get install html2text Useful methods: Method disablelogging() setparams(array) setparam($key, $value) isvalid address( address) setdocument(document_ ) getdocument() getsubjectrendered() getbodyhtmlrendered() getbodytextrendered() enablehtml2textbinary() sethtml2textoptions($options) Description Disables logging - by default it is enabled Sets the parameters for the view and the Placeholders Sets a single parameter for the view and the Placeholders Static helper to validate a address Sets the document Returns the Document Replaces the placeholders with the content and returns the rendered Subject Replaces the placeholders with the content and returns the rendered Html * Replaces the placeholders with the content and returns the rendered text if a text was set with "$mail->setbodytext()" * if no text was set, a text version on the html will be automatically created "html2text" from Martin Bayer ( ex.shtml) throws an Excpetion if html2text is not installed! set options for html2text (only for binary version) Class Location: /pimcore/lib/pimcore/mail.php

246 Usage example $params = array('firstname' => 'Pim', 'lastname' => 'Core', 'product' => 73613); //sending an document (pimcore document) $mail = new \Pimcore\Mail(); $mail->addto('[email protected]'); $mail->setdocument('/ /my document'); $mail->setparams($params); $mail->send(); // sending a text-mail $mail = new \Pimcore\Mail(); $mail->addto('[email protected]'); $mail->setbodytext("this is just plain text"); $mail->send(); // Sending a rich text (HTML) $mail = new \Pimcore\Mail(); $mail->addto('[email protected]'); $mail->addbcc("[email protected]"); $mail->setbodyhtml("<b>some</b> rich text"); $mail->send(); Best Practices CLI Script for Object Import Extending the Pimcore User with User - Object Relation High Traffic Server Setup (*nix based Environment) Write-only database connection CLI Script for Object Import The following script is a cli script from a pimcore plugin, which imports pimcore objects. It can be executed from the directory where it is located like this: php import.php -f Please exchange the path "php" with the path to your php binary or add your php binary to the $PATH variable. Source Code for import.php:

247 <?php include(dirname( FILE ). "/../../../pimcore/cli/startup.php"); //this is optional, memory limit could be increased further (pimcore default is 1024M) ini_set('memory_limit', '1024M'); ini_set("max_execution_time", "-1"); //execute in admin mode define("pimcore_admin", true); //some command line options for my importer try { $opts = new \Zend_Console_Getopt(array( 'force f' => "force update of all objects" )); $opts->parse(); catch (\Zend_Console_Getopt_Exception $e) { echo $e->getusagemessage(); exit; $overwrite = $opts->getoption('force'); $importerconfig = new \Zend_Config_Xml(dirname( FILE ). "/../config/import.xml"); try { //do the import job $importer = new \Myplugin\Importer($importerConfig,$overwrite); $importer->init(); $importer->doimport(); echo "\ndone with import\n"; catch (Exception $e) { p_r($e); For the import logic, which in this case is encapsulated in "Myplugin_Importer" please refer to the import snippet in External System Interaction and the hints towards object field getters and setters in the pages about the different object fields. Note Don't forget to call the garbage collector if you're importing a huge amount of objects. Read more... Extending the Pimcore User with User - Object Relation Pimcore does not allow to extend the user directly. Instead it allows to create a relation between a user and one or more pimcore objects. This can be used to add information to a user or to associate one or more objects directly with a system user. This article presents an example where the "member" object is associated with a pimcore user. The screen shots show how this can be achieved through the pimcore admin interface. In the bottom there is also an example of how the user and member objects can be created and associated with each other programmatically. Regardless of the creation method of users and objects, in the first step the member class has to be defined in Settings/Object/Classes:

248 In this example the class " member" has the three properties location, name and user. The class can have an arbitrary number of properties. What is important in this context is, that it has a property of the type "User". Speaking in code this would be a Object_Class_Data_User. When creating the first object instance of the member class, you can see the input widget for the user property. It is a combo box where a us er can be selected from all available pimcore users.

249 In this example the user "michi" was selected. In the Settings/Users panel this relation is portrayed in the second tab of each user which is called "User - Object Dependencies"

250 Objects and users are not necessarily always created through user input in the admin interface. Sometimes these objects are created programmatically in the context of importers. The following code snippet shows how to first create the user and then the member object with the relation to the previously created user: use Pimcore\Model\User; use Pimcore\Model\Object;... //create a new user for Sydney $user = User::create(array( "parentid" => intval($usergroup->getid()), "username" => "sydney", "password" => "password1234", "hascredentials" => true, "active" => true ));... //create the Sydney member object $object = new Object\Member(); $object->setcreationdate(time()); $object->setuserowner($currentuser->getid()); $object->setusermodification($currentuser->getid()); $object->setpublished(true); $object->setname("sydney Subsidiary"); $object->setkey("member1234"); //select the user belonging to this object $object->setuser($user->getid()); $object->setparentid($parentfolderid); $object->save();... Aside from creating these objects, one might need to find out what objects are associated with a certain user. There is a convenient method

251 available to find all objects associated with a specific user: $objects = \Pimcore\Tool\Admin::getObjectsReferencingUser($userId); High Traffic Server Setup (*nix based Environment) For high traffic websites we recommend the following software and configurations: 1. Use a high performance cache backend for pimcore (pimcore provides special adapters) a. memcache (read optimization) => don't forget to install the memcache PHP bindings b. MongoDB (high read/write) => don't forget to install the MongoDB PHP bindings 2. Disable output-filters which are not indispensable a. CSS minify b. Javascript minify c. Image base64 embedding Enable the output-cache Install and setup varnish cache (pimcore sends already the right headers for varnish => ouput-cache lifetime,... ) Only use sessions when really necessary Tuning your MySQL configuration to exactly fit the needs of your application a. mysqltuner.pl b. mysqlprimer c. Percona Server / MariaDB 7. Make use of in-template caching Apache Tuning (mod_php) (if improvements are expected => test it on your stage-environment) Since pimcore uses the Zend Framework and other big libraries, apache has to load a huge amount of files for each request. The problem is that many linux distributions set limits for each user, and also for the user which is running the httpd. On Debian based systems the limit for open file handles is mostly 1024, that is enough for the most use cases, but not for high traffic websites. Follow the following instructions to fix the problem Please note that the following instructions are only tested with Debian 5.0 Execute the following commands: echo "* soft nofile 1024" >> /etc/security/limits.conf echo "* hard nofile " >> /etc/security/limits.conf echo "session required /lib/security/pam_limits.so" >> /etc/pam.d/login echo > /proc/sys/fs/file-max echo "ulimit -n " >> /etc/default/apache2 After that restart apache. /etc/init.d/apache restart Write-only database connection This feature is still experimental. Please do not use it in production yet. Since version Sometimes it is necessary to have a dedicated "write-only" database connection. A common case is for instance when pimcore is running on a MyQSL/MariaDB Galera Cluster and you want to effectively avoid deadlocks. See also Galery know limitations. To have a dedicated write connection you have to modify your website/var/config/system.xml and add the following part to it:

252 <database> <!-- this is your normal db setup --> <adapter>mysqli</adapter> <params> <username>root</username> <password>secret_password</password> <dbname>pimcore</dbname> <host>localhost</host> <port>3306</port> </params> <!-- now comes the interesting part --> <writeonly> <params> <username>root</username> <password>secret_password</password> <dbname>pimcore</dbname> <host>localhost</host> <port>3306</port> </params> </writeonly> </database> pimcore will now automatically use the 2nd configuration for data manipulation and DDL queries. Administrator's Guide Backups Commandline Interface Backend Search Reindex Cache Warming Command-line Backup Image & Video Thumbnail Generator MySQL Tools Install Plugins Setting up WebDav Translations User Permissions Backups Backups in the administration interface You can create full backups of the system directly in pimcore. Just go to "Extras" -> "Backup" The backup includes the following directories: /pimcore /website /plugins All other directories in the document root are ignored and not part of the backup archive. NOTE Depending on the amount of content in your pimcore installation, the backup can take up to some hours. Please use the commandl ine backup tool in this case, which is up to 10 times faster. Commandline backups Click here for more details. Restore backups

253 The backup file is an compressed zip archive which can be extracted in *nix systems with the commandline tool "unzip" on Windows systems you can use for example 7-Zip or the built-in support in Explorer. Commandline Interface pimcore offers for some jobs a commandline interface. Backend Search Reindex Cache Warming Command-line Backup Image & Video Thumbnail Generator MySQL Tools Backend Search Reindex This script recreates the search index for the backend search. The script is located at: /pimcore/cli/search-backend-reindex.php Cache Warming This script is useful to prefill the cache with all items. Especially on high-traffic sites this can be very useful. The script is located at: /pimcore/cli/cache-warming.php Please read the latest help message by calling: php pimcore/cli/cache-warming.php --help Command-line Backup Introduction The cli backup-tool gives you the possibility to create faster and periodic (eg. cron-jobs) backups from your pimcore installation. Options --filename -f <string> filename for the backup (default: backup_m-d-y_h-i).tar is added automatically --directory -d <string> target directory (absolute path without trailing slash) for the backup-file (default: /home/pimcore/www/website/var/backup) --overwrite -o overwrite existing backup with the same filename, default: true --cleanup -c <string> in days, backups in the target directory which are older than the given days will be deleted, default 7, use false to disable it --verbose -v show detailed information during the backup --help -h display this help Examples Just create a single backup into the backup-folder php backup.php Create a backup in a custom folder with a custom filename. If the destination file exists, it will be overwritten php backup.php -f mybackupname -d /var/backups/ -o

254 Image & Video Thumbnail Generator Image This script is useful to prefill the thumbnail cache. Especially on high-traffic sites this can be very useful (eg. generating thumbnails on a dedicated machine/cluster and sharing them via NFS), or just so save loading time. The script is located at: /pimcore/cli/image-thumbnails.php Please read the latest help message by calling: php pimcore/cli/image-thumbnails.php --help Video The script is located at: /pimcore/cli/video-thumbnails.php Please read the latest help message by calling: php pimcore/cli/video-thumbnails.php --help MySQL Tools This script is useful to optimize and warmup the database to increase the overall performance. Using this script is only useful after the MySQL daemon was restarted (or system reboot). The script will then defragment all the tables and load them into the memory. This is only useful if your MySQL configuration (my.cnf) is specifically optimized for your data (innodb pool size,... ). The script is located at: /pimcore/cli/mysql-tools.php Please read the latest help message by calling: php pimcore/cli/mysql-tools.php --help Install Plugins 1.) Use composer to install plugins 2.) copy your plugin into /plugins Setting up WebDav The following lines provide a guideline for setting up pimcore WebDAV access: 1. A vhost vor WebDAV access needs to be set up and targeted at pimcore For webdav access from a windows client this has to be a separate (sub)domain! HTTPS It is absolutely recommended to use WebDAV only with a HTTPS connection as it is using HTTP Basic Auth. Using WebDAV without HTTPS is highly insecure and grossly negligent - DON'T DO THIS!

255 Tip E.g. use dav.mysite.org for DAV access; configure your pimcore vhost to accept all sub domains of mysite.org by adding the server alias mysite.org <VirtualHost *:443> ServerName ServerAlias *mysite.org (...) </VirtualHost> 2. The WebDAV host needs to be added to the pimcore system configuration (Settings/System -> Assets, Hostname for WebDAV) Tip add dav.mysite.org as host name vor WebDAV 3. Connect to pimcore using WebDAV with any available pimcore user Tip connect to with the username admin and the admin's pimcore password Translations The following FAQs give an overview of possible translations in pimcore and its different translation modules. How do I get pimcore in different Languages? Pimcore is shipped with the standard language English. Further translations can be downloaded in Extras > Download System Languages by any user who has the "Translations" permission. The translations available for download are maintained by the pimcore community. Anybody can join and support the pimcore translation project by providing system translations in their own language. How can I add special Translations in pimcore which are not covered by the System Translations? There are several components in the pimcore backend which are configured differently for each project. These are object class names object field names object layout components document types predefined properties custom views document editables All these elements (except document editables) can be translated in Extras > Translations Admin. All installed system languages are availabe for translation. Document editables are translated through a special view helper. Please see Translation of Document Editables for more information. Strings which are subject to special translations, but have not been translated yet, are displayed with a "+" in front and after the string, if pimcore is in DEBUG mode. What about plugin translations? If you are a plugin developer, you can add translations to your plugins and provide them with your plugin as you wish. To see how plugins can hook into translations, please see the Plugin Developer's Guide. Just like the pimcore system translations, official pimcore plugins can be translated by the community in the pimcore translation project. How can I add Translations to the Website developed with pimcore? Please have a look at Website Translations. User Permissions In pimcore there are two levels of user permissions. Firstly the permissions on system components and secondly permissions on website elements, which are assets, objects and documents. Permissions can be granted to roles or individual users. The following paragraphs describe how and where permissions can be set and how they will or will not affect eachother. The precondition of setting user permissions is that the desired users and roles have been created in the system / users section. It is not mandatory to use roles, permissions can be granted to users directy. However, it is advised to use roles if there is a larger user group to manage. In the user/role settings tab it can be decided which permissions are granted to that user or role. An individual user has a few more general settings than the role

256 System Permissions Admin - if checked, all permissions on all system components are granted to that user Show welcome screen on startup Show close warning Roles - Select all roles incorporated by the user The following list outlines what the different system permissions (available for users and roles) mean: documents: defines whether the documents accordion is visible to a user assets: defines whether the assets accordion is visible to a user objects: defines whether the objects accordion is visible to a user system settings: specifies accessibility to the system settings users: defines whether a user may manage other users settings and system permissions classes: defines accesibility to object classes routes: specifies whether a user may create and edit static routes redirects: specifies whether a user may create and edit redirects clear cache: defines if a user may clear pimcore cache (internal cache and response cache if configured) clear temporary files: defines if user may delete temporary system files ( e.g thumbnails) thumbnails: specifies if a user may create thumbnail templates website translations: defines whether a user may view and edit website translations plugins: specifies if a user is allowed to download install and manage extensions seemode: seemode available/not available for user glossary: glossary accessible or not for user predefined properties: specifies wheter a user may create predefined properties document types: specifies whether a user may create and edit document types When new system components are introduced by the pimcore developer team, these permissions might be enhanced to include new components. A user will be granted any system permission that is granted to him directly or to any role he incorporates. A permission granted to a role incorporated by an individual user, can not be rescinded for that individual user. So it does not matter if the checkbox in the user's individual permissions settings is unchecked once a permission is granted through a role. Element Permissions - Workspaces Beyond the permissions mentioned above, a user's access can be restricted on element basis. This can be done by defining workspaces for a user or role. Provided that a user may generally access documents, it can be specified what a user / role may do or not do with each document or workspace. The same is true for objects and assets. These settings are manipulated in the " Workspaces" tab of a user / role. A user needs to be granted access to one or more workspaces. The user can not access any resources outside of his workspace(s). However, there are a few general rules on element permissions which need to be regarded: if a user does not have the right to list an element, all other permissions are obsolete if a user does not have the list permission on an element, all permissions on this element's children are obsolete The user permissions on element basis are summed up as follows: list: element can be listed in tree view: element can be opened save: element can be saved (save button visible) publish: element can be published (publish button visible) unpublish: element can be unpublished (unpublish button visible); does not exist for assets create: new child elements can be created (does not exist for assets) delete: element can be deleted rename: name of the element can be changed settings: element's settings can be managed i.e. the settings tab is visible; the settings permission also the path and thereby he right to move the element in tree versions: versions tab available properties: properties tab available and can be managed An individual user is granted access to all defined workspaces for any role he incorporates. In addition to that, users can have their own

257 workspaces. These are added to the permissions granted by roles. For example, a role myrole has been granted list and view access to /home/mypath. The user editor incorporates the role myrole and thereby inherits all workspace settings from the role. In case the editor has his own workspace settings on /home/mypath, these permissions are added to permissions from any role he incorporates. A permission granted by any role can not be rescinded for a single user. It is also possible to restrict access to localized fields on a language-level. By default, a user can view and edit (as long as he also has sufficient object permissions) all localized fields. This can now be restricted to a subset of languages. The configuration panel is accessible via the Special Settings column. User's Guide Backend Search and Properties Keyboard Shortcuts Working with WebDAV BitKinex as WebDAV Client Cyberduck as WebDAV Client NetDrive as WebDAV Client Windows Explorer as WebDAV Client Backend Search and Properties The pimcore backend search uses the mysql full-text search for performing the search for elements. More information for possible options and searches you can find here: Some general examples:

258 Search ' search1 search2 ' finds all elements that contain search1 OR search2. Search ' +search1 +search2 ' finds all elements that contain search1 AND search2. Search ' +search1 -search2 ' finds all elements that contain search1 BUT NOT search2. Searching for Properties For searching for values of specific properties, there is a special syntax available. "PROPERTYNAME:PROPERTY_VALUE" If you have a property photographer assigned to your assets, you can find all assets for Jon Doe with the following search: "photographer:jon Doe" Some additional examples: Search ' "city:london" ' finds all elements that have a property city with the value london. Search ' "city:london" "photographer:jon doe" ' finds all elements that have a property city with the value london OR a property photographer with the value jon doe. Search ' +"city:london" +"photographer:jon doe" ' finds all elements that have a property city with the value london AND a property photographer with the value jon doe. Keyboard Shortcuts Keys Action Notice F5 Reload active tab (document, asset, object) Crtl + S Save & publish active element (document, asset, object) Esc Close all active WYSIWYG editors also in document editmode Crtl + Shift + A Open asset by ID Ctrl + Shift + D Open document by ID Ctrl + Shift + O Open object by ID Ctrl + Shift + F Open document by Path including pretty URL's Working with WebDAV Before using WebDAV please check if you have done the setup correctly. Permissions & WebDAV

259 The following permissions are used for WebDAV: All other permissions are ignored by WebDAV. Please take also note of the following: Because of some special pimcore peculiarities (eg. lowercase filename without spaces,...) it's sometimes necessary to reconfigure your WebDAV client to work correctly with pimcore. How to setup a WebDAV connection Please see the following tutorials for the different WebDAV clients: BitKinex as WebDAV Client Cyberduck as WebDAV Client NetDrive as WebDAV Client Windows Explorer as WebDAV Client BitKinex as WebDAV Client BitKinex is one of the best WebDAV Client in terms of speed, if you want to have a fast and high configureable client, BitKinex is your choice. Set-Up a new WebDAV connection First you have to create a new connection. Click directly on "Connect" or use the menu "File" -> "Quick Connect". Then type in the configured hostname for your WebDAV connection and your username and password.

260 Then click "Connect". After that we have to configure some settings concerning the integrity checks of BitKinex. To do this, click right on your new connection, an select "Properties": Then go to "Transfers" and uncheck the marked settings:

261 Enjoy! Cyberduck as WebDAV Client Cyberduck is another very fast and reliable WEBDAV client, available for MacOS and Windows. Setup The setup of Cyberduck is straight forward, just create a new connection and access your assets. NetDrive as WebDAV Client

262 NetDrive is a small client which allows to mount a WebDAV connection directly as a drive into your explorer. With this client it's possible to work directly on files with programs of your choice. Setup Just click on "New Site", then enter your connection data, and click "Connect" Windows Explorer as WebDAV Client Since Windows XP every Windows version has an integrated WebDAV client. We recommend to use an other client because the Windows client is slow and sometimes buggy. The setup is different in every Windows version. Because there are no special settings, just follow a tutorial from here. pimcore >= 2.2.0: Please note that pimcore uses HTTP Basic Authentication which isn't enabled by default in Windows 7+ Extensions Contents Deeplinks into the pimcore Admin Interface Extension management using Composer Extension Manager Hooks Plugin Developer's Guide Example Plugin Anatomy and Design Plugin Backend UI Development and JS Hooks Useful Hints Adding custom main-navigation item Deeplinks into the pimcore Admin Interface This feature allows you to create deeplinks to documents, assets and objects in the admin interface. The specified element will open immediately after the framework is loaded.

263 Example: /admin/login/deeplink?document_1_page Schema is: MAINTYPE_ID_SUBTYPE More examples: /admin/login/deeplink?document_123_snippet /admin/login/deeplink?document_456_link /admin/login/deeplink?asset_1_folder /admin/login/deeplink?asset_312_video /admin/login/deeplink?object_1_folder /admin/login/deeplink?object_789_object /admin/login/deeplink?object_567_variant Extension management using Composer pimcore is using Composer to install public available extensions. Installing available extensions using Composer Create a new composer.json one level above your document root and put the following contents into it. { "config": { "document-root-path": "./public_html", "require": { "pimcore/example-areabrick": ">=1", "pimcore/example-plugin": ">=1" This configurations assumes that the document root is in./public_html, if yours is different, just replace the (relative) path with your own (e.g../www). So the composer.json is not very special, the only difference is the "document-root-path" configuration which is specific to the pimcore installer plugins. If you would now run php composer.phar install this will install the 2 example extensions into your /plugins and /website/views/areas directories. Of course you can include some other 3rd party libraries in your composer.json. You can find pimcore extensions at packagist.org. Sharing an extension on packagist If your're not familiar with packagist.org I'd recommend to read this first. Depending on the type of your extension you have to use a different configuration in your composer.json. Click here to see an example of a plugin Click here to see an example of an area brick There are 2 things to consider in general:

264 1.) the option "type" has to contain either "pimcore-plugin" or "pimcore-areabrick" depending on your extension 2.) you have to put either "pimcore/installer-plugin": ">=1" or "pimcore/installer-areabrick": ">=1" into your require section Depending on the type of your plugin there are some more things to keep in mind: Plugin Assuming we have the following configuration: { "name": "your-org/your-plugin", "type": "pimcore-plugin", "require": { "pimcore/installer-plugin": ">=1" The name of the plugin has to be "YourPlugin" (in your plugin.xml,...) - this is the same convention used for controllers, etc. The plugin will install in /plugins/yourplugin/ Area Brick Assuming we have the following configuration: { "name": "your-org/your-areabrick", "type": "pimcore-areabrick", "require": { "pimcore/installer-areabrick": ">=1" The name(id of the area brick (in area.xml) has to be "your-areabrick". It will automatically install in /website/views/areas/your-areabrick. Examples You can find an example for a plugin and an areabrick on github: Plugin example Area brick example Extension Manager Hooks With the extension hub hooks it's possible to hook into some actions concerning enable/disable extensions (bricks and plugins). Hooks are quite simple, the hook itself is a simple PHP script without a class or anything else. To use it, just put a file called [HOOKNAME].php into your extension root-directory. For example /plugins/myplugin/enable.php. Structure:

265 Inside the hook you can access all classes/methods/function,... as everywhere else in pimcore, you don't have to load anything. (see example below) Currently there are 2 hooks which you can use: Enable (Manager) This hook is called when the extension is enabled (not installed) in the Extension Manager. You can use this hook for example to create a thumbnail configuration for your area-brick, or something similar. Example: <?php $thumb = new Asset_Image_Thumbnail_Config(); $thumb->setname($this->_getparam("name")); $thumb->save(); Disable (Manager) This hook is called when the extension is disabled in the Extension Manager. You can use this hook to revert the changes you've made in enable.php Example: $thumb = Asset_Image_Thumbnail_Config::getByName("myThumbnail"); $thumb->delete(); Plugin Developer's Guide In order to be able to enhance pimcore and its already powerful core features, pimcore comes along with a plugin API. Thereby developers are able to reuse modules created for pimcore websites, or enhance and add system features. Pimcore plugins can be developed by utilizing Javascript user interface and PHP backend hooks. The following sections explain firstly how to design and structure plugins and secondly how to register for and utilize the hooks provided in the PHP backend and the Ext JS frontend.

266 Example Plugin Anatomy and Design Plugin Backend UI Development and JS Hooks Useful Hints Adding custom main-navigation item Example We're going to create a simple plugin which adds a menu item to the admin menu, opens a new tab when clicked and displays a table of data which is fetched from a webservice. Before we start, you need to understand a bit about how plugins are routed (if you have a solid knowledge of how Zend framework works, this will probably not be necessary). If you strictly follow this example, you probably won't have any problems but if you a mistake as little as a wrong-cased letter you will have a hard time debugging it. Pimcore has special routes for plugins so you need to understand how these work. For plugins, requesting a controller and action in the browser works a little bit different from the standard pimcore way. For the example below, we will have a URL like this: There are some aspects regarding the above URL that you should keep in mind: /plugin - is always the prefix for accesing plugins (notice there is no "s" in plugin) /MyPlugin/ - the plugin name is always case sensitive /get-address-book - if the action is in camelcase you should write it with hyphens and make it lowercase Plugin Skeleton First create a directory named: plugins/myplugin And add the following file: plugins/myplugin/plugin.xml With the following content:

267 <?xml version="1.0"?><zend-config xmlns:zf=" <plugin> <pluginname>myplugin</pluginname> <plugindescription>an example plugin</plugindescription> <pluginnicename>my Plugin</pluginNiceName> <pluginicon>/plugins/myplugin/static/img/icon.png</pluginicon> <pluginversion>1.0.0</pluginversion> <pluginrevision>1</pluginrevision> <pluginbuildtimestamp></pluginbuildtimestamp> <pluginclassname>myplugin\plugin</pluginclassname> <pluginincludepaths> <path>/myplugin/lib</path> <path>/myplugin/controllers</path> </pluginincludepaths> <pluginnamespaces> <namespace>myplugin</namespace> </pluginnamespaces> <pluginjspaths> <path>/myplugin/static/js/admin.js</path> </pluginjspaths> <plugindocumenteditmodejspaths> <path>/myplugin/static/js/pimcore/document/tags/demo.js</path> </plugindocumenteditmodejspaths> </plugin> </zend-config> Next, we need to write the plugin PHP class, so create the following file: plugins/myplugin/lib/myplugin/plugin.php With the following content:

268 <?php namespace MyPlugin; use Pimcore\API\Plugin as PluginLib; class Plugin extends PluginLib\AbstractPlugin implements PluginLib\PluginInterface { public static function needsreloadafterinstall() { return true; public static function install() { // we need a simple way to indicate that the plugin is installed, so we'll create a directory /////////////////////////////////////////////////////////////////////////////////// /////////////////// // NOTE - make sure that your plugin/myplugin directory is writable by whatever user Apache runs as // /////////////////////////////////////////////////////////////////////////////////// /////////////////// $path = self::getinstallpath(); if(!is_dir($path)) { mkdir($path); if (self::isinstalled()) { return "MyPlugin Plugin successfully installed."; else { return "MyPlugin Plugin could not be installed"; public static function uninstall() { rmdir(self::getinstallpath()); if (!self::isinstalled()) { return "MyPlugin Plugin successfully uninstalled."; else { return "MyPlugin Plugin could not be uninstalled"; public static function isinstalled() { return is_dir(self::getinstallpath()); public static function gettranslationfile($language) { public static function getinstallpath() { return PIMCORE_PLUGINS_PATH."/MyPlugin/install";

269 At this point, if you log into the Pimcore admin then navigate to Extras -> Extensions -> Manage Extensions you should be able to see, install and uninstall your new plugin. Modifying The Admin Interface Next, we're going to modify the admin interface. All of the UI changes are driven by javascript, so create the following file: plugins/myplugin/static/js/admin.js And put the following code in it: pimcore.registerns("pimcore.plugin.myplugin"); pimcore.plugin.myplugin = Class.create(pimcore.plugin.admin, { getclassname: function() { return "pimcore.plugin.myplugin";, initialize: function() { pimcore.plugin.broker.registerplugin(this);, pimcoreready: function (params,broker){ // add a sub-menu item under "Extras" in the main menu var toolbar = pimcore.globalmanager.get("layout_toolbar"); var action = new Ext.Action({ id: "my_plugin_menu_item", text: "My Plugin", iconcls:"my_plugin_menu_icon", handler: this.showtab );, toolbar.extrasmenu.add(action); showtab: function() { myplugin.panel = new Ext.Panel({ id: "my_plugin_check_panel", title: "My Plugin", iconcls: "my_plugin_check_panel_icon", border: false, layout: "fit", closable: true, items: [] ); var tabpanel = Ext.getCmp("pimcore_panel_tabs"); tabpanel.add(myplugin.panel); tabpanel.activate("my_plugin_check_panel"); ); pimcore.layout.refresh(); var myplugin = new pimcore.plugin.myplugin(); Now, if you reload the admin, you should see a new menu item under Extras -> My Plugin, where clicking on it will bring up a blank tab. Making It Do Something Useful

270 Let's add some functionality to pull data from a web service and display it in a table. In the showtab function, in admin.js, change the following line: items: [] To: items: [myplugin.getgrid()] And add a new method after showtab: showtab: function() {..., getgrid: function() { // fetch data from a webservice (which we haven't written yet!) myplugin.store = new Ext.data.JsonStore({ id: 'my_plugin_store', url: '/plugin/myplugin/admin/getaddressbook', restful: false, root: "addresses", fields: [ "name", "phonenumber", "address" ] ); myplugin.store.load(); var typecolumns = [ {header: "Name", width: 100, sortable: true, dataindex: 'name', {header: "Phone Number", width: 100, sortable: true, dataindex: 'phonenumber', {header: "Address", width: 100, sortable: true, dataindex: 'address' ]; myplugin.grid = new Ext.grid.GridPanel({ frame: false, autoscroll: true, store: myplugin.store, columns: typecolumns, trackmouseover: true, columnlines: true, striperows: true, viewconfig: { forcefit: true ); return myplugin.grid; Now we write a simple webservice so that we can pull some data - create the following file:

271 plugins/myplugin/controllers/admincontroller.php With the following content: <?php use Pimcore\Controller\Action\Admin; class MyPlugin_AdminController extends Admin { public function getaddressbookaction() { $addresses = array(); $addresses[] = array( 'name' => 'Bob Dole', 'phonenumber' => ' ', 'address' => '123 Fake Street' ); $addresses[] = array( 'name' => 'Joe Smith', 'phonenumber' => ' ', 'address' => '45 Newington Heights' ); return $this->_helper->json(array('addresses' => $addresses)); Reload the admin interface, navigate to Extras -> My Plugin and you should see a table with two rows, which can be sorted by column. Adding an individual Document Editable In order to create an individual document editable, all that needs to be done ist creating a Document_Tag_Mytag which extends the Document_Tag. It must be within that namespace! For the frontend a JavaScript class needs to be added "pimcore.document.tags.mytag". It can extend any of the existing pimcore.document.tags and must return it's type by overwriting the function gettype(). This JS file must be included in editmode. You can tell pimcore to do so by adding <plugindocumenteditmodejspaths> to the plugin.xml. The example at the top of the page shows such an include. Plugin Anatomy and Design Plugins are situated within the plugins folder in the pimcore root directory. For each plugin a separate folder must be created. The folder structure of a plugin should look as follows: /plugins /PLUGIN_NAME /controllers /lib /PLUGIN_LIB /modules /PLUGIN_NAMESPACE /static /css /js /img /views plugin.xml The lib folder should contain all PHP libraries and plugin specific libraries, as well as any other php code required by the plugin. Before

272 including external libs, it should be checked if they are not already included by pimcore itself. The lib folder is also the place for the plugin class, which needs to extend the abstract plugin class and implement the plugin interface provided by pimcore. More information on plugin development in the backend is provided in backend development. All user interface components should be situated in the static folder. Javascript/CSS files are included when the pimcore user interface starts up, provided that they are properly specified in the plugin config. Further information on user interface plugin development is is given in Ext JS frontend development. Plugins need to be configured in the plugin.xml. The following parameters need to be configured in plugin.xml: plugin name (unique identifier = plugin folder) (must not contain spaces) plugin nice name (name to be displayed in pimcore) plugin icon (icon to be displayed in pimcore) plugin description plugin server (repository where plugin can be downloaded and updated) plugin version and revision (these tags must be available, values are filled at repository checkin) source to display in an iframe in the pimcore admin when opening the plugin optins (can be used for your own config form) PHP class name of the plugin PHP include paths PHP namespaces Javascript paths CSS file paths Javascript paths for scripts which should be included in document editmode CSS paths for stylesheets which should be included in document editmode An example plugin.xml looks like this:

273 <?xml version="1.0"?> <zend-config xmlns:zf=" <plugin> <!-- unique identifier = folder name --> <pluginname>test</pluginname> <pluginnicename>my Test Plugin</pluginNiceName> <!-- 64 x 64 Pixel Icon --> <pluginicon>/plugin/test/static/img/icon.png</pluginicon> <plugindescription> Put the description of your Plugin here. It is displayed in Pimcore backend </plugindescription> <pluginserver>your.pluginrepository.com</pluginserver> <!-- Version, revision and timestamp are updated by createrevision script at check in to plugin server!--> <pluginversion>1.0.0</pluginversion> <pluginrevision>1</pluginrevision> <pluginbuildtimestamp>0</pluginbuildtimestamp> <!-- content to include in pimcore admin in a iframe, if pluginiframesrc is defined, the options button is included and the iframe is shown in an extra tab --> <pluginiframesrc>/plugin/test/controller/action</pluginiframesrc> <!-- classname of the plugin which extends Pimcore_API_Plugin_Abstract--> <pluginclassname>test\plugin</pluginclassname> <!-- include paths relative to plugin-directory --> <pluginincludepaths> <path>/test/path1</path> <path>/test/path2</path> </pluginincludepaths> <!-- namespaces to register with autoloader--> <pluginnamespaces> <namespace>test</namespace> <namespace>resource</namespace> </pluginnamespaces> <!-- js files needed for pimcore plugin (backend) with path relative to plugin-directory --> <pluginjspaths> <path>/test/static/js/test.js</path> </pluginjspaths> <!-- css files needed for pimcore plugin (backend) with path relative to plugin-directory --> <plugincsspaths> <path>/test/static/css/test.css</path> </plugincsspaths> <!-- js which should be included in document edit mode, path relative to plugin-directory --> <plugindocumenteditmodejspaths> <path>/test/static/js/editmode.js</path> </plugindocumenteditmodejspaths> <!-- css files which should be included in document edit mode, path relative to plugin-directory --> <plugindocumenteditmodecsspaths> <path>/test/static/css/edimode.css</path> </plugindocumenteditmodecsspaths> <!-- path to a configuration file which is directly opened in the file explorer when you click on the configure button in the extension manager --> <pluginxmleditorfile>/website/var/plugins/test/config.xml</pluginxmleditorfile> </plugin> </zend-config>

274 Plugin Backend Pimcore plugins should extend Pimcore\API\Plugin\AbstractPlugin and must implement Pimcore\API\Plugin\PluginInterface. This means a plugin must implement the install, uninstall and isinstalled static methods specified in the interface. The install method can be used to create database tables and do other initial tasks. The uninstall method should make sure to undo all these things. Moreover it can override the read yforinstall method. This is the right place to check for requirements such as minimum pimcore version or read/write permissions on the filesystem. If this method returns false, the plugin cannot be installed via the pimcore admin. Hooks To hook into the core functionalities you can attach to any event provided by the pimcore event manager. For a full list of events and to lear more about event in pimcore please have a look at the event reference. Example The following example shows a plugin that hooks into the document save process. <?php namespace Test; use Pimcore\API\Plugin as PluginLib; class Plugin extends PluginLib\AbstractPlugin implements PluginLib\PluginInterface { public function init() { // this method is called automatically during the initialization process of the plugin \Pimcore::getEventManager()->attach("document.postAdd", array($this, "handledocument")); \Pimcore::getEventManager()->attach("document.postUpdate", array($this, "handledocument")); public function handledocument($e) { $doc = $e->gettarget(); // do something with the document public static function install(){ return true; public static function uninstall(){ return true; public static function isinstalled() { return true; i18n If a plugin requires its own i18n texts in pimcore admin user interface, it should override the gettranslationfile method contained in Pimcore\ API\Plugin\AbstractPlugin. This method receives the current language as parameter and must return the path to the according texts file relative to the plugin directory. E.g. something like /Testplugin/texts/en.csv. The texts file must be a.csv file in which translations are specified by the translation key in the first column and the text in the second column. Column separator: ',' text identifier: ' '

275 /** * string $language string $languagefile for the specified language relative to plugin directory */ function gettranslationfile($language){ return null; Plugin Installation and Deinstallation in pimcore UI When a plugin is installed/uninstalled in the pimcore admin user interface, the frontend component might need the following information from the plugin. If a plugin does not have a user interface component, these abstract methods can be ignored, and do not need to be overridden. More information on installing and uninstalling a plugin with UI components, is provided in plugin user interface development. public static function getjsclassname(){ return ""; public static function needsreloadafterinstall(){ return false; Plugin State Each plugin has the possiblity to issue a status message which is displayed in pimcore plugin settings. To accomplish that, the getpluginstat e() Method of Pimcore\API\Plugin\AbstractPlugin needs to be overridden in the plugin class. Local storage for your plugin (dynamic files) Sometimes a plugin needs some HDD - space to put logfiles or some other dynamic files on it. Please put this files into /website/var/plugins/your_plugin_name/ UI Development and JS Hooks The pimcore user interface is based upon the Ext JS Framework. A plugin can add Ext components to the user interface or execute any other Javascript required in the plugin context. User interface plugins for the pimcore admin need to be registered at a plugin broker, which notifies all registered plugins upon certain hooks. All Javascript and CSS which should be included, needs to be defined in plugin.xml, as described in plugin anatomy and design. These scripts are loaded last upon pimcore startup. They are loaded in the same order as specified in plugin.xml. A plugin must extend pimcore.plugin.admin and can override all provided methods defined there. Each plugin needs to register itself with the plugin broker by calling pimcore.plugin.broker.registerplugin(plugin) The broker then will notify each plugin upon the hooks described below: uninstall - is called when the corresponding plugin is uninstalled via pimcore admin pimcoreready - admin is loaded, viewport is passed as parameter preopenasset - before asset is opened, asset and type are passed as parameters postopenasset - after asset is opened, asset and type are passed as parameters preopendocument - before document is opened, document and type are passed as parameters postopendocument - after document is opened, document and type are passed as parameters preopenobject - before object is opened, object and type are passed as parameters postopenobject - after object is opened, object and type are passed as parameters Uninstall is called after plugin has been uninstalled - this hook can be used to do remove plugin features from the UI after installation. Note: In order to be notified upon uninstall, a plugin must override the getclassname() method of pimcore.plugin.admin and return its own class name

276 Further js hooks are currently being discussed, but have not yet been declared official js code hooks. I18n texts for plugins Plugin backend development gives details on how plugin specific translation files can be included. Once this is done, translations can be accessed anywhere in the plugin's javascript by calling t('translation_key') Installing and Uninstalling Plugin UI Components Plugin UI components might need to be activated/loaded after the plugin is installed in the pimcore admin. As plugin Javascript and CSS files are only available in the browser after installation and reloading of the UI, the backend plugin can return a flag that UI reload is required. If this flag is set to true, the UI asks the user to reload after install. After that, all plugin components should be available. With uninstall, it is not absolutely necessary to reload just to deactivate plugin components. The plugin is notified through the uninstall hook (provided that it implements the getclassname() method correctly). In the uninstall function the plugin can hide/deactivate everything in the frontend UI that will not work anymore after uninstalling the plugin. Useful Hints Adding to the pimcore UI Extending and Modifying the Object Tab Classes of Object Layout Components The name of an object class layout element is added to the element's class names as objectlayout_element_fieldname e.g. An object panel with the name "mypanel" would have the css class "objectlayout_element_mypanel" This comes in handy when you need to style a layout element differently through a plugin. Adding custom main-navigation item Overview of possible icons: (Font Awesome) Icon-classes included in pimcore: /pimcore/static/css/fontello.css

277 pimcore.registerns("pimcore.plugin.example"); pimcore.plugin.example = Class.create(pimcore.plugin.admin, { getclassname: function() { return "pimcore.plugin.example";, initialize: function() { pimcore.plugin.broker.registerplugin(this); this.navel = Ext.get('pimcore_menu_logout').insertSibling('<li id="pimcore_menu_custom">truck</li>');, pimcoreready: function (params,broker){ var menu = new Ext.menu.Menu({ items: [{ text: "Item 1", iconcls: "pimcore_icon_apply", handler: function () {alert("pressed 1"), { text: "Item 2", iconcls: "pimcore_icon_delete", handler: function () {alert("pressed 2") ], cls: "pimcore_navigation_flyout" ); var toolbar = pimcore.globalmanager.get("layout_toolbar"); this.navel.on("mousedown", toolbar.showsubmenu.bind(menu)); ); var exampleplugin = new pimcore.plugin.example(); FAQ Why I have to install pimcore into the document-root? Why pimcore doesn't work on my webspace? Why I have to install pimcore into the document-root? Pimcore isn't designed to operate on shared hosts, so we don't care about the needs for those systems. On dedicated systems you can create as much virtual hosts as you like and the install into the document-root shouldn't be the big deal. Why pimcore doesn't work on my webspace? Well, pimcore has special needs to the running environment. Most of the webhosters don't offer or limit them. Please read more about the minimal system-requirements here. We highly recommend to run pimcore only on dedicated systems (root-server,...).

XCloner Official User Manual

XCloner Official User Manual XCloner Official User Manual Copyright 2010 XCloner.com www.xcloner.com All rights reserved. xcloner.com is not affiliated with or endorsed by Open Source Matters or the Joomla! Project. What is XCloner?

More information

Kollaborate Server Installation Guide!! 1. Kollaborate Server! Installation Guide!

Kollaborate Server Installation Guide!! 1. Kollaborate Server! Installation Guide! Kollaborate Server Installation Guide 1 Kollaborate Server Installation Guide Kollaborate Server is a local implementation of the Kollaborate cloud workflow system that allows you to run the service in-house

More information

ultimo theme Update Guide Copyright 2012-2013 Infortis All rights reserved

ultimo theme Update Guide Copyright 2012-2013 Infortis All rights reserved ultimo theme Update Guide Copyright 2012-2013 Infortis All rights reserved 1 1. Update Before you start updating, please refer to 2. Important changes to check if there are any additional instructions

More information

Installing an open source version of MateCat

Installing an open source version of MateCat Installing an open source version of MateCat This guide is meant for users who want to install and administer the open source version on their own machines. Overview 1 Hardware requirements 2 Getting started

More information

Drupal CMS for marketing sites

Drupal CMS for marketing sites Drupal CMS for marketing sites Intro Sample sites: End to End flow Folder Structure Project setup Content Folder Data Store (Drupal CMS) Importing/Exporting Content Database Migrations Backend Config Unit

More information

FORTIS. User Guide. Fully responsive flexible Magento theme by Infortis. Copyright 2012-2013 Infortis. All rights reserved

FORTIS. User Guide. Fully responsive flexible Magento theme by Infortis. Copyright 2012-2013 Infortis. All rights reserved FORTIS Fully responsive flexible Magento theme by Infortis User Guide Copyright 2012-2013 Infortis All rights reserved How to use this document Please read this user guide carefully, it will help you eliminate

More information

How To Use Ngnix (Php) With A Php-Fpm (Php-Fmm) On A Web Server (Php5) On Your Web Browser) On An Ubuntu Web Server On A Raspberry Web 2.5 (Net

How To Use Ngnix (Php) With A Php-Fpm (Php-Fmm) On A Web Server (Php5) On Your Web Browser) On An Ubuntu Web Server On A Raspberry Web 2.5 (Net Powering Magento with Ngnix and PHP-FPM Written by: Yuri Golovko Alexey Samorukov Table of Contents INTRODUCTION WHY YOU SHOULD CONSIDER NGNIX NGNIX AND STATIC CONTENT HOW TO USE NGNIX NGNIX SYSTEM REQUIREMENTS

More information

RecoveryVault Express Client User Manual

RecoveryVault Express Client User Manual For Linux distributions Software version 4.1.7 Version 2.0 Disclaimer This document is compiled with the greatest possible care. However, errors might have been introduced caused by human mistakes or by

More information

ProjectPier v0.8.8. Getting Started Guide

ProjectPier v0.8.8. Getting Started Guide ProjectPier v0.8.8 Getting Started Guide Updated October 2014 Contents Contents... 2 Overview... 4 License... 4 Installation... 4 Who should perform the installation?... 4 Requirements... 5 Enabling InnoDB

More information

MAMP 3 User Guide! March 2014 (c) appsolute GmbH!

MAMP 3 User Guide! March 2014 (c) appsolute GmbH! MAMP 3 User Guide March 2014 (c) appsolute GmbH 1 I. Installation 3 1. Installation requirements 3 2. Installing and upgrading 3 3. Uninstall 3 II. First Steps 4 III. Preferences 5 Start/Stop 5 2. Ports

More information

Bazaarvoice for Magento

Bazaarvoice for Magento Bazaarvoice Bazaarvoice for Magento Extension Implementation Guide v6.1.2.3 Version 6.1.2.3 Bazaarvoice Inc. 8/5/2015 Introduction Bazaarvoice maintains a pre-built integration into the Magento platform.

More information

Online Backup Linux Client User Manual

Online Backup Linux Client User Manual Online Backup Linux Client User Manual Software version 4.0.x For Linux distributions August 2011 Version 1.0 Disclaimer This document is compiled with the greatest possible care. However, errors might

More information

MAGENTO Migration Tools

MAGENTO Migration Tools MAGENTO Migration Tools User Guide Copyright 2014 LitExtension.com. All Rights Reserved. Magento Migration Tools: User Guide Page 1 Content 1. Preparation... 3 2. Setup... 5 3. Plugins Setup... 7 4. Migration

More information

Online Backup Client User Manual

Online Backup Client User Manual For Linux distributions Software version 4.1.7 Version 2.0 Disclaimer This document is compiled with the greatest possible care. However, errors might have been introduced caused by human mistakes or by

More information

1. Product Information

1. Product Information ORIXCLOUD BACKUP CLIENT USER MANUAL LINUX 1. Product Information Product: Orixcloud Backup Client for Linux Version: 4.1.7 1.1 System Requirements Linux (RedHat, SuSE, Debian and Debian based systems such

More information

Online Backup Client User Manual Linux

Online Backup Client User Manual Linux Online Backup Client User Manual Linux 1. Product Information Product: Online Backup Client for Linux Version: 4.1.7 1.1 System Requirements Operating System Linux (RedHat, SuSE, Debian and Debian based

More information

Digital Downloads Pro

Digital Downloads Pro Digital Downloads Pro [Install Manual] Start Requirements Install What s New About Created: 24/09/2014 By: wojoscripts.com http://wojoscripts.com/ddp/ Thank you for your purchase! If you have any questions

More information

Bubble Code Review for Magento

Bubble Code Review for Magento User Guide Author: Version: Website: Support: Johann Reinke 1.1 https://www.bubbleshop.net [email protected] Table of Contents 1 Introducing Bubble Code Review... 3 1.1 Features... 3 1.2 Compatibility...

More information

Xtreeme Search Engine Studio Help. 2007 Xtreeme

Xtreeme Search Engine Studio Help. 2007 Xtreeme Xtreeme Search Engine Studio Help 2007 Xtreeme I Search Engine Studio Help Table of Contents Part I Introduction 2 Part II Requirements 4 Part III Features 7 Part IV Quick Start Tutorials 9 1 Steps to

More information

This installation guide will help you install your chosen IceTheme Template with the Cloner Installer package.

This installation guide will help you install your chosen IceTheme Template with the Cloner Installer package. Introduction This installation guide will help you install your chosen IceTheme Template with the Cloner Installer package. There are 2 ways of installing the theme: 1- Using the Clone Installer Package

More information

This guide specifies the required and supported system elements for the application.

This guide specifies the required and supported system elements for the application. System Requirements Contents System Requirements... 2 Supported Operating Systems and Databases...2 Features with Additional Software Requirements... 2 Hardware Requirements... 4 Database Prerequisites...

More information

X-POS GUIDE. v3.4 INSTALLATION. 2015 SmartOSC and X-POS

X-POS GUIDE. v3.4 INSTALLATION. 2015 SmartOSC and X-POS GUIDE INSTALLATION X-POS v3.4 2015 SmartOSC and X-POS 1. Prerequisites for Installing and Upgrading Server has Apache/PHP 5.2.x/MySQL installed. Magento Community version 1.7.x or above already installed

More information

JISIS and Web Technologies

JISIS and Web Technologies 27 November 2012 Status: Draft Author: Jean-Claude Dauphin JISIS and Web Technologies I. Introduction This document does aspire to explain how J-ISIS is related to Web technologies and how to use J-ISIS

More information

MassTransit 6.0 Enterprise Web Configuration for Macintosh OS 10.5 Server

MassTransit 6.0 Enterprise Web Configuration for Macintosh OS 10.5 Server MassTransit 6.0 Enterprise Web Configuration for Macintosh OS 10.5 Server November 6, 2008 Group Logic, Inc. 1100 North Glebe Road, Suite 800 Arlington, VA 22201 Phone: 703-528-1555 Fax: 703-528-3296 E-mail:

More information

AJ Matrix V5. Installation Manual

AJ Matrix V5. Installation Manual AJ Matrix V5 Installation Manual AJ Square Consultancy Services (p) Ltd., The Lord's Garden, #1-12, Vilacheri Main Road, Vilacheri, Madurai-625 006.TN.INDIA, Ph:+91-452-3917717, 3917790. Fax : 2484600

More information

Sitemap. Component for Joomla! This manual documents version 3.15.x of the Joomla! extension. http://www.aimy-extensions.com/joomla/sitemap.

Sitemap. Component for Joomla! This manual documents version 3.15.x of the Joomla! extension. http://www.aimy-extensions.com/joomla/sitemap. Sitemap Component for Joomla! This manual documents version 3.15.x of the Joomla! extension. http://www.aimy-extensions.com/joomla/sitemap.html Contents 1 Introduction 3 2 Sitemap Features 3 3 Technical

More information

System Administration Training Guide. S100 Installation and Site Management

System Administration Training Guide. S100 Installation and Site Management System Administration Training Guide S100 Installation and Site Management Table of contents System Requirements for Acumatica ERP 4.2... 5 Learning Objects:... 5 Web Browser... 5 Server Software... 5

More information

ProxiBlue Dynamic Category Products

ProxiBlue Dynamic Category Products ProxiBlue Dynamic Category Products Thank you for purchasing our product. Support, and any queries, please log a support request via http://support.proxiblue.com.au If you are upgrading from a pre v3 version,

More information

Installation & User Guide

Installation & User Guide SharePoint List Filter Plus Web Part Installation & User Guide Copyright 2005-2011 KWizCom Corporation. All rights reserved. Company Headquarters KWizCom 50 McIntosh Drive, Unit 109 Markham, Ontario ON

More information

Online Backup Client User Manual

Online Backup Client User Manual For Mac OS X Software version 4.1.7 Version 2.2 Disclaimer This document is compiled with the greatest possible care. However, errors might have been introduced caused by human mistakes or by other means.

More information

Top Navigation menu - Tabs. User Guide 1. www.magazento.com & www.ecommerceoffice.com

Top Navigation menu - Tabs. User Guide 1. www.magazento.com & www.ecommerceoffice.com User Guide User Guide 1 Extension Description Successful Websites ALWAYS have logical navigation that mirror real world navigational expectations and experiences. Good menus ALWAYS looks 100% clear, because

More information

Getting Started with Dynamic Web Sites

Getting Started with Dynamic Web Sites PHP Tutorial 1 Getting Started with Dynamic Web Sites Setting Up Your Computer To follow this tutorial, you ll need to have PHP, MySQL and a Web server up and running on your computer. This will be your

More information

OpenCart. SugarCRM CE (Community Edition Only) Integration. Guide

OpenCart. SugarCRM CE (Community Edition Only) Integration. Guide OpenCart SugarCRM CE (Community Edition Only) Integration Guide By Lim Tee Chert 23 June 2012 (last updated on: 08 January 2015) http://www.cartbooks.com Purpose: This is A Release for OpenCart SugarCRM

More information

Greenstone Documentation

Greenstone Documentation Greenstone Documentation Web library and Remote Collection Building with GLI Client Web Library. This enables any computer with an existing webserver to serve pre-built Greenstone collections. As with

More information

ultimo theme Update Guide Copyright 2012-2013 Infortis All rights reserved

ultimo theme Update Guide Copyright 2012-2013 Infortis All rights reserved ultimo theme Update Guide Copyright 2012-2013 Infortis All rights reserved 1 1. Update Before you start updating, please refer to 2. Important changes to check if there are any additional instructions

More information

Build it with Drupal 8

Build it with Drupal 8 Build it with Drupal 8 Comprehensive guide for building common websites in Drupal 8. No programming knowledge required! Antonio Torres This book is for sale at http://leanpub.com/drupal-8-book This version

More information

1. Digital Asset Management User Guide... 2 1.1 Digital Asset Management Concepts... 2 1.2 Working with digital assets... 4 1.2.1 Importing assets in

1. Digital Asset Management User Guide... 2 1.1 Digital Asset Management Concepts... 2 1.2 Working with digital assets... 4 1.2.1 Importing assets in 1. Digital Asset Management User Guide....................................................... 2 1.1 Digital Asset Management Concepts.................................................... 2 1.2 Working with

More information

Magento 1.3: PHP Developer's Guide

Magento 1.3: PHP Developer's Guide Magento 1.3: PHP Developer's Guide Jamie Huskisson Chapter No. 3 "Magento's Architecture" In this package, you will find: A Biography of the author of the book A preview chapter from the book, Chapter

More information

SIMIAN systems. Setting up a Sitellite development environment on Windows. Sitellite Content Management System

SIMIAN systems. Setting up a Sitellite development environment on Windows. Sitellite Content Management System Setting up a Sitellite development environment on Windows Sitellite Content Management System Introduction For live deployment, it is strongly recommended that Sitellite be installed on a Unix-based operating

More information

Creating Value through Innovation MAGENTO 1.X TO MAGENTO 2.0 MIGRATION

Creating Value through Innovation MAGENTO 1.X TO MAGENTO 2.0 MIGRATION Creating Value through Innovation MAGENTO 1.X TO MAGENTO 2.0 MIGRATION AGENDA 1. Overview of Magento 2.0 2. Features and benefits of Magento 2.0 over Magento 1.x 3. Why should we upgrade to Magento 2.0

More information

OpenCart AliExpress Affiliate Plugin User Guide

OpenCart AliExpress Affiliate Plugin User Guide OpenCart AliExpress Affiliate Plugin User Guide By TC 30 April 2015 (last updated on: 15 Oct 2015) http://www.aliwebstore.com Purpose: The purpose of this Opencart AliExpress Affiliate Plugin is to provide

More information

Online Backup Client User Manual

Online Backup Client User Manual Online Backup Client User Manual Software version 3.21 For Linux distributions January 2011 Version 2.0 Disclaimer This document is compiled with the greatest possible care. However, errors might have

More information

FileMaker Server 13. Custom Web Publishing with PHP

FileMaker Server 13. Custom Web Publishing with PHP FileMaker Server 13 Custom Web Publishing with PHP 2007 2013 FileMaker, Inc. All Rights Reserved. FileMaker, Inc. 5201 Patrick Henry Drive Santa Clara, California 95054 FileMaker and Bento are trademarks

More information

FileMaker Server 12. Custom Web Publishing with PHP

FileMaker Server 12. Custom Web Publishing with PHP FileMaker Server 12 Custom Web Publishing with PHP 2007 2012 FileMaker, Inc. All Rights Reserved. FileMaker, Inc. 5201 Patrick Henry Drive Santa Clara, California 95054 FileMaker and Bento are trademarks

More information

phpservermon Documentation

phpservermon Documentation phpservermon Documentation Release 3.1.0-dev Pepijn Over May 11, 2014 Contents 1 Introduction 3 1.1 Summary................................................. 3 1.2 Features..................................................

More information

Module Google Rich Snippets + Product Ratings and Reviews

Module Google Rich Snippets + Product Ratings and Reviews Module Google Rich Snippets + Product Ratings and Reviews Date : May 13 th, 2013 Business Tech Installation Service If you need help installing and configuring your module, we can offer you an installation

More information

Online Backup Client User Manual Mac OS

Online Backup Client User Manual Mac OS Online Backup Client User Manual Mac OS 1. Product Information Product: Online Backup Client for Mac OS X Version: 4.1.7 1.1 System Requirements Operating System Mac OS X Leopard (10.5.0 and higher) (PPC

More information

Online Backup Client User Manual Mac OS

Online Backup Client User Manual Mac OS Online Backup Client User Manual Mac OS 1. Product Information Product: Online Backup Client for Mac OS X Version: 4.1.7 1.1 System Requirements Operating System Mac OS X Leopard (10.5.0 and higher) (PPC

More information

Module developer s tutorial

Module developer s tutorial Module developer s tutorial Revision: May 29, 2011 1. Introduction In order to keep future updates and upgrades easy and simple, all changes to e-commerce websites built with LiteCommerce should be made

More information

Front-End Performance Testing and Optimization

Front-End Performance Testing and Optimization Front-End Performance Testing and Optimization Abstract Today, web user turnaround starts from more than 3 seconds of response time. This demands performance optimization on all application levels. Client

More information

1. Digital Asset Management User Guide... 2 1.1 Digital Asset Management Concepts... 2 1.2 Working with digital assets... 4 1.2.1 Importing assets in

1. Digital Asset Management User Guide... 2 1.1 Digital Asset Management Concepts... 2 1.2 Working with digital assets... 4 1.2.1 Importing assets in 1. Digital Asset Management User Guide........................................................................... 2 1.1 Digital Asset Management Concepts........................................................................

More information

CREATE A CUSTOM THEME WEBSPHERE PORTAL 8.0.0.1

CREATE A CUSTOM THEME WEBSPHERE PORTAL 8.0.0.1 CREATE A CUSTOM THEME WEBSPHERE PORTAL 8.0.0.1 WITHOUT TEMPLATE LOCALIZATION, WITHOUT WEBDAV AND IN ONE WAR FILE Simona Bracco Table of Contents Introduction...3 Extract theme dynamic and static resources...3

More information

Installation Instructions

Installation Instructions WampServer Installation Instructions The Web pages that students create in CIT 173 PHP Programming contain code that must be processed by a Web server. It isn t possible to open PHP files directly using

More information

Official Amazon Checkout Extension for Magento Commerce. Documentation

Official Amazon Checkout Extension for Magento Commerce. Documentation Official Amazon Checkout Extension for Magento Commerce Documentation 1. Introduction This extension provides official integration of your Magento store with Inline Checkout by Amazon service. Checkout

More information

CEFNS Web Hosting a Guide for CS212

CEFNS Web Hosting a Guide for CS212 CEFNS Web Hosting a Guide for CS212 INTRODUCTION: TOOLS: In CS212, you will be learning the basics of web development. Therefore, you want to keep your tools to a minimum so that you understand how things

More information

Open Source Content Management System for content development: a comparative study

Open Source Content Management System for content development: a comparative study Open Source Content Management System for content development: a comparative study D. P. Tripathi Assistant Librarian Biju Patnaik Central Library NIT Rourkela [email protected] Designing dynamic and

More information

JoomDOC Documentation

JoomDOC Documentation JoomDOC Documentation Pavel Macháček Jiří Trumpeš Copyright 2014 - ARTIO International Co. JoomDOC Documentation ARTIO Publication date: 26.5.2014 Version: 1.0.3 Abstract User documentation for JoomDOC

More information

cpanel 11 User Manual

cpanel 11 User Manual cpanel 11 User Manual Table Of Contents README FIRST...1 README FIRST...1 Common Questions...3 Common Questions...3 Advanced...9 Apache Handlers...9 Cron Jobs...10 Error Pages...12 Frontpage Extensions...14

More information

Hypercosm. Studio. www.hypercosm.com

Hypercosm. Studio. www.hypercosm.com Hypercosm Studio www.hypercosm.com Hypercosm Studio Guide 3 Revision: November 2005 Copyright 2005 Hypercosm LLC All rights reserved. Hypercosm, OMAR, Hypercosm 3D Player, and Hypercosm Studio are trademarks

More information

Zend Server 4.0 Beta 2 Release Announcement What s new in Zend Server 4.0 Beta 2 Updates and Improvements Resolved Issues Installation Issues

Zend Server 4.0 Beta 2 Release Announcement What s new in Zend Server 4.0 Beta 2 Updates and Improvements Resolved Issues Installation Issues Zend Server 4.0 Beta 2 Release Announcement Thank you for your participation in the Zend Server 4.0 beta program. Your involvement will help us ensure we best address your needs and deliver even higher

More information

JOOMLA 2.5 MANUAL WEBSITEDESIGN.CO.ZA

JOOMLA 2.5 MANUAL WEBSITEDESIGN.CO.ZA JOOMLA 2.5 MANUAL WEBSITEDESIGN.CO.ZA All information presented in the document has been acquired from http://docs.joomla.org to assist you with your website 1 JOOMLA 2.5 MANUAL WEBSITEDESIGN.CO.ZA BACK

More information

Tonido Cloud Admin Guide

Tonido Cloud Admin Guide CODELATHE LLC Tonido Cloud Admin Guide Installing and Managing Tonido Cloud CodeLathe LLC 10/27/2012 (c) CodeLathe LLC 2012. All Rights Reserved Contents 1. Introduction... 3 2. Pre-Requisites... 3 3.

More information

Getting Started with AWS. Hosting a Static Website

Getting Started with AWS. Hosting a Static Website Getting Started with AWS Hosting a Static Website Getting Started with AWS: Hosting a Static Website Copyright 2016 Amazon Web Services, Inc. and/or its affiliates. All rights reserved. Amazon's trademarks

More information

Quick Start Guide Mobile Entrée 4

Quick Start Guide Mobile Entrée 4 Table of Contents Table of Contents... 1 Installation... 2 Obtaining the Installer... 2 Installation Using the Installer... 2 Site Configuration... 2 Feature Activation... 2 Definition of a Mobile Application

More information

WebSpy Vantage Ultimate 2.2 Web Module Administrators Guide

WebSpy Vantage Ultimate 2.2 Web Module Administrators Guide WebSpy Vantage Ultimate 2.2 Web Module Administrators Guide This document is intended to help you get started using WebSpy Vantage Ultimate and the Web Module. For more detailed information, please see

More information

Create e-commerce website Opencart. Prepared by : Reth Chantharoth Facebook : https://www.facebook.com/tharothchan.ubee E-mail : rtharoth@yahoo.

Create e-commerce website Opencart. Prepared by : Reth Chantharoth Facebook : https://www.facebook.com/tharothchan.ubee E-mail : rtharoth@yahoo. Create e-commerce website Opencart Prepared by : Reth Chantharoth Facebook : https://www.facebook.com/tharothchan.ubee E-mail : [email protected] Create e-commerce website Opencart What is opencart? Opencart

More information

Kentico CMS 7.0 Intranet Administrator's Guide

Kentico CMS 7.0 Intranet Administrator's Guide Kentico CMS 7.0 Intranet Administrator's Guide 2 Kentico CMS 7.0 Intranet Administrator's Guide Table of Contents Introduction 5... 5 About this guide Getting started 7... 7 Installation... 11 Accessing

More information

FileWay Administrator s Guide. Version 4

FileWay Administrator s Guide. Version 4 FileWay Administrator s Guide Version 4 1 FileWay Administrator's Guide Copyright (c) 2003-2015 Everywhere Networks Corporation, All rights reserved. Complying with all applicable copyright laws is the

More information

Installation & User Guide

Installation & User Guide SharePoint List Filter Plus Web Part Installation & User Guide Copyright 2005-2009 KWizCom Corporation. All rights reserved. Company Headquarters P.O. Box #38514 North York, Ontario M2K 2Y5 Canada E-mail:

More information

I. Delivery E-mail: Flash CMS template package... 2. II. Flash CMS template installation... 4. III. Control Panel setup... 5

I. Delivery E-mail: Flash CMS template package... 2. II. Flash CMS template installation... 4. III. Control Panel setup... 5 Contents I. Delivery E-mail: Flash CMS template package... 2 II. Flash CMS template installation... 4 III. Control Panel setup... 5 IV. Control Panel activation... 6 Appendix 1: Switching to binary file

More information

About us. Proximity 2015

About us. Proximity 2015 About us Agenda What is open source? PHP for open source PHP on IBM i Live install of Zend Server Live install of Zend DBi What are Zend Server applications? Hands-on install from a.zpk Hands-on install

More information

Web Ambassador Training on the CMS

Web Ambassador Training on the CMS Web Ambassador Training on the CMS Learning Objectives Upon completion of this training, participants will be able to: Describe what is a CMS and how to login Upload files and images Organize content Create

More information

nopcommerce User Guide

nopcommerce User Guide nopcommerce User Guide Open source ecommerce solution Version 1.90 Copyright Notice Information in this document, including URL and other Internet Web site references, is subject to change without notice.

More information

Opigno LMS V1.13 User Manual. Opigno. LMS made simple. User Manual

Opigno LMS V1.13 User Manual. Opigno. LMS made simple. User Manual Opigno LMS made simple Opigno is a Learning Management System based on Drupal. It has been design to be fully integrated with any Drupal platform (website, intranet, extranet, ) and offer a maximum flexibility

More information

Document History Revision 5.0.2 Date: October 30, 2006

Document History Revision 5.0.2 Date: October 30, 2006 vtiger CRM 5.0.2 Installation Manual (For Wiindows OS) Document History Revision 5.0.2 Date: October 30, 2006 - 2 - Table of Contents 1. System Requirements...3 2. How do I choose right distribution?...4

More information

Getting Started With Author-it

Getting Started With Author-it Getting Started With Author-it Prepared 2 February, 2012 Copyright 1996-2011 Author-it Software Corporation Ltd. All rights reserved Due to continued product development, this information may change without

More information

ez Agent Administrator s Guide

ez Agent Administrator s Guide ez Agent Administrator s Guide Copyright This document is protected by the United States copyright laws, and is proprietary to Zscaler Inc. Copying, reproducing, integrating, translating, modifying, enhancing,

More information

OU Campus Web Content Management

OU Campus Web Content Management DRAFT DRAFT DRAFT DRAFT DRAFT DRAFT DRAFT DRAFT DRAFT DRAFT DRAFT DRAFT DRAFT DRAFT DRAFT DRAFT DRAFT OU Campus Web Content Management Table of Contents OU Campus Web Content Management... 1 Introduction

More information

OxyClassifieds Installation Handbook

OxyClassifieds Installation Handbook OxyClassifieds Installation Handbook OxyClassifieds Team Email: [email protected] Web: http://www.oxyclassifieds.com OxyClassifieds Installation Handbook by OxyClassifieds Team Copyright 2006-2011

More information

About This Document 3. Integration Overview 4. Prerequisites and Requirements 6

About This Document 3. Integration Overview 4. Prerequisites and Requirements 6 Contents About This Document 3 Integration Overview 4 Prerequisites and Requirements 6 Meeting the Requirements of the cpanel Plugin... 6 Meeting the Requirements of Presence Builder Standalone... 6 Installation

More information

Quick Reference / Install Guide v1.3

Quick Reference / Install Guide v1.3 v1.3 Table of Contents License... 2 Supported Languages Disclaimer... 2 Roadmap... 2 Wordpress Support... 2 Twitter, Facebook / Social Media Support... 2 Installation... 3 Installation requirements...

More information

FileMaker Server 9. Custom Web Publishing with PHP

FileMaker Server 9. Custom Web Publishing with PHP FileMaker Server 9 Custom Web Publishing with PHP 2007 FileMaker, Inc. All Rights Reserved. FileMaker, Inc. 5201 Patrick Henry Drive Santa Clara, California 95054 FileMaker is a trademark of FileMaker,

More information

Site Store Pro. INSTALLATION GUIDE WPCartPro Wordpress Plugin Version

Site Store Pro. INSTALLATION GUIDE WPCartPro Wordpress Plugin Version Site Store Pro INSTALLATION GUIDE WPCartPro Wordpress Plugin Version WPCARTPRO INTRODUCTION 2 SYSTEM REQUIREMENTS 4 DOWNLOAD YOUR WPCARTPRO VERSION 5 EXTRACT THE FOLDERS FROM THE ZIP FILE TO A DIRECTORY

More information

Bubble Full Page Cache for Magento

Bubble Full Page Cache for Magento User Guide Author: Version: Website: Support: Johann Reinke 2.0 http://www.bubbleshop.net [email protected] Table of Contents 1 Introducing Bubble Full Page Cache... 3 1.1 Features... 3 1.2 Compatibility...

More information

Module Google Rich Snippets + Product Ratings and Reviews

Module Google Rich Snippets + Product Ratings and Reviews Module Google Rich Snippets + Product Ratings and Reviews Date : June 3 th, 2014 Business Tech Installation Service If you need help installing and configuring your module, we can offer you an installation

More information

ConvincingMail.com Email Marketing Solution Manual. Contents

ConvincingMail.com Email Marketing Solution Manual. Contents 1 ConvincingMail.com Email Marketing Solution Manual Contents Overview 3 Welcome to ConvincingMail World 3 System Requirements 3 Server Requirements 3 Client Requirements 3 Edition differences 3 Which

More information

Eucalyptus 3.4.2 User Console Guide

Eucalyptus 3.4.2 User Console Guide Eucalyptus 3.4.2 User Console Guide 2014-02-23 Eucalyptus Systems Eucalyptus Contents 2 Contents User Console Overview...4 Install the Eucalyptus User Console...5 Install on Centos / RHEL 6.3...5 Configure

More information

Version 1.0.0 USER GUIDE

Version 1.0.0 USER GUIDE Magento Extension Grid Manager Version 1.0.0 USER GUIDE Last update: Aug 13 th, 2013 DragonFroot.com Grid Manager v1-0 Content 1. Introduction 2. Installation 3. Configuration 4. Troubleshooting 5. Contact

More information

How to use PDFlib products with PHP

How to use PDFlib products with PHP How to use PDFlib products with PHP Last change: July 13, 2011 Latest PDFlib version covered in this document: 8.0.3 Latest version of this document available at: www.pdflib.com/developer/technical-documentation

More information

JTouch Mobile Extension for Joomla! User Guide

JTouch Mobile Extension for Joomla! User Guide JTouch Mobile Extension for Joomla! User Guide A Mobilization Plugin & Touch Friendly Template for Joomla! 2.5 Author: Huy Nguyen Co- Author: John Nguyen ABSTRACT The JTouch Mobile extension was developed

More information

ultimo theme Update Guide Copyright 2012-2014 Infortis All rights reserved

ultimo theme Update Guide Copyright 2012-2014 Infortis All rights reserved ultimo theme Update Guide Copyright 2012-2014 Infortis All rights reserved 1 1. Important changes Here you can find description of the most important changes in selected versions. List of all changes in

More information

BT CONTENT SHOWCASE. JOOMLA EXTENSION User guide Version 2.1. Copyright 2013 Bowthemes Inc. [email protected]

BT CONTENT SHOWCASE. JOOMLA EXTENSION User guide Version 2.1. Copyright 2013 Bowthemes Inc. support@bowthemes.com BT CONTENT SHOWCASE JOOMLA EXTENSION User guide Version 2.1 Copyright 2013 Bowthemes Inc. [email protected] 1 Table of Contents Introduction...2 Installing and Upgrading...4 System Requirement...4

More information

Sitecore InDesign Connector 1.1

Sitecore InDesign Connector 1.1 Sitecore Adaptive Print Studio Sitecore InDesign Connector 1.1 - User Manual, October 2, 2012 Sitecore InDesign Connector 1.1 User Manual Creating InDesign Documents with Sitecore CMS User Manual Page

More information

PHP on IBM i: What s New with Zend Server 5 for IBM i

PHP on IBM i: What s New with Zend Server 5 for IBM i PHP on IBM i: What s New with Zend Server 5 for IBM i Mike Pavlak Solutions Consultant [email protected] (815) 722 3454 Function Junction Audience Used PHP in Zend Core/Platform New to Zend PHP Looking to

More information

TECHNICAL DOCUMENTATION SPECOPS DEPLOY / APP 4.7 DOCUMENTATION

TECHNICAL DOCUMENTATION SPECOPS DEPLOY / APP 4.7 DOCUMENTATION TECHNICAL DOCUMENTATION SPECOPS DEPLOY / APP 4.7 DOCUMENTATION Contents 1. Getting Started... 4 1.1 Specops Deploy Supported Configurations... 4 2. Specops Deploy and Active Directory...5 3. Specops Deploy

More information

OpenCart AliExpress Retail Plugin User Guide

OpenCart AliExpress Retail Plugin User Guide OpenCart AliExpress Retail Plugin User Guide By Lim Tee Chert 30 April 2015 (last updated on: 15 Oct 2015) http://www.aliwebstore.com Purpose: The purpose of this Opencart AliExpress Retail Plugin is to

More information

Install guide for Websphere 7.0

Install guide for Websphere 7.0 DOCUMENTATION Install guide for Websphere 7.0 Jahia EE v6.6.1.0 Jahia s next-generation, open source CMS stems from a widely acknowledged vision of enterprise application convergence web, document, search,

More information

ekomimeetsmage Manual for version 1.0.0, 1.1.0, 1.2.0, 1.3.0, 1.4.0

ekomimeetsmage Manual for version 1.0.0, 1.1.0, 1.2.0, 1.3.0, 1.4.0 ekomimeetsmage Manual for version 1.0.0, 1.1.0, 1.2.0, 1.3.0, 1.4.0 Version 0.6 Last edit: 16.05.2013 Overview 1 Introduction...3 1.1 Requirements...3 1.2 Function Overview...3 2 Installation...3 2.1 Important

More information

Attix5 Pro Server Edition

Attix5 Pro Server Edition Attix5 Pro Server Edition V7.0.3 User Manual for Linux and Unix operating systems Your guide to protecting data with Attix5 Pro Server Edition. Copyright notice and proprietary information All rights reserved.

More information