Вы находитесь на странице: 1из 316

1. Pimcore Version 3.x Documentation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

3
1.1 Installation and Upgrade . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
1.1.1 System Requirements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
1.1.2 Installation (Apache) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
1.1.3 Nginx Configuration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
1.1.4 Quick Start Package / Sample Data / Boilerplate Installation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
1.1.5 Upgrade Notes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
1.1.6 Version History . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
1.2 Develop with pimcore . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
1.2.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
1.2.2 Documents . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
1.2.2.1 Quick Start Guide . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
1.2.2.2 Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
1.2.2.3 Controllers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
1.2.2.4 Templates (Views) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
1.2.2.4.1 Editables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
1.2.2.4.2 Helpers (Available View Methods) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70
1.2.2.5 Document-Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72
1.2.2.6 Thumbnails . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73
1.2.2.7 Glossary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81
1.2.2.8 Redirects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82
1.2.2.9 Document Lists . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83
1.2.2.10 Localize your Documents (i18n) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84
1.2.2.10.1 Translation of Document Editables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 86
1.2.2.10.2 Website Translations / Shared Translations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 86
1.2.2.11 Navigation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88
1.2.2.12 Document Tree . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 96
1.2.2.12.1 Copy documents and rewrite relations to new documents . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 96
1.2.2.12.2 Hardlinks for documents . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97
1.2.3 Assets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97
1.2.3.1 Asset Lists . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97
1.2.3.2 Custom Settings (Properties) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97
1.2.3.3 Image Thumbnails . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 98
1.2.3.4 Video Thumbnails . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 98
1.2.3.5 Asset Document Thumbnails (PDF, docx, odf, ...) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 100
1.2.3.6 Asset (Localized) Metadata . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101
1.2.4 Data Objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102
1.2.4.1 Object Classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 103
1.2.4.1.1 Data Fields . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 104
1.2.4.1.2 Layout Elements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 152
1.2.4.2 Object Lists . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 153
1.2.4.3 External System Interaction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 160
1.2.4.4 Inheritance . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 161
1.2.4.5 Custom Icons . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 163
1.2.4.6 Locking fields . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 164
1.2.4.7 Object Variants . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 164
1.2.4.8 Object Preview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 167
1.2.4.9 Object Tree . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 168
1.2.4.9.1 Custom icons and style in object-tree (since 1.4.2) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 168
1.2.4.10 Custom Layouts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 169
1.2.5 Reports & Marketing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 171
1.2.5.1 Google Analytics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 171
1.2.5.2 Setup Google Analytics Reporting with OAuth2 Service Accounts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 173
1.2.6 General . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 175
1.2.6.1 Building URL's . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 176
1.2.6.2 Cache . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 177
1.2.6.2.1 Custom Cache Backends . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 180
1.2.6.2.2 Output-Cache . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 183
1.2.6.3 Custom Reports (previously SQL Reports) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 186
1.2.6.4 Custom Routes (Static Routes) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 188
1.2.6.5 Extending pimcore . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 192
1.2.6.5.1 Class-Mappings - Overwrite pimcore models . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 192
1.2.6.5.2 Custom persistent models . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 195
1.2.6.5.3 Event API (EventManager) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 200
1.2.6.5.4 Events . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 204
1.2.6.5.5 Extend pimcore using composer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 209
1.2.6.5.6 Hook into the startup-process . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 210
1.2.6.6 Google Custom Search Engine (Site Search ~ CSE) Integration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 213
1.2.6.7 Logging . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 219
1.2.6.8 Magic Parameters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 221
1.2.6.9 Newsletter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 221
1.2.6.10 Notes & Events . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 233
1.2.6.11 Placeholders . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 233
1.2.6.11.1 Object . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 236
1.2.6.11.2 Text . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 237
1.2.6.12 Properties . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 237
1.2.6.12.1 Predefined Properties . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 238
1.2.6.13 Static Helpers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 239
1.2.6.14 System Settings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 240
1.2.6.14.1 Email . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 241
1.2.6.15 Tag & Snippet Management . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 241
1.2.6.16 Targeting & Personalization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 242
1.2.6.16.1 Managing Global Targeting Rules and Personas (Target Groups) . . . . . . . . . . . . . . . . . . . . . . . . . . . 243
1.2.6.16.2 Document personalization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 249
1.2.6.16.3 Assign Personas based on visited pages . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 250
1.2.6.16.4 Interacting with the targeting & personalization engine . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 251
1.2.6.16.5 Targeting: Tips for developers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 252
1.2.6.17 UUID Support . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 253
1.2.6.18 Versioning . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 254
1.2.6.19 Website Settings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 255
1.2.6.20 Working with Sites (Multisite) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 256
1.2.6.21 Adaptive Design / Device Helper / Tool . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 257
1.2.6.22 Application Logger . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 258
1.2.6.23 Reference: Database Structure, Fields, Relations - "How are objects stored?" . . . . . . . . . . . . . . . . . . . . . . 259
1.2.6.24 Console / CLI . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 264
1.2.7 Web Services . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 266
1.2.7.1 REST Webservice . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 267
1.2.8 Localization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 273
1.2.9 Outputfilters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 273
1.2.9.1 LESS (CSS Compiler) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 274
1.2.9.1.1 Install lessc on your server (Debian) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 274
1.2.10 Mailing Framework . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 276
1.2.10.1 Pimcore\Mail Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 277
1.2.11 Best Practices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 278
1.2.11.1 CLI Script for Object Import . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 279
1.2.11.2 Extending the Pimcore User with User - Object Relation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 280
1.2.11.3 High Traffic Server Setup (*nix based Environment) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 284
1.2.11.4 Write-only database connection . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 285
1.3 Administrator's Guide . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 286
1.3.1 Backups . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 286
1.3.2 Commandline Interface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 287
1.3.2.1 Backend Search Reindex . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 287
1.3.2.2 Cache Warming . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 287
1.3.2.3 Command-line Backup . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 287
1.3.2.4 Image & Video Thumbnail Generator . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 288
1.3.2.5 MySQL Tools . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 288
1.3.3 Install Plugins . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 289
1.3.4 Setting up WebDav . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 289
1.3.5 Translations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 290
1.3.6 User Permissions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 290
1.4 User's Guide . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 292
1.4.1 Backend Search and Properties . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 292
1.4.2 Keyboard Shortcuts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 293
1.4.3 Working with WebDAV . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 293
1.4.3.1 BitKinex as WebDAV Client . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 294
1.4.3.2 Cyberduck as WebDAV Client . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 296
1.4.3.3 NetDrive as WebDAV Client . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 297
1.4.3.4 Windows Explorer as WebDAV Client . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 298
1.5 Extensions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 298
1.5.1 Deeplinks into the pimcore Admin Interface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 298
1.5.2 Extension management using Composer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 299
1.5.3 Extension Manager Hooks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 301
1.5.4 Plugin Developer's Guide . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 302
1.5.4.1 Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 302
1.5.4.2 Plugin Anatomy and Design . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 309
1.5.4.3 Plugin Backend . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 312
1.5.4.4 UI Development and JS Hooks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 313
1.5.4.5 Useful Hints . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 314
1.5.4.6 Adding custom main-navigation item . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 315
1.6 FAQ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 316
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
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
Email
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
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
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 https://www.pimcore.org/download/pimcore-latest.zip
# OR curl -O https://www.pimcore.org/download/pimcore-latest.zip
unzip pimcore-latest.zip
Install via Composer

