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
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:
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.
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
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, ...)
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
Pimcore doesn't support Windows officially, but it should work almost perfectly.
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.
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.
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
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:
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.
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:
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.:
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:
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/
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.
Allowoverride All
<VirtualHost *:80>
DocumentRoot "C:/sites/pimcore"
ServerName pimcore.local
<directory "C:/sites/pimcore">
Allowoverride All
allow from all
</directory>
</VirtualHost>
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.
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)
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;
# 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;
#}
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.
Upgrade Notes
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.
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:
Lists:
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 |
+---------+-------+---------------------+
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).
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).
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.
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.
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”:
The previous two sections outline how documents can be configured to use different controller/action and views. The table below represents
the three configurations:
Document Properties
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.
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:
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!
Introduction
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 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;
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, …
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:
<body>
<?= $this->layout()->content ?>
</body>
</html>
The code <?= $this->layout()->content ?> is the placeholder where your contentpage will be inserted.
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)
Examples
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.
enableLayout none Register Zend_Layout for you, if called you can use this layout in your views.
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
Example
use Pimcore\Model\Document;
...
// example of properties
if ($this->document instanceof Document\Page) {
// do something
}
...
// your custom code
...
}
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
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:
editmode boolean Is true if you are in editmode (admin), false if you are on the website
document Document Reference to the current document object you can directly access the properties of the
document in the view (eg. $this?document?getTitle();)
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)
Example
<!-- creates a input in editmode (admin) and directly outputs the text in
frontend -->
<h1><?= $this->input("headline", ["width" => 540]); ?></h1>
<?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'
]); ?>
<?= $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",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"
)
)));
?>
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
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"
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
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:
Methods
Name Description
getCurrentIndex() get the current index (index is different from position, as you can move block around)
The manual mode offers you the possibility to deal with areablocks the way you like, this is for example useful with tables:
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.
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.
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.
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->getConfig() returns the configuration (Zend_Config) out of area.xml (to get your custom properties, ... )
$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).
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'); ?>
<?php
$class = '';
if(!$this->input('class')->isEmpty()) {
$class = $this->input('class')->getData();
}
?>
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 {
/**
* 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">';
}
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)
$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
Methods
Name Description
Example
Block
Configuration
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
getElements() Return a array for every loop to access the defined childs
Move block up
Basic usage
<?php if(!$this->select("blocktype")->isEmpty()) {
$this->template("content/blocks/".$this->select("blocktype")->getData()."
.php");
} ?>
<?php } ?>
<?php } ?>
The manual mode offers you the possibility to deal with block the way you like, this is for example useful with tables:
Checkbox
Configuration
reload boolean Set to true to reload the page in editmode after changing the state
Simple Example
Advanced Example
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
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.).
Configuration
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
element Document|Asset|Object The property for getElement() it's a good idea to use the getter
Examples
// Basic usage
<?= $this->href("myHref"); ?>
Image
Configuration
title string You can give the image widget in editmode a title
width integer Width of the image in pixel
reload boolean Set true to reload the page in editmode after updating the image
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)
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
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)
Examples
// Basic usage
<?= $this->image("myImage"); ?>
// Advanced usage
<?= $this->image("myImage", array(
"title" => "Drag your image here",
"width" => 200,
"height" => 200,
"thumbnail" => "contentimages"
)); ?>
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.
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>
Configuration
htmlspecialchars boolean Set to false to get the raw value without HTML special chars like & (default to 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
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
//advanced usage
<?php if($this->editmode): ?>
<script type="text/javascript">
$(function(){
</script>
<?php endif ?>
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
...
reload boolean Set to true to reload the page in editmode after changing the state
Methods
Simple Example
<?= $this->link("myLink"); ?>
Advanced Example
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
Accessible properties
Available methods
Example
Multiselect
Configuration
width integer
height integer
Additionally you can use every configuration property of Ext.ux.form.MultiSelect to customize the select widgetin editmode.
Example
Numeric
The numeric editable is like a normal textfield but with special configurations for numbers.
Configuration
You can use every configuration property of Ext.ux.form.SpinnerField to customize the numeric widget in editmode.
Accessible Properties
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
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 target controller action you get the follwing parameters which can be accessed by $this->getParam("key").
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.
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
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
)); ?>
<?php
foreach ($this->assets as $asset) {
echo '<img src="'.$asset.'"/>';
}
?>
<?php
use Website\Controller\Action;
class ContentController extends Action {
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
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
getData string Value of the select, this is useful to get the value even in editmode
Example
// 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
Accessible Properties
Example
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:
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
defaults array Array can have the following properties: rows, cols, data (see example)
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
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
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
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)
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:
Methods
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:
Accessible Properties
</head>
<body>
</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
Accessible Properties
text string Value of the WYSIWYG, this is useful to get the value even in editmode
Basic Example
// Basic usage
<?= $this->wysiwyg("myWysiwyg") ?>
// Advanced useage
<?= $this->wysiwyg("content", array(
"width" => 700,
"height" => 500));
?>
// 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 } ?>
<?= $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
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
hotspotCallback closure possibility to add custom attributes on hotspot <div> tags, ... see example below
Methods
Name Arguments Return Value Description
Accessible Properties
Example
Advanced Example
<section class="area-pdf">
<?= $this->pdf("pdf", [
"hotspotCallback" => function ($data) {
$data["attributes"] = [];
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)
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;
$doc = Document::getById(477);
echo $this->inc($doc, array(
"param1" => "value1"
));
Parameters in the included template are then accessible through $this->paramName i.e. from the example above
$this->getParam(string $key)
Get's a parameter (get, post, .... ), it's an equivalent to $this->getParam() in the controller action.
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.
$this->glossary()
$this->t("translation_key")
$this->ts("translation_key")
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);
?>
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.
Usage Examples
$asset = Asset::getByPath("/path/to/image.jpg");
$imageThumb = $asset->getThumbnail("myThumbnail");
?>
<?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");
More Examples
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.
pimcore auto-generates a thumbnail if it doesn't exist and is directly called (not using any of 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).
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
When using this configuration in combination with the image editable using the following code
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):
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
/website/var/tmp/thumb_123456__teaserportal@3.2x.png
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);
<?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
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:
<p>
<abbr title="Hypertext Preprocessor">PHP</abbr> is a widely used,
general-purpose scripting language that was originally designed for web
development to produce dynamic web pages. For this purpose, <abbr
title="Hypertext Preprocessor">PHP</abbr> code is embedded into the HTML
source document and interpreted by a web server with a <abbr
title="Hypertext Preprocessor">PHP</abbr> processor module, which generates
the web page document. As a general-purpose programming language,
<abbr title="Hypertext Preprocessor">PHP</abbr> code is processed by an
interpreter application in command-line mode performing desired operating
system operations and producing program output on its standard output
channel. It may also function as a graphical application. <abbr
title="Hypertext Preprocessor">PHP</abbr> is available as a processor for
most modern web servers and as standalone interpreter on most operating
systems and computing platforms. You can <a
href="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.
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
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;
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:
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
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 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.
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).
There is a View Helper availabe which allows you to use translations in your document editables.
(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"))
)
)); ?>
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 {
parent::init();
?>
<div>
<address>© <?= $this->translate("copyright") ?></address>
<a href="/imprint"><?= $this->translate("imprint") ?></a>
<a href="/legal"><?= $this->translate("legal_notice") ?></a>
</div>
Shorthands
<div>
<address>© <?= $this->t("copyright") ?></address>
<a href="/imprint"><?= $this->t("imprint") ?></a>
<a href="/legal"><?= $this->t("legal_notice") ?></a>
</div>
Navigation
Basics
Pimcore comes with a standard navigation implementation in the form of a view helper, which utilizes Zend_Navigation. The
Pimcore\View\Helper\PimcoreNavigation gets registered by default with the other pimcore view helpers. It builds a Zend_Navigation container
based on the existing document structure and needs to be set up as follows in your view script or layout script:
Only documents are included in this structure, directories are ignored, regardless of their navigation properties.
<?php
// get root node if there is no document defined (for pages which are
routed directly through static route)
if(!$this->document instanceof Document\Page) {
$this->document = Document::getById(1);
}
$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));
?>
...
...
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;
}
Title: Document's title used in the navigation - the HTML Attribute title
getProperty("navigation_exclude")
Relation: Only available in custom navigation script. Supposedly the HTML rel attribute to open the link in a sort of Lightbox / Clearbox
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.
/website/views/scripts/includes/navigation.phtml
<ul class="navigation">
<?php
foreach ($this->container as $page) {
echo $this->navigation()->menu()->htmlify($page), PHP_EOL
}
?>
</ul>
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.
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"
]);
?>
<?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>
In the following example we're adding news items (objects) to the navigation using the callback.
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:
Using this method will dramatically improve the performance of your navigation.
Sometimes it's necessary to manually set the key for 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
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)
See the above question. If none of the documents have a navigation title set the render function will simply return nothing.
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:
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.
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.
$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
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.
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/
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
$asset = Asset::getById(123);
if($asset instanceof Asset\Video) {
// 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);
}
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 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);
}
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")); ?>
The "copyright" field will be appended to every "title" and "alt" attribute separated by |.
Examples
Getting data
$asset = Asset::getById(123);
Setting data
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();
$myObject->setName("My Name");
$myObject->save();
// you can also get an object by id where you don't know the type
$object = Object::getById(235);
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
country Pimcore\Model\Object\Class\Data\Country combo box with predefined country list from Zend_Locale
date & time Pimcore\Model\Object\Class\Data\Datetime calendar date selector + combo box for time
language Pimcore\Model\Object\Class\Data\Language combo box with predefined language list from Zend_Locale
slider Pimcore\Model\Object\Class\Data\Slider number input with slider widget (min - max slider)
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.
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.
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;
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 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();
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();
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());
Internally the setter sets the value to an empty array, regardless if an empty array or null is passed to it.
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.
use Pimcore\Model\Object;
$object = Object::getById(73585);
//load your object (in this object we save the metadata objects)
$object = Object::getById(73585);
//loop throu the objectlist (or array ...) and create object metadata
foreach( $yourObjectsList as $yourObject ){
Unpublished relations
Related items that are unpublished are normally not returned. You can disable this behavior like this:
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.
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.
Setting 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.
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);
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.
Since $object->getImage() just returns an asset object, you can of course use all other thumbnail features of Asset_Image. See here.
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
Array
(
[0] => Array
(
[name] => hotspot1
[top] => 3.8922155688623
[left] => 48.076923076923
[width] => 8.3333333333333
[height] => 48.802395209581
)
To get the cropped image you have to use the getThumbnail() method:
Thumbnail of image
of course you can use the above code regardless if the image is cropped or not:
$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>
$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
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:
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.
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.
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.
Unlike other complex data types, the “Search, Edit and Export” grid column configuration can be configured on a key – basis.
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.
The key definition consists at least of a key name and its type.
"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:
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);
//
--------------------------------------------------------------------------
-----------
// Working with existing Key-Value Pairs
//
--------------------------------------------------------------------------
-----------
$object = Object\Article::getById(62883);
// 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)");
// 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")));
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.
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")
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
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
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
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));
$object->setCollectionitems($items);
$object->save();
Inheritance
WARNING
Is not possible with this datatype.
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.
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.
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.
$product->setName("testproduct");
if ($tireBrick) {
$tireBrick->setDoDelete(true);
}
$product->save();
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.
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.
Structured Table
Similar to the table widget, the structured table can hold structured data. But there are a few fundamental differences:
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();
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.
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:
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
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
Video Field
Code Example
<?php
$object = Object::getById(1234);
print_r($object->getMyVideo());
?>
Pimcore\Model\Object\Data\Video Object
(
[type] => asset
[data] => Pimcore\Model\Asset\Video Object
(
[type] => video
[id] => 27
...
)
# 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
)
$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();
<?php
$object = Object::getById(789);
$assetVideo = Asset::getById(123);
$assetImage = Asset::getById(456);
$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.
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:
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:
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
foreach ...
foreach ...
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.
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.
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.
Examples:
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
To filter objects by attributes from field collections, you can use following syntax (Both code snippets result in the same object list).
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
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
<br />
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();
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->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!
You can also add items to the static list of globally protected keys by passing them to
\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.
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;
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.
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();
$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.
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.
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));
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:
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:
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.
}
}
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
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
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, ...)
(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>
Goto https://console.developers.google.com/project/
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:
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
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.
https://www.google.com/analytics/web/?hl=en#management/Settings
Open the pimcore admin system settings menu (left screenshot) and
copy credentials from the google API console (right screenshot)
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
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.
The default route is also responsible to assembe the URL's for documents.
Examples
Link: /about
Link: ?key=value
Link: /about?key=value
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.
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!).
$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
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.
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:
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
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>
<?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>
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 ;-)
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");
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}
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.
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.
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.
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');
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:
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).
/some-example/some~random~text_45
/some-example/This+is+some+random+text_998_category_776
<?= $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
The parameters text and id are available via the route pattern, so the will be added automatically if you don't specify them.
<?= $this->url(array(
"id" => 776
),
"example"
) ?>
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.
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:
// using the ID
echo $this->url(array(
"id" => 4,
"text" => "some-text",
"site" => 3
), "news");
<?= $this->url(array(
"id" => 4,
"text" => "some-text",
"site" => 0
), "news");
?>
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).
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 code triggers the error handler which shows the error page with the correct 404 status code.
Example:
Contents
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;
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>
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.
$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.
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));
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.
<?xml version="1.0"?>
<zend-config xmlns:zf="http://framework.zend.com/xml/zend-config-xml/1.0/">
<Object_List>Website\ObjectList</Object_List>
<Asset_List>Website\AssetList</Asset_List>
<Document_List>Website\DocumentList</Document_List>
</zend-config>
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.
This code depends on Pimcore build 1638 but should work fine on older releases, too.
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.
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;
// just getters/setters
public function setScore($score) {
$this->score = $score;
}
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.
Resource
#website/lib/Website/Model/Vote/Resource.php
<?php
namespace Website\Model\Vote;
class Resource extends \Pimcore\Model\Resource\AbstractResource {
if ($id != null)
$this->model->setId($id);
if(!$data["id"])
throw new Exception("Object with the ID " .
$this->model->getId() . " doesn't exists");
$this->assignVariablesToModel($data);
}
$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;
if(!is_callable(array($this->model, $getter)))
continue;
$value = $this->model->$getter();
if(is_bool($value))
$value = (int)$value;
$buffer[$k] = $value;
}
$this->db->insert($this->tableName, $buffer);
$this->model->setId($this->db->lastInsertId());
}
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.
soon ....
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();
// ...
});
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 {
The following example shows how to deal with event parameters and a static callback method.
<?php
namespace Website\Auth;
class Handler {
<?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
system.startup Zend_Controller_Front This event is fired on startup, just before the MVC dispatch
starts.
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
Document
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
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
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
object.class.preAdd Pimcore\Model\Object\ClassDefinition -
object.class.preUpdate Pimcore\Model\Object\ClassDefinition -
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.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
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();
// ...
});
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
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 {
The following example shows how to deal with event parameters and a static callback method.
<?php
namespace Website\Auth;
class Handler {
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()->trigger("website.user.register", $this,
['firstName' => $post['firstName']]);
Available Events
System / General
system.startup Zend_Controller_Front This event is fired on startup, just before the MVC dispatch
starts.
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
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
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
object.class.preAdd Pimcore\Model\Object\ClassDefinition -
object.class.preUpdate Pimcore\Model\Object\ClassDefinition -
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
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
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();
// ...
});
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:
{
"require": {
"symfony/symfony": "2.4.*@dev"
}
}
Install dependencies:
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.
use Pimcore\Controller\Action;
use Symfony\Component\Stopwatch\Stopwatch;
print_r($event);
exit;
}
...
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
$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
$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);
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(),
)));
$router = $front->getRouter();
$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.
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;
$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>
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".
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
2013-07-25T18:26:30+02:00 Timestamp
2 User-ID
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
dbprofile-*.log
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
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
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
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).
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
{
$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);
$this->view->success = true;
} catch (\Exception $e) {
}
}
}
$this->view->success = false;
if($newsletter->confirm($this->getParam("token"))) {
$this->view->success = true;
}
}
$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) { ?>
<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 />
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
<label for="email">E-Mail</label>
<input id="email" name="email" type="text" />
<br />
Confirmation E-Mail
%Text(firstname);
%Text(lastname);
%Text(token);
%Text(email);
%Text(gender);
and:
%Object(object,{"method" : "xxxx"});
Example:
Sending Newsletter
To send a newsletter you need an email document, which is used as content for the newsletter (nothing special to consider).
%Text(firstname);
%Text(lastname);
%Text(token);
%Text(email);
%Text(gender);
and:
Example
Send a Test
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.
use Pimcore\Model;
$object = Model\Object::getById(4);
// 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();
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.
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.
More detailed:
First The text that contains the placeholders which sould be replaced or a document (the document is rendered to HTML)
one
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.
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).
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
{
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();
$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
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.
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.
Parameter Description
callMethod The method of the object which sould be called (the call is localized)
Example Usage:
public function objectPlaceholderAction()
{
$this->disableViewAutoRender();
Text
The Text Placeholder is used to replace the placeholder with the passed value.
key string
value string
config available no
Example Usage:
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
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
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
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:
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) {
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.
You can specify your own texts and add your custom detail link using the "Shared Translations".
Just search for "cookie-" in Shared Translations, then you get listed the predefined keys for the cookie texts and links:
There is a convenience function which allows any pimcore system component or plugin to use a preconfigured Zend_Mail instance based on the
pimcore system settings' 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 ;-)
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:
When this parameter is available, frontend JS will trigger redirect to the URL set in the backend
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)
Content
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
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.
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.
Regex of URLs to match. (use single backslash to escape question mark if needed \?)
Browser
Country
Language
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.
Checks if user visited certain page before - Regex of the page URL . (use single backslash to escape question mark if needed \?)
Condition is true if user is on the site for at least the duration set.
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.
Condition is true if user clicked at least the number of links set in the condition.
Hardware Platform
Operating System
Redirect
Programmatically
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.
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.
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").
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
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
$frontController = \Zend_Controller_Front::getInstance();
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
var targetingRulesIds =
window.location.search.match(/(\?|&)_ptc\=([^&]*)/)[2].split(",");
Pimcore logs targeting actions to console. If you want to further examine targeting data enter this:
pimcore.targeting
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;
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])
You can globally deactivate and activate versioning with the following PHP code directly in your scripts:
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
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&key=<?=
$this->config->googleMapsKey ?>"></script>
Example in a controller:
Note
Pimcore caches Website Settings, it is therefore a good idea to clear the cache after you have finished adding / editing values.
Code Snippets
if(\Pimcore\Model\Site::isSiteRequest()) { /* ... */ }
Working with the navigation helper
See Navigation
if(\Pimcore\Model\Site::isSiteRequest()) {
$site = \Pimcore\Model\Site::getCurrentSite();
$navStartNode = $site->getRootDocument();
} else {
$navStartNode = \Pimcore\Model\Document::getById(1);
}
The DeviceDetector helper makes it easy to implement the adaptive design approach in pimcore.
if($device->isDesktop() || $device->isTablet()) {
// do something
}
}
Sometimes it's necessary to force a device type. A typical use case is a "Back to Desktop Version" or vice versa link.
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
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):
The application logger extends the Zend_Log component and therefore it can be used the same way:
$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)
// add a related object to the log entry (assets + documents are possible
too)
$relatedObject = \Pimcore\Model\Object\AbstractObject::getById(50833);
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
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
Text
Table: object_store_(id)
Number
Table: object_store_(id)
Date
Table: object_store_(id)
Select
Table: object_store_(id)
Name Datatype Default Comment
Relations
Structured
Structured Table Each table cell is stored distinctively; schema: (fieldname)__(row key)#(column key)
Field-Collections
Geographic
Table: object_store_(id)
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’
Other
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.
CRM
Objectbricks
Table/View Purpose
object_brick_query_(id)
Table
Table
Localized fields
Table/View Purpose
object_localized_(id)_(language-code) A database view per language, combining reguar and localized data fields
View
Tabelle
Tabelle
Fieldcollections
Table/View Purpose
object_collection_(collection-name)_(object-id) Stores data of the field collections fields and the order (index)
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
documents List of all documents, folders, links, hardlinks, emails and snippets of the document area with meta- and
config-data, relations
keyvalue_groups
keyvalue_keys
keyvalue_translator_configuration
locks
notes
notes_data
redirects
sanitycheck
schedule_tasks
search_backend_data Stores the Index for the backend search - is a MyISAM Table with fulltext capabilities
tmp_store
tree_locks
versions Liste of object/asset/document versions. Actual data is serialized and written to disk
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:
# or
$ php pimcore/cli/console.php list
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
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
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:
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.
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();
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()
<?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;
writeError()
<?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:
Available Calls
Get Object By ID
Method: GET
URL: http://YOUR-DOMAIN/webservice/rest/object/id/1281?apikey=[API-KEY\
Delete Object By ID
Method: DELETE
URL: http://YOUR-DOMAIN/webservice/rest/object/id/1281?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
Example:
"success": true,
"data":
Unknown macro: {** "4500"}
Method: GET
URL: http://YOUR-DOMAIN/webservice/rest/object-meta/id/1281?apikey=API-KEY
Method: GET
URL: http://YOUR-DOMAIN/webservice/rest/classes?apikey=API-KEY
Get Class By ID
Method: GET
URL: http://YOUR-DOMAIN/webservice/rest/class/id/1281?apikey=API-KEY
Method: GET
URL: http://YOUR-DOMAIN/webservice/rest/field-collections?apikey=API-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
Method: GET
URL: http://YOUR-DOMAIN/webservice/rest/object-bricks?apikey=API-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
Method: GET
URL: http://YOUR-DOMAIN/webservice/rest/image-thumbnails?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.
Delete Asset By ID
Method: DELETE
URL: http://YOUR-DOMAIN/webservice/rest/asset/id/1281?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
Get Document By ID
Method: GET
URL: http://YOUR-DOMAIN/webservice/rest/document/id/1281?apikey=API-KEY
Delete Document By ID
Method: DELETE
URL: http://YOUR-DOMAIN/webservice/rest/document/id/1281?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
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
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
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
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
Parameters:
condition: where clause
groupBy: group by key
Method: GET
URL: http://YOUR-DOMAIN/webservice/rest/document-count?apikey=API-KEY2&condition=type%3D%27folder%27
Parameters:
condition: where clause
groupBy: group by key
Method: GET
URL: http://YOUR-DOMAIN/webservice/rest/document-count?apikey=API-KEY2&condition=type%3D%27folder%27
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
Method: GET
URL: http://YOUR-DOMAIN/webservice/rest/key-value-definition?apikey=API-KEY
Parameters:
condition: where clause
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.
Method: GET
URL: http://YOUR-DOMAIN/webservice/rest/system-clock?apikey=API-KEY
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
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.
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:
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:
Now every pimcore and ZF module will respect your registered locale.
Outputfilters
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.:
# get root
su root
cd ~
# 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
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.
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);
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.
Useful methods:
Method Description
setParam($key, $value) Sets a single parameter for the email view and the Placeholders
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
Usage example
// sending a text-mail
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
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.
include(dirname(__FILE__) . "/../../../pimcore/cli/startup.php");
$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";
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 ...
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;
...
...
$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);
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
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.
Please note that the following instructions are only tested with Debian 5.0
/etc/init.d/apache restart
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>
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
/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
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.
/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.
/pimcore/cli/cache-warming.php
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
php backup.php
Create a backup in a custom folder with a custom filename. If the destination file exists, it will be overwritten
Image
/pimcore/cli/image-thumbnails.php
Video
/pimcore/cli/video-thumbnails.php
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, ... ).
/pimcore/cli/mysql-tools.php
Install Plugins
Setting up WebDav
The following lines provide a guideline for setting up pimcore WebDAV access:
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
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.
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
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.
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.
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:
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.
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
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
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"
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
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.
Please see the following tutorials for the different WebDAV clients:
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.
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.
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
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
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.
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
{
"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
{
"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
Plugin example
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)
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
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();
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
plugins/MyPlugin
plugins/MyPlugin/plugin.xml
Next, we need to write the plugin PHP class, so create the following file:
plugins/MyPlugin/lib/MyPlugin/Plugin.php
<?php
namespace MyPlugin;
//////////////////////////////////////////////////////////////////////////
////////////////////////////
$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";
}
}
if (!self::isInstalled()) {
return "MyPlugin Plugin successfully uninstalled.";
} else {
return "MyPlugin Plugin could not be uninstalled";
}
}
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.
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
pimcore.plugin.myPlugin = Class.create(pimcore.plugin.admin, {
getClassName: function() {
return "pimcore.plugin.myPlugin";
},
initialize: function() {
pimcore.plugin.broker.registerPlugin(this);
},
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: []
});
pimcore.layout.refresh();
}
});
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.
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()]
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'}
];
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
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'
);
Reload the admin interface, navigate to Extras -> My Plugin and you should see a table with two rows, which can be sorted by column.
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 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
<?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;
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;
}
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.
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.
Sometimes a plugin needs some HDD - space to put logfiles or some other dynamic files on it.
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.
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')
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
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?