cd /your/working/directory
curl -sS https://getcomposer.org/installer | 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:

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 http://(your 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

offically support only for Apache (others may work, but not tested), nginx see here.
mod_rewrite
.htaccess (AllowOverride All)

PHP

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)

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: http://www.pimcore.org/forum/discussion/comment/2009/

* 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 f
irst.

Download & copy to server

First of all download the latest release or the sample data package.

Extract the files and put it into the document-root directory (pimcore doesn't work in subdirectories) on your server. (If you want to know why,
read more here)

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 /plugins d
irectory.

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:

/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 http://www.your-domain.
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.

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 v1.4.10 (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 "${getassets}A"; }
if ($request_method = GET) { set $getassets "${getassets}B"; }
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)).*|/webs
ite/var/(?!tmp|assets|plugins|areas)|^.*modules/.*/static.*) {
return 403;
}

# basic zend-framework setup see:


http://framework.zend.com/manual/en/zend.controller.html
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 127.0.0.1:9000


location ~ \.php$ {
# Zero-day exploit defense.
# http://forum.nginx.org/read.php?2,88845,page=3
# Won't work properly (404 error) if the file is not stored on this
server, which 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 127.0.0.1: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;
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


1. Download the sample data package from our download page
2. Extract the ZIP package to your document root
3. 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:

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
+---------+-------+---------------------+
| Version | Build | Date |
+---------+-------+---------------------+
| 1.0.3 | 11 | 15th March 2010 |
| 1.0.4 | 47 | 17th March 2010 |
| 1.0.5 | 102 | 10th April 2010 |
| 1.0.6 | 268 | 14th May 2010 |
| 1.0.7 | 274 | 14th May 2010 |
| 1.0.8 | 351 | 27th May 2010 |
| 1.1.0 | 468 | 30th July 2010 |
| 1.1.1 | 491 | 7th August 2010 |
| 1.2.0 | 618 | 20th September 2010 |
| 1.2.1 | 688 | 9th October 2010 |
| 1.2.2 | 757 | 17th November 2010 |
| 1.3.0 | 835 | 14th January 2011 |
| 1.3.1 | 899 | 28th January 2011 |
| 1.3.2 | 951 | 12th February 2011 |
| 1.4.0 | 1154 | 18th May 2011 |
| 1.4.1 | 1200 | 17th June 2011 |
| 1.4.2 | 1500 | 15th October 2011 |
| 1.4.3 | 1750 | 12th February 2012 |
| 1.4.4 | 1780 | 20th February 2012 |
| 1.4.5 | 1953 | 16th April 2012 |
| 1.4.6 | 2082 | 2nd July 2012 |
| 1.4.7 | 2172 | 20th August 2012 |
| 1.4.8 | 2330 | 26th October 2012 |
| 1.4.9 | 2560 | 2nd March 2013 |
| 1.4.10 | 2635 | 9th April 2013 |
| 2.0 | 2972 | 5th November 2013 |
| 2.0.1 | 3007 | 20th December 2013 |
| 2.0.2 | 3042 | 7th January 2014 |
| 2.1.0 | 3103 | 28th February 2014 |
| 2.2.0 | 3159 | 11th April 2014 |
| 2.2.1 | 3190 | 13th May 2014 |
| 2.2.2 | 3230 | 2nd July 2014 |
| 2.3.0 | 3270 | 1st September 2014 |
| 3.0.0 | 3372 | 15th January 2015 |
| 3.0.1 | 3375 | 16th January 2015 |
| 3.0.2 | 3380 | 16th January 2015 |
| 3.0.3 | 3400 | 28th January 2015 |
| 3.0.4 | 3444 | 4th March 2015 |
| 3.0.5 | 3450 | 6th March 2015 |
| 3.0.6 | 3496 | 29th May 2015 |
| 3.1.0 | 3542 | 14th September 2015 |
| 3.1.1 | 3543 | 16th September 2015 |
+---------+-------+---------------------+

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
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
Email
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 http://en.wikipedia.org/wiki/Model%E2%80%93
view%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 http://framework.zend.com/manual/en/zend.controller.html 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.

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. http://example.com/some/content).
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 The controllers directory, the naming follows the Zend ContentController.php
Framework naming-convention

/website/views/scripts The template directory, the naming (subfolders) follows also the /website/views/scripts/content/view-single.php
naming-convention of ZF (if the above controller were to contain an
(/website/views/scripts/[controller]/[action].php) action called viewSingleAction)

/website/views/layouts Optionally: here you can put your layouts which are used by layout.php (this is the default when enabled)
pages

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

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 Type 2 Type 3


Only Controller/Action Controller/Action and Template Only Template
If you have a controller named content with an The specified controller/action is executed, but The default controller/action defined in the system
action named defaultAction the template file then the specified template is rendered settings are executed, then the specified template
required will be /website/views/scripts/content/def is rendered.
ault.php

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 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

Header Often there are header images on a website, if you don't want to define it for every page, you can use the properties,
Images because of their inheritance you can define a default one at the root document, and overrule this in a deeper document.

Sidebars See header images

SEO 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 ezCompo
nents, 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_Vie
w 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 doc
uments-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.

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:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"


"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<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:
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
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\Email
Used to send Emails with Pimcore\Mail, they are similar to Document\Snippets and the have additional settings for email receivers.

About Document\Email

The Document\Email extends the Document\Snippet and can be used to send emails

The settings tab contains a group of email related properties, i.e. Subject, From, To, CC, BCC

If the email logging isn't deactivated by the Pimcore\Mail Class you can see the the sent emails in the "Sent Emails" Tab.
(The Column "Dynamic params" is hidden by default so you have to unhide it manually)

Dynamic Parameters passed when email was sent:


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 Pimcore\Co
ntroller\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 With this method you can set the default document for the current action which will be
$document 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


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 Description

inc Use this function to directly include a document

template Use this method to include a template

cache In template caching

translate i18n / translations

glossary 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 Description

action http://framework.zend.com/manual/en/zend.view.helpers.html#zend.view.helpers.initial.action

headMeta http://framework.zend.com/manual/en/zend.view.helpers.html#zend.view.helpers.initial.headmeta

headTitle http://framework.zend.com/manual/en/zend.view.helpers.html#zend.view.helpers.initial.headtitle

translate http://framework.zend.com/manual/en/zend.view.helpers.html#zend.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
Block
Checkbox
Date
Href (1 to 1 Relation)
Image
Input
Link
Multihref
Multiselect
Numeric
PDF (Document)
Renderlet
Select
Snippet (embed)
Table
Textarea
Video
WYSIWYG

General
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(/http:.*/)){
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.
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:

<?= $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


//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

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 Description

renderIndex() renders only one specific block within the areablock

getCount() total count of blocks

getCurrent() current number of block (useful within area bricks)

getCurrentIndex() get the current index (index is different from position, as you can move block around)

getElement() 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(); ?>

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.php a
nd 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 pimcore
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="http://framework.zend.com/xml/zend-config-xml/1.0/">
<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:
<?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.
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 Description

$this->brick->getId() returns the id of the current brick

$this->brick->getConfig() returns the configuration (Zend_Config) out of area.xml (to get your custom properties, ... )

$this->brick->getIndex() returns the current index inside the areablock

$this->brick->getPath() 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

<?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

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: https://github.com/pimcore/pimcore/tree/master/website_demo/views/areas
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 Description

getCount() Get the total amount of iterations

getCurrent() Get the current index while looping

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:


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:

<?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

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

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

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\Thumbnail get a specific thumbnail of the image

getText() / getAlt() - string, alt/title text from the image The entered alternative text in the widget

getSrc() - string, absolute path to the image 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(
"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__auto_xxxxxxxx@2x.png"
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
]) ?>

// custom drop targets


<div class="myCustomImageDropTarget anotherClass">My first alternative drop
target</div>
<?= $this->image("image", array(
"thumbnail" => "contentfullimage",
"dropClass" => "myCustomImageDropTarget"
)) ?>
<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.

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>
<?= $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 } ?>
</p>
</div>

... and here is the output:


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
// 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', 'email' 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(){

// 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"
});
});

</script>
<?php endif ?>

<?= $this->input("headline", array("vtype"=>"starts_with_number",


"vtypeText"=>"This field is invalid")); ?>

From version 1.4.2 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

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 http://

Example linklist:

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

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.

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
// 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

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

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-field
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

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

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: https://developers.google.com/youtube/player_pa


rameters - 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) string, absolute path to get a specific image thumbnail of the video, or a thumbnail of the poster image (if assigned)
$name the thumbnail

getVideoType() - string, type of the video this is to check which video type is assigned
(asset|youtube|vimeo|url)

getVideoAsset() - asset returns the video asset object if assigned, otherwise null

getThumbnail() (string/array) array, absolute paths to get a specific video-thumbnail of the video
$name the different video
thumbnails Return Value:

[status] => finished


[formats] => Array
(
[mp4] =>
/website/var/tmp/video_3414__example.mp4
[webm] =>
/website/var/tmp/video_3414__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)


<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<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"]
)); ?>

<?php if (!$this->editmode) { ?>


<script type="text/javascript">
$('video,audio').mediaelementplayer(/* Options */);
</script>
<?php } ?>

</body>
</html>

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>

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

<?= $this->wysiwyg("content", [
"toolbarGroups" => [
[
"name" => 'basicstyles',
"groups" => [ 'basicstyles', 'list', "links"]
]
]
]); ?>

More examples and config options for the toolbar and toolbarGroups can be found at http://nightly.ckeditor.com/13-02-21-09-53/basic/samples/pl
ugins/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
<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])

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.
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:
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:

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.

SCALE BY The image is scaled respecting the ratio to the given height, the width is
HEIGHT 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.

ROTATE Rotates the image with the given angle. The background is transparent
by default.

BACKGROUND Background color is especially useful if you have transparent PNG as


COLOR 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.

ROUNDED Rounds the corners to the given width/height.


CORNERS

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,
"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"));
?>

Advanced Examples

Since version 1.5.0 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(["disableWidthHeightAtt
ributes" => 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: Ad
obe 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

pimcore auto-generates a thumbnail if it doesn't exist and is directly called (not using any of the getThumbnail() methods).

For example: Call http://example.com/website/var/tmp/image-thumbnails/0/6644/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

<?= $this->image("myImage", array("thumbnail" => "contentimages")); ?>

this will create the following output:

<img src="/website/var/tmp/thumb_6644__contentimages@2x.png" 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__testimage@2x.png

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__teaserportal@2x.png

/website/var/tmp/thumb_6644__testimage@5x.png

Using float is possible too:

/website/var/tmp/thumb_123456__teaserportal@3.2x.png

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")-
>getMedias()));

// 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 including @srcset 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:
<?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&nbsp; 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="http://www.php.net/">download</a> 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)

Example

http://www.pimcore.org/?test=test&urlencoded=test\%20value

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: http://framework.zend.com/manual/en/zend.db.adapter.html#zend.db.adap
ter.select.fetchall

// 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:

// 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

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

<div>
<address>&copy; <?= $this->translate("copyright") ?></address>
<a href="/imprint"><?= $this->translate("imprint") ?></a>
<a href="/legal"><?= $this->translate("legal_notice") ?></a>
</div>

Shorthands
<div>
<address>&copy; <?= $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:
<!-- META NAVIGATION - ONLY 1st LEVEL -->
<?php
echo $mainNavigation->menu()->renderMenu($navigation, array("maxDepth"
=> 0));
?>

...

<!-- BREADCRUMBS -->


<div>
<a href="/">Home</a> &gt;
<?= $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)

#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)

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')->rende
r();
?>

/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:

$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:

<?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 /* @var $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>

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:

<?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
));

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 thumbnai
ls.

Other kinds of common assets are PDF or MS Word documents which people can download from the website.
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): http://johnvansickle.com/ffmpeg/ (at least Version 2.0.1)
Windows builds: http://ffmpeg.zeranoe.com/builds/

Install FFMPEG and qt-faststart (on Debian 64bit)

Run all this commands as root

cd ~
wget http://FFMPEG-ARCHIVE-URL-FROM-ABOVE -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


$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

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");

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.

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\Checkbox 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\Fieldcollections A collection of fields

geopoint Pimcore\Model\Object\Class\Data\Geopoint google maps widget to find longitude/latitude

geobounds Pimcore\Model\Object\Class\Data\Geobounds google maps widget to define geographical bounds

geopolygon Pimcore\Model\Object\Class\Data\Geopolygon 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 Pimcore\Model\Object\Class\Data\KeyValue key/value pairs

language Pimcore\Model\Object\Class\Data\Language 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 Pimcore\Model\Object\Class\Data\Multiselect combo box with multiple select

nonownerobjects Pimcore\Model\Object\Class\Data\Nonownerobjects 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\Password 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

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::getObjectsReferencingUser($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.

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
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 = 2.2008440814678;
$latitude = 102.25112915039;
$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 snippet
shows how to set Geobounds:

use Pimcore\Model\Object\Data\Geopoint;

$northEast = new Geopoint(150.96588134765625, -33.704920213014425);


$southWest = new Geopoint(150.60333251953125, -33.893217379440884)
$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(150.54428100585938, -33.464671118242684),
new Geopoint(150.73654174804688, -33.913733814316245),
new Geopoint(151.2542724609375, -33.9946115848146)
);
$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 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

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.
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


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.
If not already configured, please specify the valid languages for your website. You can do this in Settings -> System -> General
Then the result in your object editor will look like this:
Now pimcore generates automatically the input widgets for every configured language.
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:

// 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 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
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.
$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.


$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] => 3.8922155688623
[left] => 48.076923076923
[width] => 8.3333333333333
[height] => 48.802395209581
)

[1] => Array


(
[name] => hotspot2
[top] => 8.9820359281437
[left] => 70.897435897436
[width] => 14.230769230769
[height] => 44.011976047904
)

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:


Array
(
[0] => Array
(
[top] => 3.8922155688623
[left] => 48.076923076923
)

[1] => Array


(
[top] => 8.9820359281437
[left] => 70.897435897436
)

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

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("http://www.pimcore.org");
$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


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:

$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.
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.
Click on the “Magnifier” class if you want the key to be part of a group.
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.
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.

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();

$keyconfig1 = Object\KeyValue\KeyConfig::getByName(“keyconfig”); //
look up the key config by name and retrieve the id
$keyid1 = $keyconfig->getId();

$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("19140601_(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")));

Structured Data Fields - Classification Store

EXPERIMENTAL, since 3.0.7 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
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

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

// 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

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.
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.

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.
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.

//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:

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

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:

$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
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 http://nightly.ckeditor.com/13-02-21-09-53/basic/samples/pl
ugins/toolbar/toolbar.html

Please refer to the CKeditor 4.0 Documentation

Video Field

Code Example

<?php
$object = Object::getById(1234);
print_r($object->getMyVideo());
?>

Will produce the following output depending on the content:


# ASSET VIDEO

Pimcore\Model\Object\Data\Video Object
(
[type] => asset
[data] => Pimcore\Model\Asset\Video Object
(
[type] => video
[id] => 27
...
)

[poster] => Pimcore\Model\Asset\Image Object


(
[type] => image
[id] => 284
...
)

[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] => 11696823
[poster] =>
[title] => My Title
[description] => My Description
)

Display Video using document's video tag


<?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

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:

$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: http://framework.zend.com/manual/en/zend.db.adapter.html#zend.db.adap
ter.select.fetchall

$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.

$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

$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)


<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]);
?>">&lt; Previous</a> |
<?php else: ?>
<span class="disabled">&lt; 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
&gt;</a> |
<?php else: ?>
| <span class="disabled">Next &gt;</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; ?>
&nbsp; Page <?= $this->current; ?> of <?= $this->last; ?>
</div>
</div>

Access and modify internal object list query

Since rev. 3528 (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'
, ''
);

Related Forum Topics

1. Get objects by relation


2. 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 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

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".
Icons that come along with pimcore by default can be found in PIMCORE_DOCUMENT_ROOT/pimcore/static/img/icon.

Here you can find an overview.

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.

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.
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.

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_867655 (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)

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
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.
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.
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:


<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','//www.google-analytics.com/analytics.js','ga'
);
var foo = 'beforeInit';
ga('create', 'UA-12436865-1');
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 1.4.6 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 https://console.developers.google.com/project/

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:

1. Click API's & auth


2.
2. Click API's
3. Enter 'Analytics' in the search box
4. 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).

1. Click the Credentials Navigation menu


2. Click the 'Create new Client ID' button
3. Select 'Service Account' in the popup menu that appears on
screen.
4. Select P12 as the key type
5. 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:

https://www.google.com/analytics/web/?hl=en#management/Settings

1. Select the correct account to administer


2. Click the 'User Management' navigation option

Add a new Analytics user

From the user management page in Google Analytics, add a new


user:

1. Enter the 'Email' credentials that were created in the API


console page (bottom of screenshot)
2. The example shows us select the 'Edit' level of access for
the user, you can select any option you think appropriate.
3. 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)

1. In Pimcore, click Settings -> System


2. Expand the "Google Credentials & API Keys" section
3. Enter the "Client ID" and the "Email address" in the correct
fields provided by pimcore.
4. 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/config/
google-api-private-key.p12

NOTE: It is recommended to clear all Pimcore cache's at this


point

Configure Reports and Marketing

In Pimcore, go to "Settings" -> "Reports & Marketing" to complete


marketing and reports setup.

Troubleshooting

https://developers.google.com/analytics/devguides/reporting/core/v2/gdataAuthentication#helpme

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
Email
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

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)


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 = "http://www.elements.at/...";
$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");
}
}

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::setForceImmediateWrite(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: http://www.pimcore.org/download?nocache=true

This will disable the entire cache, not only the output-cache, to disable only the output-cache you can add this parameter: ?pimcore_outputfilter
s_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

<?xml version="1.0"?>
<zend-config xmlns:zf="http://framework.zend.com/xml/zend-config-xml/1.0/">
<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="http://framework.zend.com/xml/zend-config-xml/1.0/">
<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="http://framework.zend.com/xml/zend-config-xml/1.0/">
<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.
<?xml version="1.0"?>
<zend-config xmlns:zf="http://framework.zend.com/xml/zend-config-xml/1.0/">
<backend>
<type>\Pimcore\Cache\Backend\Redis2</type>
<custom>true</custom>
<options>
<server>127.0.0.1</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="http://framework.zend.com/xml/zend-config-xml/1.0/">
<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

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).
Enable Tick to generally enable the output-cache.

Lifetime 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.

Exclude You can define some exclude patterns where the cache doesn't affect. The patterns have to be valid regular expressions (including
Patterns delimiters). Type one pattern in each line.

Disbale You can define an additional cookie-name which disables the cache.
Cookie 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):
Unknown macro: {html}

<iframe frameborder="0" width="300" height="60" src="http://www.pimcore.org/download/cache-bookmarklet.htm"></iframe>


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:
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:


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:

<?= $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

<?= $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 1.4.2 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

<?= $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 1.3.2 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:
// 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="http://framework.zend.com/xml/zend-config-xml/1.0/">
<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

$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="http://framework.zend.com/xml/zend-config-xml/1.0/">

<!-- 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.

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.
# 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
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()));
}

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 http://framework.zend.com/manual/1.12/de/zend.event-manager.event-manager.html

Examples

The following example shows how to register events for assets, documents and objects where the event-handler is in a custom class.
<?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)

<?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\Manager\Procedural| - Use this event to register your own maintenance jobs, this event
Pimcore\Model\Schedule\Manager\Daemon 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 3.1.0

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

object.preAdd Pimcore\Model\Object\AbstractObje -
ct

object.postAdd Pimcore\Model\Object\AbstractObje -
ct

object.preUpdate Pimcore\Model\Object\AbstractObje (bool) saveVersionOnly is set if method saveVersion() was called instead of
ct saveVersionOnly save()
object.postUpdate Pimcore\Model\Object\AbstractObje (bool) saveVersionOnl saveVersionOnly is set if method saveVersion() was called instead of
ct y save()

object.preDelete Pimcore\Model\Object\AbstractObje -
ct

object.postDelete Pimcore\Model\Object\AbstractObje -
ct

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 Pimcore\Model\Object\ClassDefinition -

object.class.preUpdate Pimcore\Model\Object\ClassDefinition -

Object KeyValue Group Configuration

Name Target Parameters Description

object.keyValue.groupConfig.preAdd Pimcore\Model\Object\KeyValue\GroupConfig -

object.keyValue.groupConfig.postAdd Pimcore\Model\Object\KeyValue\GroupConfig -

object.keyValue.groupConfig.preUpdate Pimcore\Model\Object\KeyValue\GroupConfig -

object.keyValue.groupConfig.postUpdate Pimcore\Model\Object\KeyValue\GroupConfig -

object.keyValue.groupConfig.preDelete Pimcore\Model\Object\KeyValue\GroupConfig -

object.keyValue.groupConfig.postDelete Pimcore\Model\Object\KeyValue\GroupConfig -

Object KeyValue Key Configuration

Name Target Parameters Description

object.keyValue.keyConfig.preAdd Pimcore\Model\Object\KeyValue\KeyConfig -

object.keyValue.keyConfig.postAdd Pimcore\Model\Object\KeyValue\KeyConfig -

object.keyValue.keyConfig.preUpdate Pimcore\Model\Object\KeyValue\KeyConfig -

object.keyValue.keyConfig.postUpdate Pimcore\Model\Object\KeyValue\KeyConfig -

object.keyValue.keyConfig.preDelete Pimcore\Model\Object\KeyValue\KeyConfig -

object.keyValue.keyConfig.postDelete Pimcore\Model\Object\KeyValue\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_Admin::init() -
only fired once (2.3.1)

admin.controller.postInit Zend_Controller_Action - Fired at the end of Pimcore_Controller_Actio


n_Admin::init() - only fired once (2.3.1)

admin.object.get.preSendData Admin_ObjectController (Pimcore\Model\Tool\Admin\EventDataContainer) Fired at the end of


returnValueContainer, Admin_ObjectController::get() - (since build
(Pimcore\Model\Object\AbstractObject) object 3277)

admin.object.treeGetChildsById.preSendData Admin_ObjectController (Pimcore\Model\Tool\Admin\EventDataContainer) Fired at the end of


returnValueContainer Admin_ObjectController::treeGetChildsById()
- (since build 3277)

admin.class.objectbrickList.preSendData Admin_ClassController (Pimcore\Model\Tool\Admin\EventDataContainer) Fired at the end of


returnValueContainer, (int) objectId Admin_ClassController:objectbrickList() -
(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 http://framework.zend.com/manual/1.12/de/zend.event-manager.event-manager.html

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.

<?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)

<?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\Manager\Procedural|Pimcore\ - Use this event to register your own maintenance jobs, this
Model\Schedule\Manager\Daemon 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 Pimcore\Model\Object\AbstractObject -

object.postAdd Pimcore\Model\Object\AbstractObject -

object.preUpdate Pimcore\Model\Object\AbstractObject -

object.postUpdate Pimcore\Model\Object\AbstractObject -

object.preDelete Pimcore\Model\Object\AbstractObject -

object.postDelete Pimcore\Model\Object\AbstractObject -

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 Pimcore\Model\Object\ClassDefinition -

object.class.preUpdate Pimcore\Model\Object\ClassDefinition -

Object KeyValue Group Configuration

Name Target Parameters Description

object.keyValue.groupConfig.preAdd Pimcore\Model\Object\KeyValue\GroupConfig -

object.keyValue.groupConfig.postAdd Pimcore\Model\Object\KeyValue\GroupConfig -

object.keyValue.groupConfig.preUpdate Pimcore\Model\Object\KeyValue\GroupConfig -

object.keyValue.groupConfig.postUpdate Pimcore\Model\Object\KeyValue\GroupConfig -

object.keyValue.groupConfig.preDelete Pimcore\Model\Object\KeyValue\GroupConfig -

object.keyValue.groupConfig.postDelete Pimcore\Model\Object\KeyValue\GroupConfig -
Object KeyValue Key Configuration

Name Target Parameters Description

object.keyValue.keyConfig.preAdd Pimcore\Model\Object\KeyValue\KeyConfig -

object.keyValue.keyConfig.postAdd Pimcore\Model\Object\KeyValue\KeyConfig -

object.keyValue.keyConfig.preUpdate Pimcore\Model\Object\KeyValue\KeyConfig -

object.keyValue.keyConfig.postUpdate Pimcore\Model\Object\KeyValue\KeyConfig -

object.keyValue.keyConfig.preDelete Pimcore\Model\Object\KeyValue\KeyConfig -

object.keyValue.keyConfig.postDelete Pimcore\Model\Object\KeyValue\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_Admin::init() -
only fired once (2.3.1)

admin.controller.postInit Zend_Controller_Action - Fired at the end of Pimcore_Controller_Actio


n_Admin::init() - only fired once (2.3.1)

admin.object.get.preSendData Admin_ObjectController (Pimcore\Model\Tool\Admin\EventDataContainer) Fired at the end of


returnValueContainer, Admin_ObjectController::get() - (since build
(Pimcore\Model\Object\AbstractObject) object 3277)

admin.object.treeGetChildsById.preSendData Admin_ObjectController (Pimcore\Model\Tool\Admin\EventDataContainer) Fired at the end of


returnValueContainer Admin_ObjectController::treeGetChildsById()
- (since build 3277)

admin.class.objectbrickList.preSendData Admin_ClassController (Pimcore\Model\Tool\Admin\EventDataContainer) Fired at the end of


returnValueContainer, (int) objectId Admin_ClassController:objectbrickList() -
(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:
\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 (http://getcomposer.org/) 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 https://getcomposer.org/installer | 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:


<?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

$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


<?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

Setup in pimcore

Create and configure a new search engine at http://www.google.com/cse/ - for more information please visit: http://support.google.com/customse
arch/

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 https://code.google.com/apis/console/ 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:

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" => "00285971xxxx30885299: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);
}
}
}

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>
</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>
<?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">&lt; Previous</a> |
<?php else: ?>
<span class="disabled">&lt; 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 &gt;</a> |
<?php else: ?>
| <span class="disabled">Next &gt;</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; ?>
&nbsp; 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.

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

2013-07-25T18:26:30+02:00 :
2|admin|page|save|{"task":"publish","id":"4","data":"{\"headTitle\":{\"dat
a\":\"Getting started\",\"..."}

Explanation

Value (from the example above) Description

2013-07-25T18:26:30+02:00 Timestamp

2 User-ID

admin Module (MVC)

page Controller (MVC)

save 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

2013-08-12T19:49:43+02:00 : 10.242.2.255 Source:


/asdsad/redirectsource/asd -> /basic-examples

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

controller/action/template/module You can call a controller/action/template directly without a route eg.: http://www.example.com/?controller=m
y-app&action=my-action&template=my/template.php

pimcore_document | pdid Sometimes it's useful to call a page directly by its ID, you can do that for example with: http://www.example.
com/?pimcore_document=345

nocache Setting this parameter disables every kind of cache. eg.: http://www.example.com/my/page?nocache=1

pimcore_outputfilters_disabled Disables all outputfilters, incl. the output-cache. But this doesn't disable the internal object cache.

pimcore_log 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: /website/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-[NAME].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 Shows the template files which are included as HTML comments.
This parameter only works if DEBUG MODE is on.

pimcore_disable_host_redirect 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 e-mail 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 "Email" 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:
The purpose of the fields gender, firstname, lastname and email 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 email document


// parameters available in the email: gender, firstname,
lastname, email, token, object
// ==> see mailing framework
$newsletter->sendConfirmationMail($user,
Document::getById(3076), array("additional" => "parameters", "key" =>
"value")); // replace "3076" with the ID of the email document you want to
use as confirmation email

// 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("email")) {
$unsubscribeMethod = "email";
$success =
$newsletter->unsubscribeByEmail($this->getParam("email"));
}
if($this->getParam("token")) {
$unsubscribeMethod = "token";
$success =
$newsletter->unsubscribeByToken($this->getParam("token"));
}

$this->view->success = $success;
$this->view->unsubscribeMethod = $unsubscribeMethod;
}
}

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="email">E-Mail</label>
<input id="email" name="email" type="text" value="<?=
$this->getParam("email"); ?>" />

<br />

<input type="submit" name="submit" value="Submit" />


</form>
<?php } else { ?>
<h2>Success, Please check your mailbox!</h2>
<?php } ?>

confirm.php
<?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 == "email") { ?>
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="email">E-Mail</label>
<input id="email" name="email" type="text" />

<br />

<input type="submit" name="submit" value="Submit" />


</form>
<?php } else { ?>
<h2>Unsubscribed</h2>
<?php } ?>

Confirmation E-Mail

The confirmation email (see $newsletter->sendConfirmationMail() above) is a simple email document.

In this document the following placeholders are available:

%Text(firstname);
%Text(lastname);
%Text(token);
%Text(email);
%Text(gender);

and:

%Object(object,{"method" : "xxxx"});

(details see here)

Example:
Sending Newsletter

Create a Newsletter (E-Mail) Document

To send a newsletter you need an email document, which is used as content for the newsletter (nothing special to consider).

In this document the following placeholders are available:

%Text(firstname);
%Text(lastname);
%Text(token);
%Text(email);
%Text(gender);

and:

%Object(object,{"method" : "xxxx"});(details see here)

Example
Send a Test

Open "Extras" -> "Reports & Marketing" -> "Newsletter"

... create a new newsletter


.. 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

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 Email Documents and the Pimcore\Mail Class.

The Syntax of a Placeholder is "%PLACEHOLDERNAME(PARAMETERKEY,JSON-CONFIG);"

PLACEHOLDERNAME 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);

PARAMETERKEY This is the key of the parameter-array that you pass as second parameter to the "replacePlaceholders" method.

JSON-CONFIG You can pass a Json-Config to the Placeholder. (It depends on the Placeholder if the config is used)

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 The text that contains the placeholders which sould be replaced or a document (the document is rendered to HTML)
one

Second The dynamic parameter for replacement

Third 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.
<?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:
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 Description

getPlaceholderKey() 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" ...

getValue() Returns the value from the dynamic parameter.

getPlaceholderConfig() Returns the JSON-Config of the placeholder (if it was set), otherwise returns an empty JSON-Config object

getParams() Returns the array that you passed to the replacePlaceholders() as second parameter.

getParam($key) Returns a specific parameter form the array that you passed to the replacePlaceholders() as second parameter.

getPlaceholderString() Returns the placeholder string (raw text)

getLocale() / Returns the current locale / language


getLanguage()

getEmptyValue() 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 The id of the object

config allowed Yes

config required Yes

Valid config parameter:

Parameter Description

callMethod The method of the object which sould be called (the call is localized)

locale The Locale to use (optional)

Example Usage:
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 string

value string

config available 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

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
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.

Note
Each defined field can be overwritten in the element after it was added.

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:

$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

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 emails and the Debug Mode is enabled, all emails will be sent to the debug email receivers defined in
"Settings" -> "System" -> "Email Settings" -> "Debug email addresses". In addition a debug information is attached to the email which shows you
to who the email 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:

Email

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' email 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. http://www.google.com/tagmanager/, http://www.adobe.com/products/tagman
ager.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 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. http://domain/?_ptc=3,4,5:

_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
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
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:
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

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

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.

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.

In above case the browser is redirected to http://test.domain/?_ptp=10 (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.

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.

http://www.yourdomain.com/?_ptc=3,5

Get ID of Persona in controller

$targetingRulesIds = json_decode("[" . $this->getParam("_ptc") . "]",


true);

Assign Persona in controller


$personaId = 10;

$frontController = \Zend_Controller_Front::getInstance();

/* @var 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.

http://www.yourdomain.com/?_ptc=3,5

Get ID of Persona in frontend

var targetingRulesIds =
window.location.search.match(/(\?|&)_ptc\=([^&]*)/)[2].split(",");

Targeting: Tips for developers

See user's activity log

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

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

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:
<script type="text/javascript"
src="http://www.google.com/jsapi?autoload=%7B%22modules%22%3A%5B%7B%22name
%22%3A%22maps%22%2C%22version%22%3A%222%22%7D%5D%7D&amp;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

$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=phone

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

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):

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) 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.
Table

object_relations_( Contains data of fields with relations to objects, assets, etc.


id)

Table

object_store_(id) This is the main data storage table of an object class. It contains all "flat" data without any relations or external
dependencies.
Table

objects 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.
Table

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

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)

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

Structured

Name Datatype Default Comment

Table Tabledata is stored as a string - serialized.

Structured Table Each table cell is stored distinctively; schema: (fieldname)__(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)__latitude’

Geographic double NULL Creates four columns: ‘(name)__NElongitude’, ‘(name)__NElatitude’, ‘(name)__SWlongitude’ und ‘(name)__SWlatit
Bounds ude’

Geographic longtext NULL Serialized geo-data


Polygon

Other

Name Datatype Default Comment

Image int(11) NULL ID of the image asset

Image int(11), NULL Creates a cloumn ‘(name)__image’(int) for the image assets id and the Column ‘(name)__hotspots’(text). Hotspots are
Advanced text 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

Email 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

Table/View Purpose

object_brick_query_(id)

Table

object_brick_store_(id Main data storage

Table

Localized fields

Table/View Purpose

object_localized_(id)_(language-code) A database view per language, combining reguar and localized data fields

View

object_localized_data_(id) Stores localized field data

Tabelle

object_localized_query_(id)_(language-code) Access to inherited data

Tabelle

Fieldcollections

Table/View Purpose

object_collection_(collection-name)_(object-id) Stores data of the field collections fields and the order (index)

Overview: Default Pimcore database tables


Table Description

assets Assets (Images, etc.), with system metadata

assets_metadata Additional user metadata (Metadata tab in the asset panel)

assets_metadata_predefined Templates for asset metadata

cache serialized data, used by the default Pimcore cache

cache_tags Tag store for cache entries. Used by some cache backends

classes List of all object classes with metadata, but not the actual configuration / class definition

content_analysis SEO-Data / Socialmedia analysis (Marketingpanel in Pimcore)

content_index Index für “page analytics”

custom_layouts Definition of the “custom layouts” for object classes

dependencies Stores dependencies between ressourcen such as objects, assets, documents

documents List of all documents, folders, links, hardlinks, emails and snippets of the document area with meta- and
config-data, relations

documents_doctypes Predefined document types

documents_elements Editables of documents (data), in a serialized form

documents_email Extra config data

documents_hardlink Extra config data

documents_link Extra config data

documents_page Extra config data

documents_snippet Extra config data

edit_lock Tracks which user opened which ressource in the backend

email_blacklist Blacklist for eMail-addresses

email_log Log for emails

glossary Words to auto-link in texts. See: https://www.pimcore.org/wiki/display/PIMCORE/Glossary

http_error_log HTTP error log

keyvalue_groups

keyvalue_keys

keyvalue_translator_configuration

locks

notes

notes_data

objects Liste of all objects with metadata

properties Data from the "properties" tab

properties_predefined Definitions of predefined properties

recyclebin Stores deleted ressources

redirects

sanitycheck

schedule_tasks
search_backend_data Stores the Index for the backend search - is a MyISAM Table with fulltext capabilities

sites Configuration: Multisite-Setups

staticroutes Configuration: Static Routes

targeting_personas Personas for targeting, see: https://www.pimcore.org/en/product/targeting-personalization

targeting_rules Rules for targeting, see: https://www.pimcore.org/en/product/targeting-personalization

tmp_store

translations_admin Backend translations

translations_website Frontend translations

tree_locks

users Backend users

users_permission_definitions List of globally assignable user permissions

users_workspaces_asset Stores user access permissions for asset folder

users_workspaces_document Stores user access permissions for document folders

users_workspaces_object Stores user access permissions for object folders

uuids stores Unique Identifiers - if enabled

versions Liste of object/asset/document versions. Actual data is serialized and written to disk

website_settings Stores “Website Settings”

Console / CLI
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 Directory

Pimcore\Console\Command /pimcore/lib/Pimcore/Console/Command

Website\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/lib/We
bsite/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\Console
\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

1. Create a plugin and let your plugin class extend Pimcore\Console\AbstractConsoleCommandPlugin (or use the Pimcore\Cons
ole\ConsoleCommandPluginTrait and call the initConsoleCommands() method yourself in init()).
2. 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:

<?php
\Pimcore::getEventManager()->attach('system.console.init',
function(\Zend_EventManager_Event $e) {
/** @var \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 checks
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()

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.

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: http://YOUR-DOMAIN/webservice/rest/object/id/1281?apikey=[API-KEY\

Returns: JSON--encoded object data.

Delete Object By ID

Method: DELETE
URL: http://YOUR-DOMAIN/webservice/rest/object/id/1281?apikey=API-KEY

Returns: JSON-encoded success value

Create a new object

Method: PUT or POST


URL: http://YOUR-DOMAIN/webservice/rest/object?apikey=API-KEY

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: http://YOUR-DOMAIN/webservice/rest/object?apikey=API-KEY

Request body: Same as for create object but with object id


Returns: JSON-encoded success value

Check exististence of Object

Method: GET or POST

URL for GET: http://YOUR-DOMAIN/webservice/rest/object-inquire?apikey=API-KEY&ids=4500,4501,2,9999&condense=0


URL for POST http://YOUR-DOMAIN/webservice/rest/object-inquire?apikey=API-KEY&condense=0

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.

Example:

"success": true,
"data":
Unknown macro: {** "4500"}

Get Object Metadata

Method: GET
URL: http://YOUR-DOMAIN/webservice/rest/object-meta/id/1281?apikey=API-KEY

Get List of Classes

Method: GET
URL: http://YOUR-DOMAIN/webservice/rest/classes?apikey=API-KEY

Returns: The JSON-encoded list of classes

Get Class By ID

Method: GET
URL: http://YOUR-DOMAIN/webservice/rest/class/id/1281?apikey=API-KEY

Returns: The JSON-encoded class definition for the given class

Get List of Field Collections

Method: GET
URL: http://YOUR-DOMAIN/webservice/rest/field-collections?apikey=API-KEY

Returns: The JSON-encoded list of field collection configurations

Get Field Collection Definition By Key

Method: GET
URL: http://YOUR-DOMAIN/webservice/rest/field-collection/id/collectionA?apikey=API-KEY

Returns: The JSON-encoded field collection definition for the collection with the given key

Get List of Field Object Bricks

Method: GET
URL: http://YOUR-DOMAIN/webservice/rest/object-bricks?apikey=API-KEY

Returns: The JSON-encoded list of object brick configurations

Get Object Brick Definition By Key

Method: GET
URL: http://YOUR-DOMAIN/webservice/rest/object-brick/id/tirebrick?apikey=API-KEY

Returns: The JSON-encoded object brick definition for the object brick with the given key

Get List of Image Thumbnail Configurations

Method: GET
URL: http://YOUR-DOMAIN/webservice/rest/image-thumbnails?apikey=API-KEY

Returns: The JSON-encoded list of image thumbnail configurations

Get Image Thumbnail Configuration By Key


Method: GET
URL: http://YOUR-DOMAIN/webservice/rest/image-thumbnail/id/product_small?apikey=API-KEY

Returns: The JSON-encoded image thumbnail configuration with the given key

Get Asset By ID

Method: GET
URL: http://YOUR-DOMAIN/webservice/rest/asset/id/1281?apikey=API-KEY

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: http://YOUR-DOMAIN/webservice/rest/asset/id/1281?apikey=API-KEY

Returns: JSON-encoded success value

Create New Asset

Method: PUT or POST


URL: http://YOUR-DOMAIN/webservice/rest/asset?apikey=API-KEY

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: http://YOUR-DOMAIN/webservice/rest/asset?apikey=API-KEY

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: http://YOUR-DOMAIN/webservice/rest/asset-inquire?apikey=API-KEY&ids=4500,4501,2,9999&condense=0


URL for POST http://YOUR-DOMAIN/webservice/rest/asset-inquire?apikey=API-KEY&condense=0

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: http://YOUR-DOMAIN/webservice/rest/document/id/1281?apikey=API-KEY

Returns: JSON-encoded document data.

Delete Document By ID

Method: DELETE
URL: http://YOUR-DOMAIN/webservice/rest/document/id/1281?apikey=API-KEY

Returns: JSON-encoded success value

Create New Document

Method: PUT or POST


URL: [http://YOUR-DOMAIN/webservice/rest/document?apikey=API-KEY]

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

Method: PUT or POST


URL: http://YOUR-DOMAIN/webservice/rest/document?apikey=API-KEY

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: http://YOUR-DOMAIN/webservice/rest/document-inquire?apikey=API-KEY&ids=4500,4501,2,9999&condense=0


URL for POST http://YOUR-DOMAIN/webservice/rest/document-inquire?apikey=API-KEY&condense=0

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: http://YOUR-DOMAIN/webservice/rest/asset-list?apikey=API-KEY&order=DESC&offset=3&orderKey=id&limit=2&conditio
n=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: http://YOUR-DOMAIN/webservice/rest/document-list?apikey=API-KEY&order=DESC&offset=3&orderKey=id&limit=2&co
ndition=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: http://YOUR-DOMAIN/webservice/rest/object-list?apikey=API-KEY&order=DESC&offset=3&orderKey=id&limit=2&object
Class=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: http://YOUR-DOMAIN/webservice/rest/asset-count?apikey=API-KEY2&condition=type%3D%27folder%27

Returns: The total number of assets matching the given criteria.

Parameters:
condition: where clause
groupBy: group by key

Get Document Count

Method: GET
URL: http://YOUR-DOMAIN/webservice/rest/document-count?apikey=API-KEY2&condition=type%3D%27folder%27

Returns: The total number of documents matching the given criteria.

Parameters:
condition: where clause
groupBy: group by key

Get Object Count

Method: GET
URL: http://YOUR-DOMAIN/webservice/rest/document-count?apikey=API-KEY2&condition=type%3D%27folder%27

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!

Get User

Method: GET
URL: http://YOUR-DOMAIN/webservice/rest/user?apikey=API-KEY

Returns: The JSON-encoded user data for the current user

Get KeyValue Definition

Method: GET
URL: http://YOUR-DOMAIN/webservice/rest/key-value-definition?apikey=API-KEY

Returns: The JSON-encoded Key/Value definition

Parameters:
condition: where clause

Get Server Info

Method: GET
URL: http://YOUR-DOMAIN/webservice/rest/server-info?apikey=API-KEY

Returns: The JSON encoded server-info including pimcore version, current time and extension data.

Get Server Time

Method: GET
URL: http://YOUR-DOMAIN/webservice/rest/system-clock?apikey=API-KEY

Returns: The JSON encoded system time.

Override HTTP Method

The HTTP request method can be overriden by providing a method parameter.

Method: any
URL: http://YOUR-DOMAIN/webservice/rest/object/id/1281?apikey=API-KEY&method=PUT
Translations

Method: GET
URL: http://YOUR-DOMAIN/webservice/rest/translations?apikey=API-KEY&type=website

Returns: List of translations.

Parameters:
type: "website" or "admin" (required)
key: tranlation key matches param
creationDateFrom: timestamp
creationDateTill: timstamp

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.:

<link href="/static/css/example.css" media="screen" rel="stylesheet/less"


type="text/css" >

Install lessc on your server (Debian)

# 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 http://nodejs.org/#download
wget http://nodejs.org/dist/node-v0.4.7.tar.gz

# 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
https://github.com/cloudhead/less.js/tarball/master

# extract
tar xfvz cloudhead-less.js-v1.0-336-g853604a.tar.gz

mv cloudhead-less.js-853604a 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
Mailing Framework

General information

The “Pimcore Mailing Framework” provides an easy way to send/create emails with Pimcore.

Therefore you have several components:

Document\Email
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" -> "Email Settings" are applied automatically.

When you enable the debug mode in Settings" -> "System" -> "Debug", all emails will be sent to the addresses given in "Settings" ->
"System" -> "Email Settings" -> "Debug Email Addresses" and the debug information (to whom the email would have been sent) is
automatically appended.
Pimcore provides a "Document Email" type where you can define the reciptients... (more information here) and "Dynamic Placeholders" (more
information here)

To send a email you just create a "Email 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 "Email Document" in the Pimcore admin (/email/myemaildocument) which looks like this:

To send this document as email we just have to write the following code-snippet in our ControllerAction:
//dynamic parameters
$params = array('firstName' => 'Pim',
'lastName' => 'Core',
'product' => 73613);

//sending the email


$mail = new \Pimcore\Mail();
$mail->addTo('example@pimcore.org');
$mail->setDocument('/email/myemaildocument');
$mail->setParams($params);
$mail->send();

Sending a plain text email:

$mail = new \Pimcore\Mail();


$mail->addTo('example@pimcore.org');
$mail->setBodyText("This is just plain text");
$mail->send();

Sending a rich text (HTML) email:

$mail = new \Pimcore\Mail();


$mail->addTo('example@pimcore.org');
$mail->addBcc("bcc@pimcore.org");
$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 E-Mail settings from "Settings" -> "System" -> "Email Settings" are automatically applied.

If the Debug Mode in "Settings" -> "System" -> "Debug" is enabled, all emails will be sent to the Debug Email recipients defined in
"Settings" -> "System" -> "Email Settings" -> "Debug Email Addresses". Additionaly the debug information (to whom the email would have
been sent) is appended to the email and the Subject contains the prefix "Debug email:".

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 email will automatically be created by Pimcore\Mail.

Optionally, you can use "html2text" from Martin Bayer (http://www.mbayer.de/html2text/index.shtml) 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 Description

disableLogging() Disables email logging - by default it is enabled


setParams(array) Sets the parameters for the email view and the Placeholders

setParam($key, $value) Sets a single parameter for the email view and the Placeholders

isValidEmailAddress(emailAddress) Static helper to validate a email address

setDocument(Document_Email) Sets the email document

getDocument() Returns the Document

getSubjectRendered() Replaces the placeholders with the content and returns the rendered Subject

getBodyHtmlRendered() Replaces the placeholders with the content and returns the rendered Html

getBodyTextRendered() * 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 email will be automatically created

enableHtml2textBinary() "html2text" from Martin Bayer (http://www.mbayer.de/html2text/index.shtml)


throws an Excpetion if html2text is not installed!

setHtml2TextOptions($options) set options for html2text (only for binary version)

Class Location: /pimcore/lib/Pimcore/Mail.php

Usage example

$params = array('firstName' => 'Pim',


'lastName' => 'Core',
'product' => 73613);

//sending an email document (pimcore document)


$mail = new \Pimcore\Mail();
$mail->addTo('example@pimcore.org');
$mail->setDocument('/email/myemaildocument');
$mail->setParams($params);
$mail->send();

// sending a text-mail

$mail = new \Pimcore\Mail();


$mail->addTo('example@pimcore.org');
$mail->setBodyText("This is just plain text");
$mail->send();

// Sending a rich text (HTML) email:&nbsp;

$mail = new \Pimcore\Mail();


$mail->addTo('example@pimcore.org');
$mail->addBcc("bcc@pimcore.org");
$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:


<?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 an
d 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:

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 user
can be selected from all available pimcore users.
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"
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
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
3. Enable the output-cache
4. Install and setup varnish cache (pimcore sends already the right headers for varnish => ouput-cache lifetime, ... )
5. Only use sessions when really necessary
6. Tuning your MySQL configuration to exactly fit the needs of your application
a.
6.
a. mysqltuner.pl
b. mysqlprimer
c. Percona Server / MariaDB (if improvements are expected => test it on your stage-environment)
7. Make use of in-template caching

Apache Tuning (mod_php)

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 262144" >> /etc/security/limits.conf
echo "session required /lib/security/pam_limits.so" >> /etc/pam.d/login
echo 262144 > /proc/sys/fs/file-max
echo "ulimit -n 262144" >> /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 3.1.0

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:
<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 commandline
backup tool in this case, which is up to 10 times faster.

Commandline backups

Click here for more details.

Restore backups

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

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!

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 www.mysite.org
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 https://dav.mysite.org 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

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
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: http://dev.mysql.com/doc/refman/5.6/en/fulltext-boolean.html

Some general examples:

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

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.
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:


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
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.

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:

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:

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.
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:

http://www.pimcore.org/plugin/MyPlugin/admin/get-address-book

There are some aspects regarding the above URL that you should keep in mind:

1. /plugin - is always the prefix for accesing plugins (notice there is no "s" in plugin)
2. /MyPlugin/ - the plugin name is always case sensitive
3. /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:


<?xml version="1.0"?><zend-config
xmlns:zf="http://framework.zend.com/xml/zend-config-xml/1.0/">
<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:

<?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";
}
}

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

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:

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' => '1234567890',
'address' => '123 Fake Street'
);

$addresses[] = array(
'name' => 'Joe Smith',
'phoneNumber' => '0987654321',
'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 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:

<?xml version="1.0"?>
<zend-config xmlns:zf="http://framework.zend.com/xml/zend-config-xml/1.0/">
<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</pluginXmlEditor
File>
</plugin>
</zend-config>

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 readyForInstall met
hod. 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: '”'

/**
*
* @param string $language
* @return 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 getPluginState() M
ethod 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

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: http://fontello.com/ (Font Awesome)
Icon-classes included in pimcore: /pimcore/static/css/fontello.css
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, ...).

Вам также может понравиться