Upload
shambalic
View
1.293
Download
2
Embed Size (px)
Citation preview
MODx Official Documentation1. Home . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
1.1 Getting Started . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61.1.1 Server Requirements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
1.1.1.1 MySQL 5.0.51 Issues . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71.1.2 Installation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
1.1.2.1 Basic Installation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81.1.2.1.1 MODx Revolution on Debian . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 111.1.2.1.2 Lighttpd Guide . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 121.1.2.1.3 Problems with WAMPServer 2.0i . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
1.1.2.2 Advanced Installation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 131.1.2.3 Git Installation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 171.1.2.4 Troubleshooting Installation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 191.1.2.5 Successful Installation, Now What Do I Do? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 211.1.2.6 Using MODx Revolution from SVN . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
1.1.3 An Overview of MODx . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 241.1.3.1 Glossary of Revolution Terms . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
1.1.3.1.1 Explanation of Directory Structure . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 301.1.3.2 Roadmap . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
1.2 Making Sites with MODx . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 351.2.1 Structuring Your Site . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
1.2.1.1 Resources . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 351.2.1.1.1 Content Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 381.2.1.1.2 Named Anchor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 391.2.1.1.3 Static Resource . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 391.2.1.1.4 Symlink . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 401.2.1.1.5 Weblink . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41
1.2.1.2 Templates . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 421.2.1.3 Chunks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 431.2.1.4 Using Snippets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45
1.2.2 Tag Syntax . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 461.2.3 Customizing Content . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47
1.2.3.1 Template Variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 471.2.3.1.1 Creating a Template Variable . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 481.2.3.1.2 Bindings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 501.2.3.1.3 Template Variable Output Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 561.2.3.1.4 Adding a Custom TV Input Type . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 581.2.3.1.5 Adding a Custom TV Output Type . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59
1.2.3.2 Properties and Property Sets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 621.2.3.3 Input and Output Filters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65
1.3 Administering Your Site . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 671.3.1 Settings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68
1.3.1.1 System Settings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 691.3.1.1.1 allow_duplicate_alias . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 691.3.1.1.2 allow_multiple_emails . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 691.3.1.1.3 allow_tags_in_post . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 691.3.1.1.4 auto_check_pkg_updates . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 701.3.1.1.5 auto_check_pkg_updates_cache_expire . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 701.3.1.1.6 auto_menuindex . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 701.3.1.1.7 blocked_minutes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 701.3.1.1.8 cache_action_map . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 701.3.1.1.9 cache_context_settings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 701.3.1.1.10 cache_db . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 711.3.1.1.11 cache_db_expires . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 711.3.1.1.12 cache_default . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 711.3.1.1.13 cache_disabled . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 711.3.1.1.14 cache_handler . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 711.3.1.1.15 cache_json . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 721.3.1.1.16 cache_json_expires . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 721.3.1.1.17 cache_lexicon_topics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 721.3.1.1.18 cache_noncore_lexicon_topics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 721.3.1.1.19 cache_resource . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 721.3.1.1.20 cache_resource_expires . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 721.3.1.1.21 cache_scripts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 731.3.1.1.22 compress_css . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 731.3.1.1.23 compress_js . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 731.3.1.1.24 concat_js . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 731.3.1.1.25 container_suffix . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 731.3.1.1.26 cultureKey . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 741.3.1.1.27 custom_resource_classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 741.3.1.1.28 default_template . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 741.3.1.1.29 editor_css_path . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 741.3.1.1.30 editor_css_selectors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 741.3.1.1.31 emailsender . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 751.3.1.1.32 emailsubject . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75
1.3.1.1.33 error_page . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 751.3.1.1.34 failed_login_attempts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 751.3.1.1.35 fe_editor_lang . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 751.3.1.1.36 feed_modx_news . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 751.3.1.1.37 feed_modx_news_enabled . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 761.3.1.1.38 feed_modx_security . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 761.3.1.1.39 feed_modx_security_enabled . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 761.3.1.1.40 filemanager_path . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 761.3.1.1.41 friendly_alias_urls . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 761.3.1.1.42 friendly_url_prefix . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 771.3.1.1.43 friendly_url_suffix . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 771.3.1.1.44 friendly_urls . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 771.3.1.1.45 mail_charset . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 771.3.1.1.46 mail_encoding . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 771.3.1.1.47 mail_smtp_auth . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 781.3.1.1.48 mail_smtp_helo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 781.3.1.1.49 mail_smtp_hosts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 781.3.1.1.50 mail_smtp_keepalive . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 781.3.1.1.51 mail_smtp_pass . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 781.3.1.1.52 mail_smtp_port . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 781.3.1.1.53 mail_smtp_prefix . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 781.3.1.1.54 mail_smtp_single_to . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 791.3.1.1.55 mail_smtp_timeout . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 791.3.1.1.56 mail_smtp_user . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 791.3.1.1.57 mail_use_smtp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 791.3.1.1.58 manager_date_format . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 791.3.1.1.59 manager_direction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 791.3.1.1.60 manager_lang_attribute . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 801.3.1.1.61 manager_language . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 801.3.1.1.62 manager_theme . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 801.3.1.1.63 manager_time_format . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 801.3.1.1.64 modx_charset . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 801.3.1.1.65 new_file_permissions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 801.3.1.1.66 new_folder_permissions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 801.3.1.1.67 password_generated_length . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 811.3.1.1.68 password_min_length . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 811.3.1.1.69 phpthumb_cache_maxage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 811.3.1.1.70 phpthumb_cache_maxfiles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 811.3.1.1.71 phpthumb_cache_maxsize . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 811.3.1.1.72 phpthumb_cache_source_enabled . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 811.3.1.1.73 phpthumb_far . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 821.3.1.1.74 phpthumb_zoomcrop . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 821.3.1.1.75 proxy_auth_type . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 821.3.1.1.76 proxy_host . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 821.3.1.1.77 proxy_password . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 821.3.1.1.78 proxy_port . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 821.3.1.1.79 proxy_username . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 821.3.1.1.80 publish_default . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 831.3.1.1.81 rb_base_dir . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 831.3.1.1.82 rb_base_url . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 831.3.1.1.83 request_controller . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 831.3.1.1.84 request_param_alias . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 831.3.1.1.85 request_param_id . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 841.3.1.1.86 search_default . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 841.3.1.1.87 server_offset_time . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 841.3.1.1.88 server_protocol . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 841.3.1.1.89 session_cookie_domain . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 841.3.1.1.90 session_cookie_lifetime . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 841.3.1.1.91 session_cookie_path . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 851.3.1.1.92 session_cookie_secure . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 851.3.1.1.93 session_handler_class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 851.3.1.1.94 session_name . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 851.3.1.1.95 settings_version . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 851.3.1.1.96 signupemail_message . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 861.3.1.1.97 site_name . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 861.3.1.1.98 site_start . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 861.3.1.1.99 site_status . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 871.3.1.1.100 site_unavailable_message . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 871.3.1.1.101 site_unavailable_page . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 871.3.1.1.102 strip_image_paths . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 871.3.1.1.103 tree_root_id . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 881.3.1.1.104 udperms_allowroot . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 881.3.1.1.105 unauthorized_page . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 881.3.1.1.106 upload_maxsize . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 881.3.1.1.107 use_alias_path . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88
1.3.1.1.108 use_browser . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 891.3.1.1.109 use_editor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 891.3.1.1.110 use_multibyte . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 891.3.1.1.111 welcome_screen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 891.3.1.1.112 which_editor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 891.3.1.1.113 which_element_editor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90
1.3.2 Using Friendly URLs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 901.3.3 Contexts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91
1.3.3.1 Creating a Subdomain from a Folder using Virtual Hosts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 921.3.3.2 Using One Gateway Plugin to Manage Multiple Domains . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 93
1.3.4 Customizing the Manager . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 941.3.4.1 Form Customization Pages . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 95
1.3.4.1.1 FC-Resource . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 951.3.4.1.2 FC-Template . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 961.3.4.1.3 FC-Chunk . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 971.3.4.1.4 FC-Snippet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 971.3.4.1.5 FC-Plugin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97
1.3.4.2 Form Customization Rules . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 971.3.4.2.1 Field Default . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 991.3.4.2.2 Field Label . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1001.3.4.2.3 Field Visible . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1011.3.4.2.4 Move TV to Tab . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1021.3.4.2.5 New Tab . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1031.3.4.2.6 Tab Title . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1051.3.4.2.7 Tab Visible . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1061.3.4.2.8 TV Default . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1061.3.4.2.9 TV Title . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1071.3.4.2.10 TV Visible . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 108
1.3.5 Security . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1091.3.5.1 Users . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1101.3.5.2 User Groups . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1121.3.5.3 Resource Groups . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1121.3.5.4 Roles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1131.3.5.5 Policies . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 115
1.3.5.5.1 Permissions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1171.3.5.5.2 ACLs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 120
1.3.5.6 Security Tutorials . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1211.3.5.6.1 Giving a User Manager Access . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1211.3.5.6.2 Making Member-Only Pages . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 122
1.3.6 Installing a Package . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1241.3.6.1 Troubleshooting Package Management . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 126
1.3.7 Upgrading MODx . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1261.3.7.1 Upgrading from MODx Evolution . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 127
1.3.7.1.1 Functional Changes from Evolution . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1281.3.7.2 Upgrading to Revolution 2.0.0-rc-2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 129
1.3.8 Moving Your Site to a New Server . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1301.4 Developing in MODx . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 132
1.4.1 Code Standards . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1321.4.2 Overview of MODx Development . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 143
1.4.2.1 Developer Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1431.4.2.1.1 Getting Started Developing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 145
1.4.2.2 Extras Directories . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1461.4.2.3 Setting up a Development Environment . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 146
1.4.3 Basic Development . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1481.4.3.1 Snippets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 148
1.4.3.1.1 Templating Your Snippets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1511.4.3.1.2 Adding CSS and JS to Your Pages Through Snippets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 154
1.4.3.2 Plugins . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1551.4.3.2.1 System Events . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 157
1.4.3.3 xPDO . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1931.4.4 Advanced Development . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 193
1.4.4.1 Namespaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1931.4.4.2 Caching . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1941.4.4.3 Custom Manager Pages . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 195
1.4.4.3.1 Actions and Menus . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2001.4.4.3.2 Custom Manager Pages Tutorial . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2011.4.4.3.3 MODExt . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 203
1.4.4.4 Internationalization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2081.4.4.4.1 Creating Lexicons for Your Components . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 211
1.4.4.5 MODx Services . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2111.4.4.5.1 modMail . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 211
1.4.4.6 Package Management . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2121.4.4.6.1 Transport Packages . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2141.4.4.6.2 Providers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2161.4.4.6.3 Creating a 3rd Party Component Build Script . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 218
1.4.4.7 Extending modUser . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2311.4.5 Other Development Resources . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 235
1.4.5.1 Loading MODx Externally . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2351.4.5.2 API Reference . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2361.4.5.3 Class Reference . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 236
1.4.5.3.1 modX . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2361.4.5.3.2 modChunk . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2611.4.5.3.3 modUser . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 262
1.5 Case Studies and Tutorials . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2681.5.1 Using Custom Database Tables in your 3rd Party Components . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2681.5.2 Creating a Blog in MODx Revolution . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2731.5.3 PHP Coding in MODx Revolution, Pt. I . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2831.5.4 PHP Coding in MODx Revolution, Pt. II . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2851.5.5 PHP Coding in MODx Revolution, Pt. III . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2861.5.6 Loading Pages in the Front-End via AJAX and jQuery Tabs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2881.5.7 Managing Resources and Elements via SVN . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2901.5.8 xPDO XML Schema File vs. Table Structure Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2911.5.9 Adding Custom Fields to Manager Forms . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 293
1.6 MODx Community Information . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2941.6.1 Getting a MODx Account . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2941.6.2 Filing Bug Reports . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2941.6.3 Becoming a Core Contributor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 295
1.6.3.1 Development Environments . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2961.6.3.2 MODx PHP Coding Standards . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 297
1.6.4 Using GitHub . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 298
Home
MODx RevolutionBeen stuck with a bloated portal system that doesn't fit your site's mold? Tired of hacking away at existing CMSes to get things the way you want?Look no further: MODx is here.
The long-awaited MODx Revolution version power-packs all sorts of new features that the MODx community asked for, such as site contexts,improved caching, increased flexibility, transport packaging, a brand-new revamped manager interface and much more.
You asked. We listened. Here is our answer: Revolution.
Want to contribute to Revolution development or testing? See our page.Becoming a Core Contributor
Revolution 2.0 Official DocumentationThis is the official documentation space for MODx Revolution 2.0.
Getting Started
Making Sites with MODx
Administering Your Site
Developing in MODx
Case Studies and Tutorials
MODx Community Information
You can download the documentation in , as of July 21st, 2010.PDF format
Getting Started
Welcome to MODx Revolution. This section provides installation tutorials, beginning concepts around MODx, and general information aboutMODx to get you started.
Server Requirements
Supported Operating Systems
Linux x86, x86-64Mac OS XWindows XP, Server
Supported Web Servers
Apache 1.3.x - 2.2.x (uses htaccess for Friendly URLs by default)IIS 6.0lighttpd ( )Setup and Friendly URL GuideZeus
PHP Compatibility
5.1.1 and above (excluding 5.1.6 and 5.2.0)Required extensions:
zlibJSON (or PECL library)mod_rewrite (for friendly URLs/.htaccess)GD lib (required for captcha and file browser)PDO, specifically pdo_mysql (for xPDO)SimpleXML
Safe_mode offRegister_globals offPHP memory_limit 24MB or more, depending on your server
PHP Configuration Options
./configure --with-apxs2=/usr/local/bin/apxs --with-mysql --prefix=/usr/local --with-pdo-mysql--with-zlib
MySQL Database Requirements
4.1.20 or newer, with the following permissions:SELECT, INSERT, UPDATE, DELETE are required for normal operationCREATE, ALTER, INDEX, DROP are required for installation/upgrades and potentially for various add-onsCREATE TEMPORARY TABLES may be used my some 3rd party add-ons
excludes version 5.0.51 ( )Why not 5.0.51?InnoDB storage engineMyISAM storage engine
Supported Browsers (for Backend Manager Interface)
Mozilla Firefox 3.0 and aboveApple Safari 3.1.2 and aboveMicrosoft Internet Explorer 8 and above
IE7 is not fully supported at this time, but may be in a feature release. You are free to use it, but might experience bugs hereand there.
MySQL 5.0.51 Issues
Why does MODx not support MySQL server version 5.0.51?
MySQL 5.0.51, including 5.0.51a, has serious bugs with PDO, specifically grouping, ordering and prepare statements.
It will cause uncorrectable errors in normal queries in MODx, as well as other open source applications, and therefore MODx does not supportinstallations with MySQL 5.0.51 installed. Please upgrade your MySQL installation.
MySQL 5.0.51 Server Buglist
Here are just some of the bugs that occur:
http://bugs.mysql.com/bug.php?id=32202http://bugs.php.net/bug.php?id=47655http://bugs.mysql.com/bug.php?id=36406
Installation
This page is for New Installations only. If you're looking to upgrade, see .Upgrading MODx
Downloading MODx
First off, you'll want to download MODx Revolution 2.0. Currently, there are two ways you can do so:
From the MODx Site
The quickest way to get your Revolution site up and running is to grab a copy directly from the page. There you will findMODx Downloadsdownloads for MODx Revolution.
It's worth noting that these packages are basically snapshots from Git, our version control software, at the time they were packaged. A lot mayhave changed since then, including bugfixes and the addition of new features. Note the release date for each package. Git will always have thelatest up-to-date snapshot of Revolution.
"Normal" vs. "Advanced"
You have probably noticed that there are a few different of packages to choose from. Some are labeled as "Advanced," others are just atypesplain old "modx-2.0.0-xxxx-#.zip". So what do these labels mean?
Normal - These packages are pre-built snapshots from Git. You can simply extract the files to your server and follow the Basic Installationguide to install MODx. Most users should choose this version.
Advanced - These packages are slightly less than half the size of the "Regular" downloads, since the "core" contents are compressed.MODx Setup will try to unpack or "build" this package during install. It's recommended you only use this if you plan to move the core,manager or connectors directories, and you have SSH access and are familiar with making folders writable. Please follow the Advanced
document for this distribution.Installation
From Git
MODx Revolution 2.0.0 is managed on . Please read the document to learn how to use MODx Revolution from Git.GitHub Git Installation
Installing MODx
MODx comes with multiple distributions for download. Installation steps will differ in each distribution, so please select the distribution's installationguide below:
Normal Distribution: Basic InstallationAdvanced Distribution: Advanced InstallationBuilding from Git: Git Installation
After finishing installation, if you are still having issues, please read the page.Troubleshooting Installation
Basic Installation
Beginning SetupInstall OptionsDatabase Options
Collations and CharsetsCreating an Administrator User
Pre-Installation ChecksPost-Installation SummaryAdditional Info
WAMPServer 2.0iMAMP on MacOSXDebianVista and XAMPPInstalling PackagesSee Also
Beginning Setup
After you've MODx Revolution, start the install process by loading your web browser and running the setup script by navigating to thedownloaded folder.setup/
You might want to check the page first. If you're still having issues installing, please read the Server Requirements page.Troubleshooting Installation
Before running setup, make sure your core/cache/ and core/config/ directories are writable by PHP.
From there you will be asked to choose a language, and be presented with a welcome screen. Click Next when you're ready.
Install Options
After this, you'll be presented with a screen with some Install Options:
The New Installation option should be the only available option for you to choose. If you need to adjust the file permissions for your webserver,you can do so in the textfields below. Most servers will be fine with the default values.
When you're finished, click Next to proceed.
Database Options
From here, you will get a form asking you for your database information:
Add in your database hostname, which is the URL at which your database is located. For most users, this will be 'localhost'. If you have yourMySQL server on a different port, specify it like so: "my.database.com;port=3307", with the ;port= appending the IP/hostname.
Also, if you want, you can specify a different table prefix here. This tells MODx to prefix the tables with this value - this is useful should you want tomake multiple MODx installations on one database.
When finished, click the 'Test database server connection and view collations' link. Should you have any errors, they will show below. If you dohave errors, check to make sure your database username and password are correct. Also, if your user does not have access to create adatabase, you might need to do that manually.
Collations and Charsets
This will then popup another form for setting your database charset and collation:
1.
2. 3.
4.
For most users you can leave these values at what they are. However, if you need to change them, the collation matches the charset.make sureClick the 'Create or test selection of your database.' after you've finished.
Creating an Administrator User
This form will now present you with a few fields for setting up your administrator user. Specify a username that you want to be the administratorusername.
MODx recommends using 'admin', as this is a common administrator username and is often the first username hackersnotcheck.
From there, put in your email (or the email of your administrator) and specify a password. Click next when you're finished.
Pre-Installation Checks
MODx will then proceed with a list of checks to verify that your system is ready for installing. If any of these fail, you'll need to proceed with thedirections that it suggests to make sure your environment meets the and has the correct directories writable.Server Requirements
Once you're ready, and all the checks pass, click 'Install' to proceed.
If you get a blank screen or cannot proceed after clicking 'Install', verify these steps:
Make sure the directories "/[root]", "/core/packages","/core/cache", and "/core/export" are writable. (root will be theactual directory you are installing to.)Make sure your php.ini setting sets memory_limit to 128M, and max_execution_time to 120Create a blank file "/core/config/config.inc.php" and make it writable. DO NOT COPY config.inc.tpl! Just make it ablank file!Post a message in the regarding your issue. State your server setup and installation info, and we'll tryRevolution forumand help you find a solution.
Post-Installation Summary
MODx will then let you know if any errors occurred during install, and prompt you to attempt reinstallation should any of those errors haveoccurred.
When install is successful, click 'Next' to proceed, and you'll be presented with one final option:
MODx recommends that you make sure to remove the setup/ directory after installing, to safeguard your site from anyone else trying to run setupon your site. You can do this by clicking the 'Check this to DELETE the setup directory from the filesystem.' checkbox.
When ready, click 'Login' to be presented with the Login form for the manager interface. You're finished!
Additional Info
Some other special cases:
WAMPServer 2.0i
Please see this article: Problems with WAMPServer 2.0i
MAMP on MacOSX
MAMP (including latest 1.8.4) works fine with MODx Revolution, with one exception. You cannot use eAccelerator as the caching system, as thedrivers compiled with MAMP are faulty with regards to PDO and will cause Apache kernel errors. Select the 'xCache' caching drivers to remedythis.
Debian
Debian uses outdated MySQL drivers for its PHP build that will need to be updated; please see the article for moreMODx Revolution on Debianinformation.
Vista and XAMPP
There have been reported problems with installing Revolution on 64-bit Vista with XAMPP. We cannot guarantee a working solution on that OSand setup at this time.
Some users have reported that applying a fix found here: will fixhttp://www.apachefriends.org/f/viewtopic.php?f=16&t=32617Apache crashing errors with PDO support in XAMPP.
Installing Packages
For information on installing 3rd-party packages, see the article.How to Install Packages
See Also
MODx Revolution on Debian
MODx Revolution on Debian
Debian packages in old versions of MySQL drivers in, so to get it up-to-date and working with the PDO drivers in MODx Revolution, you'll have to
1. 2.
do the following:
Update the server MySQL drivers. You can do this manually on debian, or use apt-get.Update the client MySQL drivers. The easiest way to do this is by updating PHP, like so:
vi /etc/apt/sources.list (you can choose a different mirror): deb http://packages.dotdeb.org stable alldeb-src http://packages.dotdeb.org stable alldeb http://php53.dotdeb.org stable alldeb-src http://php53.dotdeb.org stable all
apt-get updateapt-get upgrade php5
vi /etc/php5/apache2/php.ini date.timezone = Europe/Amsterdam
/etc/init.d/apache2 reload
Lighttpd Guide
Lighttpd Guide for Setup and Friendly URLs.Friendly URL Setup
Lighttpd Guide for Setup and Friendly URLs.
This is still a work in progress, and currently only covers the URL rewriting aspect.This guide assumes you already have a working lighttpd, mysql, and PHP installation.This guide only covers proper settings and the use of friendly URL Rewriting.
Friendly URL Setup
lighttpd does not use the same system, or even same idea as Apache does for URL rewriting. All URL rewriting is done in thelighttpd.conf file
First we need to make sure that the URL rewriting module is enabled.So open your lighttpd.conf config file (In Linux it is usually located in /etc/lighttpd/lighttpd.conf)Look for the directive server.modules.Under this directive, look for an entry named "mod_rewrite",.By default it has a # in front of it. This is a comment symbol. Please remove the # from the line and save the file.
Next we need to find the location in which to put the friendly URL code.So lets search for something that looks like this:
$SERVER[ ] == {"socket" ":80" $HTTP[ ] =~ {"host" "yourdomainname.com" server.document-root = "/path/to/your/doc/root" server.name = "yourservername"
Directly under this you should add the following code.
url.rewrite-once = ( => ,"^/(assets|manager|core|connectors)(.*)$" "/$1/$2" => ,"^/(?!index(?:-ajax)?\.php)(.*)\?(.*)$" "/index.php?q=$1&$2" => "^/(?!index(?:-ajax)?\.php)(.*)$" "/index.php?q=$1" )
This does not mean you are done! Lighttpd handles url-rewrites a bit differently. You HAVE to exclude any files or folders you donot want rewritten in the config file. Excluded dirs/files in the example above are (assets|manager|core|connectors). If you wishto add more to these, simple add another | followed by the folder or filename you wish to omit from url rewriting.
After this is done, you will have working friendly URLs again in lighttpd.
Problems with WAMPServer 2.0i
How to get WAMPServer 2.0i working on MODx Revolution
Mary (einsteinsboi) has a great blog post about using WAMPServer 2.0i with MODx Revolution, and some problems you might encounter.
http://codingpad.maryspad.com/2010/01/11/modx-revolution-and-wamp/
A short summary and explanation is below.
WAMPServer uses mismatched MySQL Server and Client builds
Usually it is best to make sure in any server configuration that your MySQL server and client build versions are the same. WAMPServer allowsyou to start your stack with different versions of PHP/MySQL combinations.
The problem child comes in WAMPServer 2.0i's PHP 5.2.11 version. It sets its server version at 5.1.36, but its client version at 5.0.51a. MODx , and therefore will not install with this configuration.does not support 5.0.51a
The Solution
To fix it, simply start WAMPServer with the PHP 5.3.0 build. WAMPServer 2.0i will set the server to 5.1.36, and the client to 5.0.5-dev. While stillnot optimal, this will allow Revolution to run smoothly without MySQL hiccups.
Advanced Installation
Installation Pre-StepsRenaming or Moving the CoreChanging the Configuration Key
Advanced OptionsDatabase Options
Collations and CharsetsCreating an Administrator User
Context ConfigurationPre-Installation ChecksPost-Installation SummarySee Also
This is the tutorial for the advanced distribution of MODx. It is recommended to only install this distribution if:
You plan on renaming the manager/ or connectors/ directories, or move the core/ directoryYou have SSH access or can easily move/make writable directories on your server.
You might want to check the page first. If after reading this, you're still having issues installing, pleaseServer Requirementsread the page.Troubleshooting Installation
Installation Pre-Steps
After you've MODx Revolution's advanced distribution, upload and extract it to your server. You should be left with two directories -downloadedcore/ and setup/. From here, if you plan on moving the core/ directory, proceed to the next section. If you're not going to do so, or rename theconfig key, browse to in your browser and skip to the section of this document.setup/ Advanced Options
Renaming or Moving the Core
MODx Revolution allows you to rename and/or move the core/ directory to enhance your level of security. You can also move the core/ directoryoutside of the webroot to further secure your MODx installation.
Should you choose to rename or move the core, MODx recommends doing so before installing. Simply rename or move the core, and setup/ - atthe beginning - will present you with a page asking for the new location of the core:
Enter into the textfield the absolute path to where you have moved the core directory. If MODx is able to find the core from there, you will proceednormally with the installation. If MODx still cannot find the directory from the path you specified, check if you have typed it correctly, that it is anabsolute path, and that you've made the directory readable (and the core/cache/ file writable).
MODx might also ask you to make the setup/includes/core.config.php file writable. This is required to change the core path, and you should do sobefore proceeding.
Changing the Configuration Key
From here, MODx will ask you to choose a language. Do so at this time. MODx will then prompt you with a welcome page, and below will ask ifyou want to change the MODx Configuration Key. This allows you to run multiple sites with a shared core, as each individual site will need its ownunique configuration key.
To change it, simply click the link the install tells you to change the config key, and you'll be presented with a textfield:
Specify a custom, unique config key and click next.
Advanced Options
You will now be presented with some options for install, similar to the screen, but with two extra options at the bottom. 'NewBasic InstallationInstallation' will be your only radio option available to check, which is what you want. Below that, you can choose to adjust the permissions forcreating new files or folders in your MODx installation. The defaults should work fine, but if on a more restrictive server, you can change thefolder/file perms to 0775/0664, respectively.
Below that, you will be presented with two checkbox options:
These will be grayed out during new installations. (During upgrades, it is recommended that you uncheck these as well.) Click 'Next' to proceed tothe next step.
Database Options
From here, you will get a form asking you for your database information:
Add in your database hostname, which is the URL at which your database is located. For most users, this will be 'localhost'. If you have yourMySQL server on a different port, specify it like so: "my.database.com;port=3307", with the ;port= appending the IP/hostname.
Also, if you want, you can specify a different table prefix here. This tells MODx to prefix the tables with this value - this is useful should you want tomake multiple MODx installations on one database.
When finished, click the 'Test database server connection and view collations' link. Should you have any errors, they will show below. If you dohave errors, check to make sure your database username and password are correct. Also, if your user does not have access to create adatabase, you might need to do that manually.
Collations and Charsets
This will then popup another form for setting your database charset and collation:
For most users you can leave these values at what they are. However, if you need to change them, the collation matches the charset.make sureClick the 'Create or test selection of your database.' after you've finished.
Creating an Administrator User
1.
2. 3.
4.
This form will now present you with a few fields for setting up your administrator user. Specify a username that you want to be the administratorusername.
MODx recommends using 'admin', as this is a common administrator username and is often the first username hackersnotcheck.
From there, put in your email (or the email of your administrator) and specify a password. Click next when you're finished.
Context Configuration
MODx will now present you with a detailed context installation screen. This is where you can configure the paths to your web context (the maincontext), as well as the directories for your connectors/ and manager/ folders. MODx recommends leaving the web/ context paths as they are,unless you have a special reason not to.
Renaming your manager/ and connectors/ directories, however, can add an extra level of security to your site. Simply change the paths and URLsin the textfields provided. Note: If you do change the directories, the directories any of those paths must be writable to allow MODx to writeabovethe manager/ and/or connectors/ directories to them.
Make sure you change the path and URL!both
When done, click 'Next' to proceed.
Pre-Installation Checks
MODx will then proceed with a list of checks to verify that your system is ready for installing. If any of these fail, you'll need to proceed with thedirections that it suggests to make sure your environment meets the and has the correct directories writable.Server Requirements
Once you're ready, and all the checks pass, click 'Install' to proceed.
If you get a blank screen or cannot proceed after clicking 'Install', verify these steps:
Make sure the directories "/[root]", "/core/config", "/core/packages","/core/cache", and "/core/export" are writable. (rootwill be the actual directory you are installing to.)Make sure your php.ini setting sets memory_limit to 128M, and max_execution_time to 120Ensure that MODx can create the manager and connectors directories; this is done by making the parents of thosedirectories writable (since you can change where they are installed)Post a message in the regarding your issue. State your server setup and installation info, and we'll tryRevolution forumand help you find a solution.
Post-Installation Summary
MODx will then let you know if any errors occurred during install, and prompt you to attempt reinstallation should any of those errors haveoccurred.
When install is successful, click 'Next' to proceed, and you'll be presented with one final option:
MODx recommends that you make sure to remove the setup/ directory after installing, to safeguard your site from anyone else trying to run setupon your site. You can do this by clicking the 'Check this to DELETE the setup directory from the filesystem.' checkbox.
When ready, click 'Login' to be presented with the Login form for the manager interface. You're finished!
See Also
Git Installation
Installation ProcessGit Location
Stable BranchesDevelopment Branches
Run the BuildRun Setup
Upgrading Your Local Git Repository After CommitsSending Pull RequestsSwitching Branches
Additional InformationUsing MAMP on Mac OS X
Installation Process
Here are some notes on participating in MODx Revolution testing and/or development. Unlike previous versions of MODx, Revolution will notinstall directly from Git. Because of the nature of the new packaging and installation system, you must first create the core installation packageusing a PHP build script before running the setup.
Git Location
Git clone the revolution repository on GitHub at: using this syntax:http://github.com/modxcms/revolution/
git clone http://github.com/modxcms/revolution.git
Or, if you'd like to contribute back, and clone that repository with:fork it in your GitHub repository
git clone [email protected]:yourgitusernamehere/revolution.git
Forking it with your GitHub account will allow you to contribute back to MODx by sending pull requests by clicking the "Pull Request" button onyour GitHub page. (You'll need to before we can accept your code, though.)submit a CLA
If you're not familiar with Git, please read the excellent tutorial from and view the .GitHub GitHub help pages
From there, make sure you are working on the branch, if you're wanting the latest bugfix release. There are three current branches in the2.0modxcms/revolution GitHub repository:
Stable Branches
master - This will usually match the latest release; ie, 2.0.0-pl. It is the stable branch, and is only changed during releases.
Development Branches
2.0 - The latest development branch for 2.0.x releases; all patches (not new features) are committed to here.2.1 - For new features for 2.1. All feature branches eventually integrate into this branch. Patches from the 2.0 branch are merged to herefrom time to time.
To create a local tracking branch from one in the origin remote; after cloning, just type:
git checkout -b 2.0 origin/2.0
And git will handle the rest.
There will be other branches in the future, centered around new features. New branches will be feature-specific and merged intothe 2.1 branch when they are complete and stable.
Run the Build
If this is the first time you are building from Git, copy the file _build/build.config.sample.php to _build/build.config.php and edit the properties topoint at a valid database with proper credentials. NOTE that this database does not have to contain anything; the build script just needs to be ableto make a connection to a MySQL database.
From the command line, change your working directory to _build/ and execute the command "php transport.core.php". If the PHP executable isnot in your path, you will need to either edit the path or give the full path to the PHP executable in the command line. The build process may takean extended period of time (10 to 30 seconds likely), so be patient. (Note: on Mac Mini (1.66Ghz Intel Core Duo with 2GB RAM) running theLeopard development environment as outlined below, this only takes 5-10 seconds.)
Note that you can also do this from the browser by browsing to the _build/transport.core.php directory, if that directory isaccessible in your web server setup.
Once that script is finished executing, confirm that you now have a file named core/packages/core.transport.zip and a directorycore/packages/core/ containing a manifest.php and many other files/directories.
Run Setup
Now you are ready to execute the new setup script at the setup/ URL (e.g. if installed in a subdirectory of the webhttp://localhost/modxrevo/setup/root named modxrevo/).
Make sure you check both the "Core package has been manually unpacked" and "Files are already in-place" options wheninstalling from Git.
If you change any paths on the Context Paths setup step, make sure and move the corresponding directories as appropriate; this is intended forinstalls from the core package with files not already in-place, where the installer will place the files in the specified locations (assuming thelocations allow the PHP process to write to them).
The actual install process requires more than the default 8M of memory allocated to PHP in many default php.ini files; if you geta blank page when you click "install", try increasing the memory_limit configuration to 32M or more (16M may work, but why notgive php a little space, eh?).
Upgrading Your Local Git Repository After Commits
Simply run these two commands:
git fetch origingit rebase origin/2.0
And Git will update your install. (Substitute '2.0' for '2.1' or 'master' if you're running from another branch.)
When a commit is made, this message might show up in the commit:
[ReUp] - If your updates require a core transport rebuild (such as anything modified in the _build directory, database model changes, or
default data changes), then prefix your commit message with this. If you see this message, simply rebuild the core transport and runsetup/ again.
If this message does not show up, you're done after you fetch and rebase.
Sending Pull Requests
If you've fixed a bug or added an improvement, and you're working on a fork of the revolution repository, you can send a pull request to MODxand one of the Integration Managers will review your patch. you are sending pull requests to the development branches, ie 2.0 forMake surepatches or 2.1 for new features. Pull requests to master will be ignored.
You'll need to before we can accept your code.submit a CLA
Switching Branches
If you want to switch to a different branch, simply type these commands:
git fetch origingit checkout 2.1
Of course, replacing 2.1 with the name of the branch you want to switch to. After you've done so, run the build and run setup/ again, sincedifferent branches might have different databases.
Switching is not always recommended; ie, switching from 2.1 to 2.0, since database changes don't alwaysbackwardsnecessarily 'backport'. While no major issues should occur, be careful when doing this.
Additional Information
Using MAMP on Mac OS X
If you use MAMP on Mac OS X, you may get problems (errors about DYLD libraries not being included) when trying to execute''transport.core.php'' from the terminal. This is because the MAMP PHP libraries won't be on the dynamic linker path by default.
To adjust the dynamic linker library path to include the MAMP PHP libraries, run the following command via the terminal:
export DYLD_LIBRARY_PATH=/Applications/MAMP/Library/lib:$\{DYLD_LIBRARY_PATH\}
You can then execute ''transport.core.php'' by using the absolute path to the MAMP PHP executable:
/Applications/MAMP/bin/php5/bin/php transport.core.php
Troubleshooting Installation
Common ProblemsPDO Error MessagesCommon Error Messages
"I get a blank white screen instead of the options page!""I clicked install and got a blank white screen!""Cannot connect to database" in the database options pageWarning: PDO::__construct() [pdo.--construct]: [2002] Argument invalid (trying to connect via unix://) OR "Checkingdatabase:Could not connect to the mysql server."The login page keeps redirecting me back to the login screen with no errorThings sometimes don't load, the page flakes out, etc (eAccelerator)Resource / Elements / File tree not appearingI can't login to the manager after installing!
Still Having Issues?
Common Problems
First off, make sure:
You have eAccelerator disabled during install. eAccelerator can cause problems when doing the heavy lifting during the install process.You followed all the directions for your distribution.hereYou are using at least PHP 5.1.1+, but not 5.1.6 or 5.2.0You are using MySQL later than 4.1.20, but not any iteration of MySQL 5.0.51 (including 5.0.51a).
PDO Error Messages
If you are getting PDO-related error messages during install, before proceeding to specific error messages as below, please confirm that yourPDO configuration is setup correctly. You can do so by running this code (replace user/password/database/host with your setup):
<?php/* Connect to an ODBC database using driver invocation */$dsn = 'mysql:dbname=testdb;host=localhost';$user = 'dbuser';$password = 'dbpass';
{try $dbh = PDO($dsn, $user, $password);new} (PDOException $e) {catch echo 'Connection failed: ' . $e->getMessage();}?>
If this fails, then your PDO setup is not configured correctly.
Common Error Messages
Here are some common problems that might occur during installation and their solutions:
"I get a blank white screen instead of the options page!"
You probably copied config.inc.tpl to config.inc.php, which is incorrect. Make the config.inc.php file an empty, writable file.
If you renamed the config.inc.tpl to config.inc.php, rename it back to config.inc.tpl and create a blank file named config.inc.php that is writable.
"I clicked install and got a blank white screen!"
Make sure your 'memory_limit' setting in php.ini is set to at least 32M. For slower servers, you might need to up it to 64M.
"Cannot connect to database" in the database options page
One of the common causes of this problem is that you're using a non-standard port for MySQL. Try putting this syntax into the hostname field(replacing the data with your mysql server's host and port):
my.database.com;port=3307
Warning: PDO::__construct() [pdo.--construct]: [2002] Argument invalid (trying to connect via unix://) OR "Checkingdatabase:Could not connect to the mysql server."
This means your MySQL socket is incorrectly configured. Usually this can be remedied by adding to (or updating) your php.ini:
mysql.default_socket=/path/to/my/mysql.sockmysqli.default_socket=/path/to/my/mysql.sockpdo_mysql.default_socket=/path/to/my/mysql.sock
The login page keeps redirecting me back to the login screen with no error
This can happen with older Revolution beta installs. To fix it, delete the following 3 system settings from the DB table `prefix_system_settings`(where prefix is your table prefix):
session_namesession_cookie_pathsession_cookie_domain
1. 2. 3. 4.
Then delete the core/cache/config.cache.php file.
Unless, of course, you've changed these explicitly for some purpose of your own.
Things sometimes don't load, the page flakes out, etc (eAccelerator)
Are you running eAccelerator? In some server configurations, this can cause problems. You might need to disable it. You can do so via yourphp.ini:
eaccelerator.enable = 0;eaccelerator.optimizer = 0;eaccelerator.debug = 0;
or in your .htaccess in the modx root directory, if your server supports php_flag server directives:
php_flag eaccelerator.enable 0php_flag eaccelerator.optimizer 0php_flag eaccelerator.debug 0
Resource / Elements / File tree not appearing
Additional, page "flake outs" may stem from items stored within your own browser's cache, which may result with the resource / elements / filetree not appearing due to old versions of javascript and other files being utilized on the client side. This can be verified by accessing the managerwith a browser not previously utilized in doing so.
The simple fix: clear your browser's cache, and log back into the manager.
A more complete solution:
Under System Clear CacheUnder Security Flush Permissions and then Flush SessionsThis will dump everything and log you outLast step Clear your browser cache
I can't login to the manager after installing!
If you're redirecting back to the login screen every time, try setting this in your .htaccess file in the root of your MODx install:
php_value session.auto_start 0
More common issues to come...
Still Having Issues?
If you're still having problems, post your error and your server environment information in , and we'll try and address your issue asour forums heresoon as possible.
Successful Installation, Now What Do I Do?
Creating the first pageCreating a Template
After a successful installation, you'll be presented with the Manager login page. Use the login and password you specified during the installation,log in. You will be presented with something like this
Since Revolution RC1 doesn't come with any default content, there aren't any pages, which is why this list of error messages. Therefore, the firstthing to do is create a page to make these errors go away.
Creating the first page
This being MODx, there are several ways to create a new resource (document). The main Site menu has a "New Document" menu item, the TreeView block on the left will have the first section, Web Resources, open (if it's not open, click on the Web Resources bar to open it) with its menubar so you can use the Create Resource button (the third from the left, the yellow folder with the green plus sign on it), or you can right-click onthe web context icon, choose Create, then Create a Document Here.
The New Document window will appear. On the right, taking up most of the page, will be the document's fields. To begin with, just give thedocument a title, Home, a Menu Title, First Document, and some content. Make sure to check the Published check box. Click on the floating Savebutton to save your new document.
And now, if you view the tree on the left, you'll see your new document listed. Go to the main Site menu, click the Preview menu item, and viewyour page in all its glory!
Creating a Template
Obviously, the page alone is missing something. We need to create a template to give it some structure and style. Click on the Tree View's'Elements' tab. This will open a selection of elements you can create and manage to add dynamic content to your page. Right-click on theTemplate element, and you'll see two choices for creating a new template, New Template and Quick Create Template.
The Quick Create Template opens a pop-up window to allow for quickly creating a template without moving from the page you're workingon.The New Template changes the right panel to a more complex form for creating a new template.
In either case, give the template a name, My Template, a description, First Revolution Template, and then the HTML code for the template. MODxtemplates are basically just HTML pages, with the content parts replaced with MODx tags. So to begin with, let's just create a really simpletemplate.
<!DOCTYPE html PUBLIC "- //W3C//DTD XHTML 1.1//EN""http: >//www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"<html xmlns="http: xml:lang= >//www.w3.org/1999/xhtml" "en"<head> <title>My First Revolutionary Page</title> <meta http-equiv= content= />"Content-Type" "text/html; charset=utf-8" <style type= media= >"text/css" "screen" #content{width:80%;margin:auto;border:5px groove #a484ce;} #content h1{color:#a484ce;padding:10px 20px;text-align:center;} #content p{padding:20px;text-align:center;} </style></head><body> <div id= >"content" [[*content]] </div></body></html>
Save the new template. Now if you open your first document for editing, you'll see that it's been assigned the template (since it's the only one, andthe document didn't have one). Go back to the site and refresh the page. And now, if you click the main Home menu, you'll get the Manager homepage without that long list of errors!
Using MODx Revolution from SVN
Installation ProcessSVN LocationsRun the BuildRun Setup
Upgrading After CommitsUsing MAMP on Mac OS X
Installation Process
Here are some notes on participating in MODx Revolution testing and/or development. Unlike previous versions of MODx, Revolution will notinstall directly from SVN. Because of the nature of the new packaging and installation system, you must first create the core installation packageusing a PHP build script before running the setup.
SVN Locations
Checkout or export the latest Revolution code from SVN at the URL:
http://svn.modxcms.com/svn/tattoo/tattoo/branches/2.0/ - Latest 2.0 devhttp://svn.modxcms.com/svn/tattoo/tattoo/branches/2.0-ui/ - For default mgr UI/design work
Run the Build
If this is the first time you are building from SVN, copy the file build.config.sample.php to build.config.php and edit the properties to point at a validdatabase with proper credentials. NOTE that this database does not have to contain anything; the build script just needs to be able to make aconnection to a MySQL database.
From the command line, change your working directory to _build/ and execute the command "php transport.core.php". If the PHP executable isnot in your path, you will need to either edit the path or give the full path to the PHP executable in the command line. The build process may takean extended period of time (10 to 30 seconds likely), so be patient. (Note: on Mac Mini (1.66Ghz Intel Core Duo with 2GB RAM) running theLeopard development environment as outlined below, this only takes 5-10 seconds.)
Note that you can also do this from the browser by browsing to the _build/transport.core.php directory, if that directory isaccessible in your web server setup.
Once that script is finished executing, confirm that you now have a file named core/packages/core.transport.zip and a directorycore/packages/core/ containing a manifest.php and many other files/directories.
Run Setup
Now you are ready to execute the new setup script at the setup/ URL (e.g. if installed in a subdirectory of the webhttp://localhost/modxrevo/setup/root named modxrevo/).
Make sure you check both the "Core package has been manually unpacked" and "Files are already in-place" options wheninstalling from SVN.
If you change any paths on the Context Paths setup step, make sure and move the corresponding directories as appropriate; this is intended forinstalls from the core package with files not already in-place, where the installer will place the files in the specified locations (assuming thelocations allow the PHP process to write to them).
The actual install process requires more than the default 8M of memory allocated to PHP in many default php.ini files; if you geta blank page when you click "install", try increasing the memory_limit configuration to 32M or more (16M may work, but why notgive php a little space, eh?).
Only the absolute required minimal data is included; there are no documents or add-ons installed by default, etc. as of this time.
Give it a try at your earliest convenience and record any issues or problems you encounter. There is a lot of work going on still, and plenty left togo, so expect the unexpected as we work through changes, fixes and refactorings.
See for recommended tools and setups for contributing directly to MODx core code.Development Environments
Upgrading After Commits
When a commit is made to an SVN branch, one of two messages might show up in the commit.
[REBUILD/UPGRADE REQUIRED] - If your updates require a core transport rebuild (such as anything modified in the _build directory,database model changes, or default data changes), then prefix your commit message with this. If you see this message, simply rebuildthe core transport and run setup/ again.
If neither of these messages show up, you simply need to svn update, and you're done.
Using MAMP on Mac OS X
If you use MAMP on Mac OS X, you may get problems (errors about DYLD libraries not being included) when trying to execute''transport.core.php'' from the terminal. This is because the MAMP PHP libraries won't be on the dynamic linker path by default.
To adjust the dynamic linker library path to include the MAMP PHP libraries, run the following command via the terminal:
export DYLD_LIBRARY_PATH=/Applications/MAMP/Library/lib:${DYLD_LIBRARY_PATH}
You can then execute ''transport.core.php'' by using the absolute path to the MAMP PHP executable:
/Applications/MAMP/bin/php5/bin/php transport.core.php
An Overview of MODx
What is MODx?
MODx is a Content Application Platform. What does this mean? Well, that depends on who you are:
End-Users (Average Joe)
MODx offers you a system that lets you publish your offline content onto the web in any form, shape or presence you want. It alsooffers a completely customizable backend interface that you can make as simple (or as complex) as you like.
You can setup everything from a simple site, to a blog, to a full-scale web presence with MODx, and keep your admin interface
simple and usable. Drag and drop your webpages around to reorder and move them. Get a full WYSIWYG view of your Resources.Leave Resources unpublished before you finish them. Schedule Resources to publish at certain times.
MODx helps you organize your content the way you want it, and get stellar built-in SEO results. MODx is fully, 100% Friendly URLcompatible, so getting mysite.com/my/own/custom/url.html is incredibly simple, and as easy as structuring your site that way.
Designers (Cool Carl)
Ever wanted complete freedom with your HTML and CSS? Tired of hacking existing systems to get your design towork the way you comp'ed it? MODx does not generate one single line of HTML - it leaves the front-end design upto you.
You can use MODx as your Content Management System (CMS) of choice, as MODx offers completely flexibletemplating and no-holds-barred content delivery. Put your CSS and images where you want them. And once you'redone designing, either hand off the development duties to your developer, or point-and-click install Extras straightfrom within the manager. Simple.
Developers (Badass Billy)
You've looked at different CMSes, but have found developing in them to be either a mishmash of too manyunconnected code pieces, or simply not powerful or elegant enough. You've looked at PHP frameworks, and havefound they have the power, but don't do Content Management nor have a good enough UI for your clients. Youwant the power and flexibility of a framework, with the UI and content management of a CMS.
Enter MODx Revolution. A completely flexible, powerful and robust API, built on OOP principles and using aPDO-powered Object Relational Model (ORM) called . Add in a rich, -powered UI for your clients,xPDO Senchathat's fully customizable. Custom properties and sets. Internationalization support. Package distribution built-in soyou can pack up your code, and distribute it across any Revolution install. Add custom manager pages to run entireapplications within MODx.
Basic Concepts
MODx, in essence, has a ton of moving parts. But the basics parts are:
Resources
Resources are basically a webpage location. It can be actual HTML content, or a file, forwarding link, or a symlink, or anything else.
Templates
Templates are the house a Resource lives in. They usually contain the footer and header for a page.
Template Variables
Template Variables (TVs) are custom fields for a Template that allow the user to assign dynamic values to a Resource. A great example would bea 'tags' TV that allows you to specify tags for a Resource. You can have an unlimited number of TVs per page.
Chunks
Chunks are simply small blocks of content, be it whatever you want inside it. They can contain , or any other Element type (Snippet,SnippetsChunk, TV, etc).
Snippets
Snippets are dynamic bits of PHP code that run when the page is loaded. They can do anything you can code, including building custom menus,grabbing custom data, tagging elements, processing forms, grabbing tweets, etc.
Plugins
Plugins are event hooks that run whenever an event is fired. They are usually used for extending the Revolution core to do something during apart of the loading process - such as stripping out bad words in content, adding dictionary links to words, managing redirects for old pages, etc.
So What Happens on a Request?
MODx loads the requested , fetches that Resource's , and then places the Resource's content in that Template. MODx thenResource Templateparses the resulting combined content, including any tags that might be in it, in the order they are reached. From there, it outputs the response tothe user's browser.
See Also
Glossary of Revolution Terms
Add-onAssetBack-endCategoryChunk TagsComponentContent ElementContent TypeContextContext SettingCore WorkspaceDocumentDocument IdentifierExtensionFile ResolverLanguage TagsLexiconLexicon Topic (formerly Foci)Link TagsManagerNamespacePlaceholder TagsResource FieldPropertyProperty SetResourceResource IdentifierResource TagsResolver (for xPDOVehicles)Setting TagsSnippet TagsStatic ResourceSymlinkSystem SettingTemplate VariablesTransport PackageTransport Provider (formerly Provisioner)Transport VehiclesUser SettingWeblinkValidator (for xPDOVehicles)xPDOVehicle
Add-on
A MODx Third-party Component (3PC) that does not modify the Core or extend any of its classes, but yet still provides extra functionality to theMODx instance.
Asset
Any type of file resource that is usually located in the /assets directory, as defined by the constant MODX_ASSETS_PATH; can includeThird-party Components, libraries, image files, css files, JavaScript files, class files, etc.
Back-end
A synonym for the MODx manager interface.
Category
An optional classifying name that can be attached to any Element or PropertySet (and other objects in later versions of Revolution) that separatesit from other similar objects.
Chunk Tags
Tags in the form [[$ChunkName]] that can be used in reference to Chunks.
Component
Also called "Third-party Component", or 3PC, a Component usually provides extra functionality to MODx, usually in the form of an Add-on, CoreExtension, or Template.
Content Element
Also called just "Element", a single Template, Template Variable, Chunk, Snippet, Plugin, Category, or Property Set visible in the ManagerElements tree.
Content Type
Sets the extension, mime-type and binary setting for any Resource.
Context
A delineator of resources and settings that can be used for a variety of reasons; usually used to create multiple-context sites, such assubdomains, multi-language sites, etc.
Context Setting
A single setting for that Context that either creates a new setting or overrides a System Setting.
Core Workspace
Each unique MODx Core is represented by a named Workspace. When you install Revolution initially, the MODx Core used by the setupapplication is recorded into the MODx database as the Default MODx Workspace. In future MODx Revolution releases, there will be an ability tomanage multiple Workspaces from a single database, directly from the manager application. This will make it easy to isolate upgrades to theMODx Core; by quickly adding a new Core Workspace and selecting a menu option, you'll be able to apply an entire new MODx Core installationto production sites after testing on a staging site, or quickly revert to a previous Core Workspace you know works. This will be especially importantfor multi-site configurations running on shared MODx Core installations.
Document
A specific type of Resource, usually pertaining to a normal website page.
Document Identifier
See Resource Identifier.
Extension
Also called "Core Extension". A MODx Third-party Component that modifies the MODx Core, such as a custom User or authentication class,caching mechanisms, or context manipulation classes.
File Resolver
A type of xPDOVehicle Resolver that copies files from the source location to the target location in a Transport Package.
Language Tags
Tags in the form [[%LanguageStringKey]] that reference MODx Lexicon entries.
Lexicon
A Lexicon is a dictionary of words and phrases organized by Culture (more specific than language, i.e. en-UK) that is used to internationalize themanager application and can be used by Add-On and Core Extension developers to provide localization facilities for their own components. Thisreplaces the legacy MODx language files and allows customization of the entries directly from the manager application.
Lexicon Topic (formerly Foci)
A set of Lexicon Entries focused on a particular subject. Revolution only loads Lexicon Entries as it needs them, by their Topic, to reduce loadtimes.
Link Tags
Tags in the form [[~ResourceId]] that reference the URL of a particular Resource.
Manager
The back-end of the MODx interface.
Namespace
An organizational tag for Components to use to identify Lexicon Entries, Settings, and other objects related to the Component in a Revolution site.Also specifies an absolute path in which the Component may be found.
Placeholder Tags
Tags in the form [[+PlaceholderName]] that reference MODx Placeholders, usually set with $modx->setPlaceholder('placeholderName','value') ina Snippet or Plugin.
Resource Field
Any of the fields of the site_content table, such as title, longtitle, introtext, alias, menuindex, etc. Some fields are available on the DocumentCreate/Edit screen and via Resource Tags; Others can only be accessed via the documentObject.
Property
A single variable for an Element; used to set a specific parameter for the Element.
Property Set
A collection of variables used for a particular purpose with an Element. Property Sets are attached to Elements and pass in the Properties thatthey carry as parameters to the Element. An example is a custom Property Set for a Snippet that passes in specific parameters to the Element,overriding the default behavior.
Resource
A type of container that is interpreted by the Parser to fetch content. Can have any number of derivative classes; the most common is aDocument.
Resource Identifier
Also called a Document ID, Resource ID, or Document Identifier; the number in parenthesis in the MODx Resource Tree in the Manager thatuniquely identifies the Resource specified.
Resource Tags
Tags in the form [[*ResourceField/TV]], which can be used to refer to Resource Fields, or Template Variables.
Resolver (for xPDOVehicles)
Post-processor: a script or predefined action that is evaluated after a Vehicle is installed or uninstalled. Resolvers always occur after the vehicle'sobject is save()'d, and can then perform actions on MODx before anything else happens in the install/uninstall process.
An example of a PHP Resolver is one that attaches Plugin Events to a newly-installed Plugin.
An example of a file Resolver is one that copies the assets/ditto directory in the xPDOVehicle path to /modx/assets/ditto.
Setting Tags
Tags in the form [[++SettingName]] that reference MODx System Settings, Context Settings, and User Settings.
Snippet Tags
Tags in the form [[SnippetName]], also referred to as Snippet Calls.
Static Resource
A specific type of Resource that is a direct reference to a specific file on the MODx site. The content is replaced with the contents of that file.
Symlink
A type of Resource that references a single, local MODx Resource; the Resource's content will replace the Symlink's content.
System Setting
A site-wide variable accessible to the MODx site. Can be overridden by Context Settings and User Settings.
Template Variables
Custom Resource Fields created by the user on the Document Create/Edit Screen and referenced using Content Tags.
Transport Package
A packaged and zipped collection of Transport Vehicles, that can be easily distributed ("transported") from one Core Workspace to another.
Transport Provider (formerly Provisioner)
A web service that enables remote installation of Transport Packages directly from the MODx manager application.
Transport Vehicles
An intelligent container that encapsulates any artifact that can be distributed in a Transport Package. Transport Vehicles store their payloads in a
portable format.
User Setting
A user-specific setting that either creates a new setting or overrides the similar Context Setting and System Setting. Used to provide uniquesettings to that user.
Weblink
A type of Resource that references a specific URL or MODx Resource, redirecting the visitor to that URL or Resource.
Validator (for xPDOVehicles)
Pre-processor: a script or predefined action that executes prior to the vehicle being installed or uninstalled. If the validator returns true, theinstall/uninstall action will proceed as normal. If the validator returns false, MODx will not uninstall or install the package.
A Validator could be used to determine if a directory exists and is writable, to see if other modx elements are already installed, or to determine if acertain version of MySQL and PHP are used on a server.
xPDOVehicle
The base Transport Vehicle class, xPDOVehicle, stores xPDOObject instances (which represent a row of data in a table) in it's payload, alongwith various attributes that control how the object is installed/uninstalled in a Core Workspace (see xPDOVehicle Validators and xPDOVehicleResolvers).
Explanation of Directory Structure
The root directory of MODx is split into several subdirectories, each with its own set of responsibilities and tasks. Some of these directories can berenamed and moved, and their locations can be configured during setup.
connectors/Notable Files
core/core/cache/
core/cache/logs/core/cache/mgr/core/cache/rss/core/cache/web/
core/components/core/config/core/docs/core/error/core/export/core/import/core/lexicon/core/model/
core/model/modx/core/model/modx/mysql/core/model/modx/processors/
core/model/schema/core/model/smarty/
core/packages/core/xpdo/Notable Files
manager/manager/assets/manager/assets/controllers/manager/assets/templates/Notable Files
setup/_build/
Notable Filesassets/
assets/components/
connectors/
Connectors are essentially entry points for AJAX requests in MODx. They don't do any database manipulation on their own; they simply load upthe main MODx class, sanitize any request data, and then handle the request by pointing to the appropriate Processor file.
For example, when we create a resource, we request connectors/resource/index.php?action=create. The index.php file will include the baseconnector file (connectors/index.php) which instantiates the main MODx object, handle any custom switching, and sanitize the GET orContextPOST request. The connectors/resource/index.php will then "handle" the request and call the correct Processor file, which we will discuss later.
Notable Files
connectors/index.php - This file is particularly useful in creating your own connectors. Simply include this file in your connectors, andthen handle the request using $modx->request->handleRequest();
core/
The Core is what makes MODx, MODx. It is the base for all the libraries for Revolution. Most everything you need, with the exception of themanager files and the setup files, are in this directory.
core/cache/
The cache directory contains all of the cache files generated by MODx. Lexicons, elements, resources, RSS, and Smarty data are generatedon-demand by MODx, meaning that they are only cached after being accessed for the first time.
core/cache/logs/
All file logging in MODx is done here. You will find the error.log file here, which contains the date, time, file, and error which was logged by MODx.
To log an entry to this file, you can use the $modx->log() method.
core/cache/mgr/
This directory contains cache data for the mgr (Manager) context. Like every context cache, it will cache any context settings that have beenoverridden from their default System Settings.
core/cache/rss/
A cache of every RSS feed in MODx.
core/cache/web/
Unlike the cache in MODx Evolution, the MODx Revolution cache is split up into several parts. Every context (ie. web and mgr) has acontext.cache.php file. This file is like the config.cache.php file, except that it only caches settings that have been overridden from their defaultSystem Setting. Any context can override a system setting.
Additionally, the web context cache will contain separate directories for resources and elements. A resource with ID 12 will be found atcache/web/resources/12.cache.php. This new caching mechanism means that loading times will decrease, and the limit on the number ofcacheable resources will disappear.
core/components/
When you install a package using the , a core/components/<component_name>/ directory will be created to hold any filesPackage Managernecessary for the installed component to run. Typically, any files needed to run in the Manager, such as controllers, model/schema data,processors and class files, should be stored here, as well as files you don't want web-accessible.
core/config/
This directory contains the configuration file for MODx Revolution. It sets up database credentials and a number of MODX_ constants for theproper operation of your site.
core/docs/
This directory contains the changelog.txt file, the GPL license, and any tutorials that have been created for Revolution.
core/error/
This contains default templating for error response messages in Revolution's front-end. You can customize those pages here.
core/export/
1.
2.
After running the Export function in MODx Revolution, the exported HTML files for your site will be located here.
core/import/
To run the Import function in MODx Revolution, you need to move your HTML files into this directory.
core/lexicon/
Lexicons in Revolution are different from language files in Evolution for two main reasons.
First, in Revolution, lexicon files are split up into separate directories, depending on their two-digit IANA code (for example, English lexicons arestored in /core/lexicon/en/). Inside these subdirectories are multiple files, in the format "topic.inc.php". A "topic" is simply a single lexicon file.Splitting lexicons into topics means that only the language strings are loaded, saving memory and loading time.required
Second, all lexicons are stored in the MODx database, and later cached on-demand. This makes it possible to manage lexicons directly from theManager, inside the Lexicon Management area.
To load a lexicon, one would use a format such as this:
$modx->lexicon->load( 'lang:namespace:topic' );
# - the 2-digit IANA code. This is optional, and defaults to 'en'.lang
namespace - Each lexicon has its own . The built-in namespace for MODx is "core". Package creators will also be able toNamespacecreate a custom namespace, and Manager users can also create their own namespaces as well.topic - The specific topic/file you want to load.
core/model/
This is the model. What's a model, you say? Well, it's the M in MVC (model-view-controller), which is an OO paradigm that states that thereshould be at least three parts to an application. The Model, which contains the structure of the database and the hooks into it; the View, which isthe GUI part of the application that contains no logic - just presentation; and the Controllers, which connect the model to the view.
So, MODx does model sort-of similar. We actually do a MVC/C model, in which we add a Connector access point and Processors to the model.We'll explain those as we come to them. What you need to know is that the model contains all the PHP classes that run Revolution, including theprocessors that handle specific functions - such as saving snippets, removing chunks, etc.
core/model/modx/
"Wait! I thought we were already in a modx dir? Why another modx subdirectory?" Good question. Well, MODx Revolution uses xPDO for itsdatabase management. xPDO uses the idea of 'packages' for different connections to different models. So, if I wanted to create my customtables, I'd create a new xPDO package, and add it in at runtime. This way I could use the maps and classes created without having to modify theMODx core. This is shown in the tutorial.Creating a 3rd Party Component
So, that said, it can be inferred that the core/model/modx directory is referring to the "modx" package. Let's go inside it, and you'll see a ton ofclasses. These are the classes that are either xPDOObjects - which are PHP classes that represent tables in the DB (ie, modsnippet.class.php isa PHP class that is an object of modx_site_snippets), or they are functional classes, such as modcachemanager.class.php.
The subdirectories in this folder - not including mysql or processors - are subcategories of classes, that are loaded like:$modx->loadClass('transport.modPackageBuilder'); with the "." being the separation of directories.
core/model/modx/mysql/
This directory contains the class and map files for each xPDO object. Maps are simply PHP arrays containing the structure of the database tablethey reference.
Other database platforms such as pgsql, mssql, and others would also appear here.
core/model/modx/processors/
This directory contains the individual processor files used in database manipulation. They are never accessed directly, and instead are accessedthrough connectors. This allows you to lock them down to prevent unauthorized access.
core/model/schema/
The schema is the XML representation of the MODx database. This is used in building new maps and classes, but is never actually read orparsed when MODx is running. For the most part, you can ignore this directory, as it is mainly used for development work. The tutorials on
teach more about schemas.creating 3rd party components
core/model/smarty/
This contains the Smarty libraries. It's simply an extraction of the Smarty files you can get from . Nothing in this folder ishttp://smarty.php.netcustomized for MODx - that happens elsewhere.
Smarty is an intelligent, object-oriented templating engine that uses dynamic, modifiable placeholders. Most pages seen in the Manager andduring Setup are Smarty template (.tpl) files that MODx interacts with.
When you edit a resource (often a document) in the Manager, for example, you're looking at a page generated by the controller atmanager/controllers/resource/staticresource/update.php. After setting the characteristics of the resource in the $resource array, this code rendersthe page:
$modx->smarty->assign('resource',$resource); $modx->smarty->fetch('resource/staticresource/update.tpl');return
The Smarty placeholders in update.tpl are filled in with the data held in the $resource array.
core/packages/
Here you will find any transport packages you've downloaded via the section of Revolution, such as TinyMCE, Ditto, etc.Package ManagementThe core package is also found here as well. This allows for easy installation and removal, as well as remote updating of installed packages.
When you build a package (for example, after checking out from SVN), the transport package will be stored here.
core/xpdo/
MODx Revolution was designed to use OpenExpedio (xPDO), an extension to PDO. It provides a uniform interface for manipulating databases,and makes it possible for MODx to support various database platforms besides MySQL.
This directory contains all of the class files needed by xPDO to do everything from query caching, to building transport packages and outputtingdata as a convenient JSON object.
These classes are used by MODx internally, and developers should never need to deal with them directly.
Notable Files
core/cache/config.cache.php - This is the cache file for all of the in MODx. Their database equivalents are found in theSystem Settings_system_settings table, and their xPDO equivalents are modSystemSetting objects.
Tip - If you ever get locked out by the CAPTCHA component, you can edit this file and set to '0' to disableuse_captchaCAPTCHA. Then you can log in and disable CAPTCHA in .System Settings
core/cache/sitePublishing.idx.php - In MODx Evolution, this file contained the cache data for all documents, chunks, and snippets. InRevolution, this is no longer the case, and this file now keeps track of cache refresh intervals.core/cache/mgr/actions.cache.php - a map of all modAction objects.
manager/
The Manager is the MODx backend or administration area for creating resources, managing users, and performing overall site maintenancetasks.
manager/assets/
This directory contains the libraries, as well as the custom ModExt implementation. ModExt extends the original ExtJS library, to makeExtJSdevelopment more convenient for users.
manager/assets/controllers/
Controllers are the PHP files tied to modActions. They simply fetch data and return or output it to the browser for rendering and display. Wheneveryou load a page in the Manager, you are in effect telling MODx to load a particular Controller, which simply loads a Smarty template and outputsany necessary JavaScript to the browser.
manager/assets/templates/
This directory contains the template files for each manager page. They do not contain PHP code, but rather are used to organize HTML. If you arelooking for the Smarty .tpl file for a particular manager page, check in the manager/templates/default/ directory.
Notable Files
manager/assets/ext2/ext-all.js - This is the main Ext library file, which must be included on all Manager pages (or any page using Ext).It's compressed to save space, decrease download time, and speed up page loads. However, if you're doing a lot of JavaScript work,you're bound to run into some cryptic errors because of the compression. The best way to deal with this is to simply rename this file, and
then rename the ext-all.js file to ext-all-debug.js to use the uncompressed version during development. Just be sure to switch them backafterwards!
setup/
This directory is the equivalent of the "install" directory in MODx Evolution. It contains the necessary files needed to run Setup and perform a or an .Fresh Installation Upgrade
_build/
This directory is only present in version of MODx Revolution downloaded from the subversion server (as well as the "SDK" distribution). Itcontains the packaged MODx core data files necessary to install MODx to a database.
Notable Files
_build/transport.core.php - This file must be executed after downloading MODx Revolution, and prior to running Setup. Aftercompletion, you should notice a "core" directory inside your core/packages/ directory, which will contain all of the necessary Vehicles forinstalling MODx Revolution.
assets/
This directory is not present in MODx Revolution by default, but like in MODx Evolution, it is common to place images, CSS, JavaScript, and othermedia in here.
assets/components/
When you install a package using the , an assets/components/<component_name>/ directory will be created to hold anyPackage Managernecessary component files, such as JavaScript or images.
Roadmap
This is a work-in-progress roadmap for MODx Revolution.
Tasks in are already finished in Git.purple
Revolution 2.0
After PL release, the core team will concentrate on creating distributions for different 2.0.0-pl implementations. Migration of the Revolutioncodebase to Git will also occur at this time.
Revolution 2.0.1
Fix any major issues in JIRA
Revolution 2.1
Add Custom DashboardsSimplify setup/ processMore API methods
Revolution 2.2
Transport Package dependenciesAdd Static ElementsAdd Heirarchical Elements
Revolution 2.3
Permissions/ACLs for specific UsersUser Group Settings
Revolution 3.0
Add native VersioningAdd Content Element supportAdd Workflow supportRedo manager into an actual 'mgr' context where pages are modx resources
See Also
Making Sites with MODx
This section contains information on creating your site using MODx.
Structuring Your Site
This section contains information about MODx objects that are used in the structure of your website.
Resources
What is a Resource?Managing Resources
Resource FieldsGeneral Resource FieldsSettings Resource FieldsUsing Resource FieldsAccessing Resource Fields in a Snippet
Linking to a ResourceURL Parameters for Resource TagsURL Schemes in Resource Tags
See Also
What is a Resource?
A resource is a representation of a page in MODx. There are different types of Resources, such as documents, weblinks, symlinks, actual files, ormany other things. The default Resource type is a Document, and simply represents a webpage on your site.
There are 4 total types of Resources, and they are Documents, , , and .Weblinks Symlinks Static Resources
Each Resource also has a unique ID, or "Resource Identifier". This lets MODx know what Resource to fetch when you are loading a webpage.Also, when you're wanting to link between Rsources, you will use this ID to do so, which allows MODx to not have to worry about the resultingURL. You can move, rename, alter or even change the type of a Resource, and the ID will stay the same - meaning that any changes you maketo the Resource wont affect your links to it.
Managing Resources
Resources are shown in the Resources tree in the left-hand navigation of the manager. To edit one, simply click on the page you would like toedit. You can alternatively right-click the Resource and click 'Edit Resource'. This will load the Resource Edit page:
The content of the Resource can then be edited in the large content field in the bottom area. Other fields related to each Resource can also beedited via the tabs on the top of the page.
Resource Fields
Resources come packaged with a list of predetermined fields by default. They are:
General Resource Fields
Name Description
id The ID of the Resource.
template A reference to the that this Resource is usingTemplate
published If the Resource is Published, or viewable on the front-end.
pagetitle The title of the Resource.
longtitle A longer title of the Resource.
description An extended description of the Resource.
introtext Also called 'Summary', an introductory excerpt of the Resource's content. Useful for blogs or searching.
alias The URL alias to use, if your site is using Friendly URLs. A Resource with alias 'home' and Content Type 'html' would render'home.html' if it isn't a Container.
parent The Parent Resource's ID.
menutitle The title to show for the Resource when displayed in a menu.
menuindex The order index of the Resource in a menu. Higher order means later.
hidemenu Also called 'Hide from Menus', if set, this Resource will not show in most Menu or Navigation snippets.
content The actual content of the Resource.
Settings Resource Fields
Name Description
isfolder Labeled as 'Container', this specifies whether or not the Resource renders with a / in Friendly URLs instead of its suffix.
searchable If the Resource is searchable.
cacheable If the Resource is cacheable.
createdby The ID of the user who created the Resource.
editedby The ID of the last user to edit the Resource.
deleted If the Resource is deleted or not.
deletedby The ID of the user who deleted the Resource.
publishedby The ID of the user who last published the Resource.
createdon The date the Resource was created.
publishedon The date the Resource was published.
editedon The date the Resource was last edited.
Using Resource Fields
Resource fields can be accessed from anywhere by using the syntax, ie:Template Variable
[[*pagetitle]] // renders the pagetitle.[[*id]] // renders the Resource's ID[[*createdby]] // renders the ID of the user who created Resourcethis
They can also have applied to them:Output Filters
// Renders a limited version of the introtext field. // If it is longer than 100 chars, adds an ...[[*introtext:ellipsis=`100`]]
// Grabs the user who last edited the Resource's username[[*editedby:userinfo=`username`]]
// Grabs the user who published the Resource's email[[*publishedby:userinfo=`email`]]
Accessing Resource Fields in a Snippet
Grabbing the Resource Fields in a is quite easy; MODx provides you with the Resource object in any Snippet, via the $modx->resourceSnippetreference. For example, this example Snippet will return the current page's pagetitle reversed:
/* output the current Resource's pagetitle */$output = $modx->resource->get('pagetitle');
strrev($output);return
Linking to a Resource
In MODx, links to Resources are dynamically managed via "Resource Tags". They look like this:
[[~123]]
where '123' is the ID of the Resource to link to. You can put these tags anywhere, and MODx will dynamically render the URL for the Resource.
You can also get the Resource Tag by dragging a Resource from the left tree into the content panel.
Also see .Named Anchor
URL Parameters for Resource Tags
Adding URL parameters in your Resource Tag is quite simple in Revolution. Let's say we have Resource ID 42 that resolves to a URL of'store/items.html'. We want to add a 'tag' parameter to the URL, with a value of 'Snacks' and a 'sort' parameter of 'Taste'. Here's how you'd do it:
[[~42? &tag=`Snacks` &sort=`Taste`]]
This would render as:
store/items.html?tag=Snacks&sort=Taste
Note that those are instead of apostrophes.backticks
URL Schemes in Resource Tags
You can specify the scheme for a Resource in your tag:
[[~123? &scheme=`https`]]
Would render the URL using 'https' instead of the normal http.
The available schemes are:
Name Description
https Renders the link with https instead of http
full Renders the link as an absolute URL
See Also
Content Types
What are Content Types?Usage
Creating New Content TypesSee Also
What are Content Types?
Content types are specific filesystem types for your resources. They are associated with file extensions and tell the MODx Parser what type ofextension to render the page with.
For example, a with an alias of 'test' and Content Type "CSS" that has a file extension of ".css" will render as:Resources
test.css
instead of test.html. This allows you to create any type of file from .Resources
Usage
When editing a Resource, simply select the Content Type that you'd like to use:
Then save the . This will automatically associate that with the selected Content Type.Resources Resources
Creating New Content Types
First, go to System -> Content Types. You'll see a grid populated with all the current Content Types. Click on 'New Content Type', and a windowwill appear:
The fields that appear are as follows:
Name - This is the name of the Content Type. It is mainly for organizational and labeling purposes, and does not affect the function of thetype.MIME Type - Here you can set the MIME Type for the extension, which will tell the browser what type of file the is. A list ofResourcesavailable MIME Types can be found or .here hereFile Extensions - This is the file extension to render the Resource as.Binary - Is the file type text/ascii or binary?Description - An optional field for your own descriptive purposes.
From there, click "save" and the Content Type will appear in the grid.
See Also
Resources
Named Anchor
What is a Named Anchor?
A named anchor is a link to content within the current resource.
A typical named anchor will be similar to:
<a name="prohibited"></a>
Accessing the Named Anchor
To generate a link to the current Resource, while using a named anchor of "prohibited":
<a href= >Prohibited Activities</a>"[[~[[*id]]]]#prohibited"
To generate a link to a Resource with ID 12, while using a named anchor of "prohibited":
<a href= >Prohibited Activities</a>"[[~12]]#prohibited"
Static Resource
What is a Static Resource?
A Static Resource is a Resource abstraction of an actual file on the filesystem. You can point a Static Resource to any file on your webserver; this
allows you to manage these Resources externally.
Static Resources can also have tags inside their content fields to determine the path of the file - so you can specify custom paths to set as SystemSettings, or use to dynamically find the path.Snippets
They behave exactly like a Document (standard Resource). Content within a Static Resource is parsed and displayed in the exact same way as aDocument.
See Also
Symlink
What is a Symlink?
Symlinks are similar to in that they redirect to another or URL; however, symlinks will persist the current URL.Weblinks Resources
See Also
Using Resource Symlinks
Understanding a Symlink
A MODx Revolution symlink simply takes the content of one resource and displays it in another. You can add a new template or what everchanges you may want to the symlink, and you can place it anywhere within the site, but you can not add additional content to it.
How do I use this feature?
1) Create the symlink with a name, an alias and a reference to the "master" document itself. 2) If you do not see it -- try clearing the site cache / document cache.
Example:
For my site this example would feed the contents of my college degrees page to the me.html
Why Symlink?
A document needs moved in the structure and google has it listed at the old locationA document may actually and logically belong in more than one place in a siteYou quickly and simply want to provide a short and simple url to a document located many levels deep in a siteYou want to deal with various spellings of a document: foursquare, 4square, forsquare and have them all point to the same content (eachof these would be a separate symlink)Most web site host control panels allow for subdomains. By creating a subdomain in the control panel and then a symlink in the root ofthe website to content burried further down, you could create a "doorway" page or a fast means to getting to a group of related content.
Weblink
What is a Weblink?
A weblink is a document of type "reference". It has no template. It simply serves as a link that will be part of a generated menu.
The content of the weblink is just an URL. The parser doesn't even parse it; as soon as it sees that it is a "reference", it just uses the content asthe argument for sendRedirect($url).
You can use an external URL for the content, or you can use a Resource ID to link to a Resource in your MODx Resource tree.
Example
Say you want a footer menu with links to a Terms of Use, a Privacy Policy, and others. But you also want a link to "Contact Us" there. Contact Usis one of your main pages, and is in the top-level of your tree to be displayed in your main menu. You would put those Resources in one "utility
pages" folder, probably unpublished so it won't show up in your main menu, and use that folder ID as the Resource ID for the menu snippet. Inthat folder you would also put a Weblink to your site's contact page. This way the menu will include a link to the contact page, even though thatResource is not in the folder.
Or, you could do it the other way around, have the Contact Us Resource in your unpublished "utility pages" folder, and put the Weblink to it in yourtop-level so it will show in the main menu.
Originally a menu snippet would make the link to the Weblink itself, just as to any other MODx resource, thus causing the page to be loaded bythe parser, triggering the redirect.
The parser will detect that the Weblink's content is a number, and run it through makeUrl($id) before redirecting.
See Also
Templates
What are Templates?
Templates typically contain the HTML markup tags that determine the layout and appearance of your site. When a document is requested, MODxloads the document and its template, and MODx finds all the special placeholders in the template and replaces them with the correspondingvalues from the document before sending the finished page off to the user's browser.
Think of a Template like a house. Your content, then, is a person. A person can live in many different houses, but only one house.Resource's
A can only be using one Template - however, it can switch Templates at any time, just as a person can move from house to house atResourcesany time. The Template, just like a house, also changes the main way a page is displayed. A Template usually contains the header and footer of apage - and/or a sidebar, navigation bar, etc.
Usage
To create a Template -- Expand the "Elements" part of the tree and right click on Templates. Select "Create a New Template" then paste yourHTML into the "Template Code" textarea; you can copy and paste the text below to get started with a very simple template:
<html><head> <title>[[*pagetitle]]</title> <meta name= content= />"description" "[[*description]]"</head><body><h1>[[*longtitle]]</h1>
Page ID: [[*id]]<br/>IntroText (Summary): [[*introtext]]<br/>MenuTitle: [[*menutitle]]
<hr/>
[[*content]]
</body></html>
Note the important [[*content]] tag; this tag tells MODx where to put the Resource's content.
MODx stores template data in its database; you cannot create a template by uploading a file to the filesystem, you createmusta template using the manager.
Remember that simply a template doesn't mean that it is automatically put to use: you have to edit each and specify whichcreating ResourcesTemplate it uses. This is different from some content management systems where each template has one or many pages. Each MODx page hasa single Template that it uses to format output.
After you've created one or more Templates, you can edit any Resource and choose a Template for it by selecting one from the "Uses Template"drop-down list.
Templates can contain any tags, including , , , and others.Template Variables Chunks Snippets
Using Resource Fields in the Template
As you noticed from our Template sample code above, the fields of a Resource can be referenced using the [[*fieldName]] syntax. A list ofavailable Resource Fields can be . For example, if we wanted to show the current Resource's pagetitle in our <title> tag, we wouldfound heresimply do this:
<title>[[*pagetitle]]</title>
You can also place the content of the current Resource using the "content" tag:
<body>[[*content]]</body>
These tags are like normal MODx tags, in that they can have applied to them. For example, say we wanted to display the "introtext"output filtersfield on a right navbar, but strip any HTML tags from it, and only display the first 400 characters - and if longer, add an ellipsis (...):
<div id= >"rightbar"[[*introtext:stripTags:ellipsis=`400`]]</div>
Template Variables in Templates
If Templates are like a house, think of (TVs) like rooms in that house. You can have an infinite number of TVs in a Template;Template Variablesjust think of it like adding new rooms to the house.
Template Variables allow you to have custom fields for any Resource with the specified Template. Say you want a 'photo' field on your Resourcesin your "BiographyPages" Template. Simple - just create a TV, call it "bioPhoto", give it an input and output type of "image", and assign it to your"BiographyPages" Template. You'll then see the TV in any Resource that's using that Template.
You can then reference your "bioPhoto" TV in your content with the same tag syntax as a Resource Field:
<div class= >"photo"[[*bioPhoto]]</div>
Again, it's important to note that must be explicitly assigned to the Template to be used. Once assigned to the Template, aTemplate VariablesTV's value for that Resource will be able to be edited when editing the Resource. If you're not seeing a newly created TV in your Resources,make sure you've assigned that TV to the Template.
See Also
Tag SyntaxTemplate Variables
Chunks
CreateUsageProcessing Chunk via the API
Modifying a Chunk Via the APISee Also
Chunks are bits of static text which you can reuse across your site, similar in function to include files or "blocks" in other content managementsystems. Common examples of Chunks might be your contact information or a copyright notice. Although Chunks cannot contain any logicdirectly, they can however contain calls to , which are executable bits of PHP code which produce dynamic output.Snippets
Create
Before you can use a Chunk, you must first create and name one by pasting text into the MODx manager (Elements --> Chunks --> New Chunk):
Usage
To use the Chunk, you reference it by name in your templates or in your page content.
[[$chunkName]]
That reference is then replaced with the contents of the Chunk.
You can also pass properties to a Chunk. Say you had a chunk named 'intro' with the contents:
Hello, [[+name]]. You have [[+messageCount]] messages.
You could fill those values with:
[[$intro? &name=`George` &messageCount=`12`]]
Which would output:
Hello, George. You have 12 messages.
You could even take it one step further, by adding a that allows the user to specify their name per Resource:Template Variable
[[!$intro? &name=`[[*usersName]]` &messageCount=`[[*messageCount]]`]]
or in the Chunk itself:
Hello, [[*usersName]]. You have [[*messageCount]] messages.
Processing Chunk via the API
Chunks are also frequently used to format the output of Snippets. A Chunk can be processed from a Snippet using the process() function; forexample, given the following Chunk named 'rowTpl':
<tr class= id= >"[[+rowCls]]" "row[[+id]]"<td>[[+pagetitle]]</td><td>[[+introtext]]</td></tr>
the following Snippet code retrieves it and processes it with an array of properties for all published Resources, and returns formatted results as atable, setting the class to "alt" if for even rows:
$resources = $modx->getCollection('modResource',array('published' => ));true$i = 0;$output = '';foreach ($resources as $resource) { $properties = $resource->toArray(); $properties['rowCls'] = $i % 2 ? '' : 'alt';
$output .= $modx->getChunk('rowTpl',$properties); $i++;}
'<table><tbody>'.$output.'</tbody></table>';return
Modifying a Chunk Via the API
Chunks can also be manipulated by the MODx API:
<?php/* create a chunk, give it some content and save it to the database */new$chunk = $modx->newObject('modChunk');$chunk->set('name','NewChunkName');$chunk->setContent('<p>This is my chunk!</p>');new$chunk->save();
/* get an existing chunk, modify the content and save changes to the database */$chunk = $modx->getObject('modChunk', array('name' => 'MyExistingChunk'));
($chunk) {if $chunk->setContent('<p>This is my existing chunks content!</p>');new $chunk->save();}
/* get an existing chunk and delete it from the database */$chunk = $modx->getObject('modChunk', array('name' => 'MyObsoleteChunk'));
($chunk) $chunk->remove();if?>
See Also
modChunk
Using Snippets
Using a SnippetSnippet PropertiesInstalling SnippetsSee Also
Snippets are MODx's answer to inline PHP code. They provide customizable dynamic content, such as menus, blog or news listings, search andother form-based functionality and anything else that your site needs to generate on-demand.
Using a Snippet
Once you have a Snippet installed, you can use it simply by putting its tags in your template, a chunk or TV, or a document's content whereveryou want the Snippet's output to be displayed.
[[MySnippet]]
If you expect the snippet code to be dynamic for different users, you can also call a snippet uncached:
[[!MySnippet]]
Snippet Properties
Snippets can have , which can be passed in the Snippet call, like so:Properties
[[!Wayfinder? &startId=`0` &level=`1`]]
You can also aggregate these Properties into a , which is a dynamic collection of properties that can be attached to any Snippet (orProperty SetElement for that matter). This allows you to share common property configs in a snippet call in one place.
Say you had a Property Set called 'Menu' with `startId` set to 0 and `level` set to 1:
[[!Wayfinder@Menu]]
would then load those properties automatically into the Snippet. And even those properties can be overridden:
[[!Wayfinder@Menu? &level=`2`]]
which would override the set's value on `level` of 1, setting it instead to 2.
Installing Snippets
You can also download and install Snippets via . See the tutorial on for more information.Package Management installing a Package
See Also
Installing a Package
Tag Syntax
To simplify parsing logic, improve parsing performance and avoid confusion with many new adopters, all tags are now of a single format,differentiated by a token or a set of tokens which appear before a string which identifies the Content Element or Content Tag to be processed; e.g.[[ ]].tokenIdentifier
Tag Format Changes for Content Elements and Content Tags
Content Elements Evolution (Old) Revolution (New)
Templates no tag representation no tag representation
Template Variables [* *]templatevar [[* ]]templatevar
Chunks {{ }} chunk [[$ ]]chunk
Snippets [[ ]]snippet [[ ]]snippet
Plugins no tag representation no tag representation
Modules no tag representation does not exist in Revolution
Content Tags
Placeholders [+ +] placeholder [[+ ]]placeholder
Links [~ ~]link [[~ ]]link
System Settings [( )]system_setting [[++ ]]system_setting
Language no tag representation [[% ]]language_string_key
Adopting this simplified format allows the new parser to be fully-recursive, following a source-order mechanism that does not depend on regularexpressions.
Previously, each tag set was parsed independently in a specific order, one level at a time, with any embedded tags delayed until the next pass. Now tags are parsed as they are encountered regardless of the element types they represent, and embedded tags are parsed before the outertag to allow much more complex tags to be composed. Combined with the ability to use the previously reserved ? & and = symbols in tag strings(when escaped by the infamous backtick, e.g. ), MODx Content Tags offer a powerful new set of¶m=`?=&is ok now, wow!?&=`capabilities for mashing up your content.
Caching
In Evolution, Snippets that need to be processed with each request should be on an uncached page or the Snippet itself should be calleduncached: [[!snippet]]
In Revolution, any tag can be called uncached by inserting an exclamation point immediately after the double-bracket:[[!snippet]], [[!$chunk]], [[!+placeholder]], [[!*template_var]], etc.
Properties
All tags - no longer just Snippets - now accept properties, as well, that can be used. For example, let's say we had a Chunk named 'Hello' with thecontent:
Hello [[+name]]!
You'll note the new placeholder syntax. So, we'll definitely want to parse that Chunk's property. In 096, this was required to be done with aSnippet; no longer. You can simply pass a property for the Chunk:
[[$Hello?name=`George`]]
This would output:
Hello George!
The syntax for properties follows the same syntax as 096/Evolution snippet properties.
Customizing Content
MODx offers many ways of customizing content. This section covers the ways to do so that are built into the core of MODx.
Template Variables
What is a Template Variable?UsageSee Also
What is a Template Variable?
A Template Variable (TV) is a custom Resource Field that is created by the site developer. Imagine that you represent the pages on your site viaan Excel spreadsheet: each property is a column on that spreadsheet, and each page is a row. By default there are a specified number ofproperties or columns for each page (e.g. page title, content, alias). Template Variables allow for you to extend the attributes based on yourcontent, for example a page about books might require additional attributes for "author" or "publisher".
A TV is used to represent a value inside a Resource. MODx allows you to have a virtually unlimited number of TVs.
When a is displayed on the web, TV tags are replaced with the actual value entered by the user. TVs are -specific, meaningResources Templatethey can only be used in that they are assigned to.Templates
Template Variable Output Renders make it easier for users to add special visual effects to their web sites in a matter of seconds. With just a fewclicks you can add an Image, URL or custom render to your website.
Usage
Let's say we have a TV named 'bioPhoto', that is an Image TV we created. We've assigned it to our 'Biography Pages' Template, and want toshow it on our page. To do so, we'd simply place this tag anywhere we want:
[[*bioPhoto]]
TVs can also have Properties. Say you had a TV called 'intromsg' with the value:
Hello [[+name]], you have [[+messageCount]] messages.
You could fill the data with the call:
[[*intromsg?name=`George` &messageCount=`123`]]
Which would output:
Hello George, you have 123 messages.
Output Filters are also great tools to be applied to TVs. Say you wanted to limit a TV's output to 100 chars. You'd simply use the 'limit' output filter:
[[*bioMessage:limit=`100`]]
See Also
Creating a Template Variable
General SettingsRendering Options (Input Type)PropertiesTemplate and Resource Group AccessSee Also
This page outlines how to create a Template Variable in MODx Revolution.
General Settings
First off, you'll want to specify a name for the Template Variable. TVs in MODx are case-sensitive, so you'll want to be careful about the name youchoose. You can also specify a caption and description.
Rendering Options (Input Type)
From there, you can select all kinds of rendering options for the TV. First off, you'll want to select an input type:
These determine how the input form is rendered for the TV in the MODx manager interface. For this example, we'll choose "Date". Should wehave chosen a list, or dropdown, we can specify the Input Option Values in the following format:
option1==value1||option2==value2||option3==value3
We can then also specify a default value for the TV as well.
Next, we'll select the output rendering options. We'll select 'Date' as well, and as you'll note, below this box (depending on the Output Renderselected) some form fields will show:
Allowing us to edit more fine-grained options for that Output Render.
Properties
From there, we can specify any default properties we want for the TV. "How can you use properties on a TV?", you might ask. Well, let's say we'redoing a textarea TV named "viewingSS". In our content, we've got this:
Viewing: [[+subsection]]
We can add a list property 'subsection' to the grid, and then allow that property to be overridden via property sets. Say we created a Property Setnamed 'CarsSectionTVPS' (PS for Property Set). In it, we set the 'subsection' property to "Cars". We'd then attach it to the TV in our Resource, orTemplate, or whereever we are using it like so:
[[*viewingSS@CarsSectionTVPS]]
This would output in the place of the TV:
Viewing: Cars
Template and Resource Group Access
We can assign TVs to , as well. This allows those Resources assigned to those to edit the TVs for each Resource.Templates Templates
Also, TVs can be restricted to certain Resource Groups, selectable in the grid labeled "Access Permissions".
See Also
Bindings
What are Bindings?@
In the context to Template Variables, a Data Source is the location of the information to be displayed. A Data source can come from any of thefollowing sources:
an externally generated file that is sent via FTP to the servera Database table accessible to MODxa in the resource treeResourcesa in the Elements treeChunkthe result of an evaluated PHP script
These Data Sources can be tied (or "bound") to a Template Variable for formatting and displaying in document. In addition, the bound data in theTVs can be almost effortlessly formatted via the Display Controls within the TV system for some truly stunning results. The format for using thetypes of data source bindings available to all template variables follows:
@FILE file_path@RESOURCE resource_id@CHUNK chunk_name@SELECT sql_query@EVAL php_code@DIRECTORY path_relative_to_base_path
These "@" commands or bindings will allow you to quickly and easily attach your template variables to virtually any database system available.
The value returned from the data source can either be a string value (including numbers, dates, etc), an array or a recordset. The value returnedis dependent on the type of binding used. Some display controls will attempt to either convert the returned value into a string or an array.
For example, controls that accept string values such as a radio button group or select list will attempt to convert a record set (rows and columns)into the following format:
col1row1Value==col2row1Value||col1row2Value==col2row2Value,...
Please note that @ bindings will work only when used inside "Input Option Values" or "Default Value" fields.
When placing @ bindings inside the "Input Option Values" field, they are used to format input options only when editing document within theManager, for example to create a drop-down list of Cities or Countries.
When placing @ bindings inside the "Default Value" field the returned value is used to render to the final web page. This makes it simple to buildcomplex forms for data input on the web very quickly.
Types
@FILE@RESOURCE@CHUNK@SELECT@EVAL@DIRECTORY@INHERIT
See Also
Template Variables
CHUNK Binding
What is the @CHUNK Binding?
The @CHUNK Binding returns the parsed content of any specified Chunk.
Syntax
@CHUNK chunk_name
Binds the variable to a document. Where chunk_name is the name of the chunk. The returned value is a string containing the content of thechunk.
This binding is very similar to the with the exception that it will bind the TV to a .@RESOURCE binding Chunk
Usage
@CHUNK MycontactForm
See Also
Template Variables
Bindings
DIRECTORY Binding
What is the @DIRECTORY Binding?
The DIRECTORY binding reads the contents of a directory. This can really useful when you tie it into a List control widget, e.g. if you want to dosomething like give the user a list of logo images to choose for a page, or choose which mp3 file plays on a particular page. REMEMBER: itreturns contents of a directory, including all files and all directories - with the sole exception of directories prefixed with a period.ALL
Usage
When you create a Template Variable, place the following text into the box:Input Option Values
@DIRECTORY /path/to/some_directory
Frequently, this is coupled with an Input Type of "DropDown List Menu" to allow the user to select a file from the list.
In MODx Revolution, the path used for the @DIRECTORY binding is relative to the site's root. It is an absolute file path. Ifnotyou want to list files above your site's root, you must use the ".." syntax, e.g. This binding@DIRECTORY /../dir_above_rootwill work with or without a trailing slash in the directory name.
If you are using the @DIRECTORY binding for your template variable [[*myTV]], you can easily imagine that your template code could have somestuff in it like:
<img src= alt="" />"[[*myTV]]"
Additional Info
Can you filter which files are selected? E.g. using *.jpg? The following DOES NOT WORK:
@DIRECTORY /list/*.jpg # doesn't work!
There are PHP code snippets out there that emulate this functionality. See the following forum thread: http://modxcms.com/forums/index.php/topic,3124.0.html
Security
Depending on how the file is used on the page, it may pose a security risk. Be careful if you were using this binding to select JavaScript files to beexecuted. What if a user had the ability to upload (and thus execute) a JavaScript file? Also, always be wary of letting users see your directorystructure.
See Also
Template VariablesBindings
EVAL Binding
What is the @EVAL Binding?
The @EVAL Binding executes the specified PHP code. It should be used with careful security precautions.
Syntax
@EVAL php_code_here
Usage
Simply put a PHP statement after the @EVAL tag:
@EVAL .time();return "The time stamp is now "
@EVAL $a = 'dog'; $a;return
Security
The eval() statement raises an eyebrow with anyone concerned with security: eval statements are notorious for being exploited, so it'srecommended that you find another way of doing whatever you are trying to do, but this context is supported by MODx. If I let my cynical mindwander, allow me to paint one disasterous circumstance: some web user of your MODx application logs in and has access to a field that getsexecuted by an EVAL binding. This nefarious user could eval some nasty or statements and destroy your web server files, or readunlink() rmdir()sensitive files on the web-server that PHP has access to. Be careful with these!anywhere
Thankfully, I've been unsuccessful in my attempts to unlink() a file using the @EVAL binding... but I'm sure there are people out there more cleverthan me...
See Also
Template VariablesBindings
FILE Binding
What is the @FILE Binding?
The @FILE Binding returns the contents of any specified file.
Syntax
@FILE file_path
Binds the variable to a file, where is the path and name of the file. The return value is a string containing the content of the file. The filefile_pathpath is the absolute path from the root of the server or your particular installation.
The command is very useful in cases where we might want to generate data that's available in file. By using the and characters as a@FILE || ==delimiter we could interface with any external database application.
Usage
For example: Let's say we have a text file called headline_news.txt that is external to our database system. This file is constantly being updatedwith up-to-the-minute news items by another external system. We want to display these news items on our website for our visitors to see. Howcan we do that?
First, we might create a new Template Variable. We then add the @FILE command inside the default value of the TV. This will point to where theheadline_news.txt is located in our example. Our default value might look like this:
@FILE assets/news/headline_news.txt
Let's say each headline in the headline_news.txt file is separated by a new-line (lf or \n) character. We can use the Delimiter render to separateeach item and display them one at a time. Our fields will look like this:
Unable to render embedded object: File (filebinding.png) not found.
And viola! We have our dynamically rendering @FILE binding.
See Also
Template VariablesBindings
INHERIT Binding
What is the @INHERIT Binding?
The @INHERIT binding will automatically find the value of the parent Resource and use that as its value. If the parent Resource also has@INHERIT, it will look at that parent's parent's value, and so forth. If it ends up at the root and no value has been specified, the value will be 0.
Usage
@INHERIT
See Also
Template VariablesBindings
RESOURCE Binding
What is the @RESOURCE Binding?
The @RESOURCE Binding returns the parsed contents of any specified Resource.
Syntax
@RESOURCE resource_id
Binds the variable to a Resource, where resource_id is the ID of the Resource. The returned value is a string containing the parsed content of theResource.
Usage
To output the contents of a Resource with ID of 12:
@RESOURCE 12
See Also
Template VariablesBindings
SELECT Binding
What is the @SELECT Binding?
The @SELECT binding calls a database query based on the provided value and returns the result.
Syntax
@SELECT * FROM site_content
To write one of these, you need to be familiar with . It is recommended that you first write a functional MySQL statement thatMySQL syntaxexecutes without error in the MySQL command line (see errors, below for some pit-falls). Once you've verified that your query works, then youcan create a @SELECT binding with it.
All you need to do is after you've got a working MySQL query is:
add an '@' symbol in front of your SELECTomit the final semi-colon and any commentsput the query on a single line (no returns!)
You can place this binding in one of two places:
On a page (edit the page in the manager). The page where you want to put this binding must be using a valid template, and that templatemust have the correct template variable(s) associated with it. If you've created the and associated it with a ,Template Variable Templateand the page you're working on is using that , then you'll have a place to enter in some text for that variable when you edit theTemplatepage. Paste the "@SELECT ..." stuff in there. It sounds more complicated than it is, but this section is written verbosely for the sake ofclear documentation.You can also place the query into the "Default Value" box for the Template Variable. If you replace the default text of a Template Variablethat's already in use, be careful, because your pages might require a specific type of output, e.g. the output type that a @SELECTbinding returns.
REMEMBER: The query must be on ONE LINE. No returns!
Alternatives
Before we get any more complicated, consider doing this a different way. A might do the job more easily than a binding.Snippet
If your query needs to work with template variables and you need specialized formatting for the output, the @SELECT binding is probably not theway to go. Pretty much everything that's done with the bindings is also possible with ; the bindings just provide a shortcut. When you startSnippetsover-using the shortcut, you may run into headaches.
More Complex Example: Template Variables
What if you need to write a query that accesses the template variables associated with a particular page? Those variables aren't directly stored inthe table, they are stored in other tables. This forces you to write a JOIN statement. Here's a more tangible example: let's say all thesite_contentpages in a particular folder have a template variable for ... that field doesn't exist in the "site_content" table. Hold onto your butts,opening_datebecause this gets complicated. You have to look at MODx's gory plumbing in order to pull this off. You have to understand how MODx extends thedata stored in the "site_content" table and makes use of the custom fields known as "Template Variables". This is open to some debate, butunfortunately, MODx's database schema doesn't follow the strict best practices for foreign keys... it's not always clear which table is beingreferenced by a particular column... it's not even always clear that a column ''is'' a foreign key, but rest assured, it is possible... it just takes a bit ofpatience to figure out.
First, have a look at the following tables (you may have prefixes to your table names):
site_templates - contains the actual template code used for the site (lots of HTML appears in the content field).site_tmplvars - contains the name of template variable. The "name" field is what triggers the substitution. E.g. A name of"my_template_variable" should be used as "[[*my_template_variable]]". If you care to think of this architecturally, this table defines thevariable class: the name and type of variable that a series of pages will have.site_tmplvar_contentvalues - contains the values of the template variables for each page that uses them. The database table has 4 fields:id, tmplvarid (foreign key back to site_tmplvars.id), contentid (foreign key back to site_content.id), value (a text field). Architecturally, thistable represents of the particular class. In other words, one row in the table might have multiple rows in this tableinstances site_tmplvars(one row for each instance of the variable).site_tmplvar_templates - This is a mapping table which associates a Template Variable with a Template (maps site_template:id tosite_tmplvars:id). Contains 3 fields: tmplvarid, templateid, rank.
In our example, we want to filter based on a custom date field named "opening_date", but if you look closely, thesite_tmplvar_contentvalues.value field is a field. MySQL won't automatically recognize arbitrary text as a date value, so you'll have to maketextuse of MySQL's function. You may think that the site_tmplvars.display_params is a savior here, but it's not... you end up smashingstr_to_date()your nose directly into the nasty truth that the formats used by PHP's (stored in site_tmplvars.display_params) are the same as whatstrftime() notMySQL can use in its STR_TO_DATE() function. There may be a way to automatically do this, but it's easier to just hard-code it. You might endup with a query like this:
SELECT page.alias, tv_val.value, DATE_FORMAT(STR_TO_DATE(tv_val.value, '%d-%m-%Y %H:%i:%s'), '%Y-%m-%d %H:%i:%s') as `FormattedOpening Date`,
FROM site_content as pageJOIN site_tmplvar_contentvalues as tv_val ON page.id=tv_val.idWHERE page.parent='95' AND tv_val.tmplvarid='6' /* 6 is the opening_date */ AND DATE_FORMAT(STR_TO_DATE(tv_val.value, '%d-%m-%Y %H:%i:%s'), '%Y-%m-%d %H:%i:%s')>'2008-10-2413:04:57';
MODx uses the MyISAM table engine, not InnoDB, so it does not rigidly enforce the foreign key constraints that are inferred bythe table structure.
Errors
What if your MySQL statement executes perfectly, but once you put it in your SELECT binding, it fails? Well, there are some pit-falls. Theimplementation isn't perfect. Pay close attention to the following:
Your query appear on one line. Newline characters cause the @SELECT binding to choke.MUSTDelete all MySQL comments /* this style */ and -- this styleMake sure you have entered the table names correctly! Many sites use table-prefixes, so it is imperative that you test your queries beforetrying to use them in a @SELECT Binding. If your query has an error, MODx will log the error to the error log.
Next Step: Formatting
Ok, so you can return a bunch of data from the database... now what? If you need to format it intelligently, you might get some mileage out of theOutput Renders, but you might find the available options limiting to you. You can write your own that formats the value of a TemplateSnippetVariable.
Security
Does this binding let you execute UPDATE, INSERT, or DELETE queries (or, , DROP TABLE statements)? Even if it doesn't supportgasp directlythis, you may be able to construct and execute a complex query that SELECT's the result of a such a destructive query. At the very least, a usercould construct a query to select another user's password hash or to see documents that the user isn't supposed to have access to. A lot of theCMS's out there give access to the database (including DROP and DELETE statements) with the database handle used by the application.fullThat's dangerous, and the @SELECT binding may expose some of those same vulnerabilities.
See Also
Template VariablesBindings
Template Variable Output Types
Output Types for TVs
Output Types (also called Renders) on allow you to format the value of a TV to any different kind of output - such as a URL,Template Variablesimage, date, or anything else you can think of.
For example, say you have a TV that uses a Textbox as its Input Type. The user would then choose an Image through the TV input on theirResource. That's great - except your TV only outputs the URL of the image! You want it to output the image itself. So you'd then choose theOutput Render of the TV to be an Image, and boom! Your image TV now outputs the image directly! Sweet, huh?
MODx Revolution comes packaged with a few default Output Types. You can also , if you know a little PHP. The list ofcreate your ownpre-packaged ones are:
See Also
Date TV Output Type
Date TV Output Type
This type allows you to output any TV input as a Date, formatted in the way you want.
Output Properties
It's output properties look like:
Name Description
Date Format A format string similar to the .PHP strftime method
Default If no value is set for the TV, use the current time? This defaults to 'no', which will output a blank value.
See Also
Delimiter TV Output Type
Delimiter TV Output Type
This type allows you to output any TV input as a delimited list. It's very useful for TV inputs that store multiple values, such as the multiple selectlist, or checkbox inputs.
The output type will split the value on each double-bar (||), and then output it delimited by the delimiter value you set in the properties.
Output Properties
It's output properties look like:
Name Description
Delimiter A delimiter to separated each item with.
See Also
HTML Tag TV Output Type
HTML Tag TV Output Type
This type allows you to wrap an HTML tag around the input.
Output Properties
It's output properties look like:
Name Description
Tag Name The tag to create, such as div, img, span, etc.
Tag ID The dom ID of the tag.
Class Any CSS classes to add to the tag.
Style Any style attributes to add to the tag.
Attributes Any other attributes you want to add to the tag.
See Also
Adding a Custom TV Input Type
What are Custom TV Input Types?
MODx Revolution allows you to create your own custom TV input types (similar to the textbox, radio, textarea, richtext, etc types alreadyavailable) for your .Template Variables
Creating the Files
To create a custom TV input type (let's say, one called "test"), you need a few things. Let's say my "test" TV input type loads a Template selectingcombobox.
I'd first need 2 files:
An input controller - put here: core/model/modx/processors/element/tv/renders/mgr/input/test.phpAn input template - put here: manager/templates/default/element/tv/renders/input/test.tpl
The input controller, test.php, would have:
$ ->xpdo->lexicon->load('tv_widget');this// any other PHP i want here
$ ->xpdo->smarty->fetch('element/tv/renders/input/test.tpl');return this
And the input template, test.tpl, for the default mgr theme would have (note that it is using syntax):Smarty
<select id= name= class= ></select>"tv{$tv->id}" "tv{$tv->id}" "combobox"<script type= >"text/javascript"// <![CDATA[{literal}MODx.load({{/literal} xtype: 'modx-combo-template' ,transform: 'tv{$tv->id}' ,id: 'tv{$tv->id}' ,width: 300{literal} ,listeners: { 'select': { fn:MODx.fireResourceFormChange, scope: }}this});{/literal}// ]]></script>
And there you go! A custom TV input type.
You don't have to use the ExtJS code as shown here to have a custom input type. It could even just be a straight HTML input.It's really up to you.
See Also
Adding a Custom TV Output Type
What are TV Output Types?Creating the Files
Setting up the Input Properties ControllerSetting up the Input Properties TemplateSetting up the Output Controller
Using the Custom TV Output TypeSee Also
What are TV Output Types?
TV Output Types allow you to output in different markup and formats. Some examples include outputting a TV value as anTemplate Variablesimage, URL, HTML tag, date, etc.
MODx Revolution lets you create custom output types fairly easily.
Creating the Files
Let's create a custom TV Output Type called "button". This will render an input button (or more than one) with a specified value and an optionalname, with some other fields for attributes. You'll need 3 files:
An input properties controller - put here: core/model/modx/processors/element/tv/renders/mgr/properties/button.phpAn input properties template - put here: manager/templates/default/element/tv/renders/properties/button.tplAn output controller - put here: core/model/modx/processors/element/tv/renders/web/output/button.php
Setting up the Input Properties Controller
This is the PHP file that will load the mgr template for managing the TV output type's properties. We'll have it contain just this:
<?php// any custom php you want to run here
$modx->smarty->fetch('element/tv/renders/properties/button.tpl');return
Setting up the Input Properties Template
This is the template for the default manager theme to render properties with. We'll use ExtJS to render some pretty form fields:
<div id= ></div>"tv-wprops-form{$tv}"{literal}<script type= >"text/javascript"// <![CDATA[
params = {var{/literal}{foreach from=$params key=k item=v name='p'} '{$k}': '{$v}'{ NOT $smarty.foreach.p.last},{/ }if if{/foreach}{literal}};
oc = {'change':{fn:function(){Ext.getCmp('modx-panel-tv').markDirty();},scope: }};var thisMODx.load({ xtype: 'panel' ,layout: 'form' ,autoHeight: true ,labelWidth: 150 ,border: false ,items: [{ xtype: 'textfield' ,fieldLabel: _('class') ,name: 'prop_class' ,id: 'prop_class{/literal}{$tv}{literal}' ,value: params['class'] || '' ,width: 300 ,listeners: oc },{ xtype: 'textfield' ,fieldLabel: _('id') ,name: 'prop_id' ,id: 'prop_id{/literal}{$tv}{literal}' ,value: params['id'] || '' ,width: 300 ,listeners: oc },{ xtype: 'textfield' ,fieldLabel: _('style') ,name: 'prop_style' ,id: 'prop_style{/literal}{$tv}{literal}' ,value: params['style'] || '' ,width: 300 ,listeners: oc },{ xtype: 'textfield' ,fieldLabel: _('attributes') ,name: 'prop_attributes' ,id: 'prop_attributes{/literal}{$tv}{literal}' ,value: params['attributes'] || '' ,width: 300 ,listeners: oc }] ,renderTo: 'tv-wprops-form{/literal}{$tv}{literal}'});// ]]></script>{/literal}
The key way these save is that each field is prepended with 'prop_' in its name. This tells MODx to save this field in the TV's output properties.Make sure you specify your fields with this prefix!
You don't have to use ExtJS, however - you can use just straight HTML - it's totally up to you.Note that if you created another manager theme, you'd have to create a properties tpl for that theme as well.
Setting up the Output Controller
Now we get into the good stuff. This controller will handle exactly how the button is outputted. Our file looks like this (comments inline):
<?php$o= '';$buttons= $ ->parseInput($value, '||', 'array');this/* allow multiple buttons separated by ||, or checkbox/multiple input tvs */foreach ($buttons as $button) { (!is_array($button)) {if $button= explode('==', $button); } /* the TV value must have a value of either: text or text==name */ $text = $button[0]; (!empty($text)) {if $attributes = ''; $attr = array( 'class' => $params['class'], 'id' => ($params['id'] ? $params['id'] : ''), 'alt' => htmlspecialchars($params['alttext']), 'style' => $params['style'] ); /* a name is specified, use it! */if (!empty($button[1])) $attr['name'] = $button[1];if
/* separate the attributes into html tag format */ foreach ($attr as $k => $v) $attributes.= ($v ? ' '.$k.'= ' : '');"'.$v.'" $attributes .= ' '.$params['attrib'];
/* Output the image with attributes */ $o .= '<button'.rtrim($attributes).'>'.$text.'</button>'. ;"\n" }}
$o;return
Using the Custom TV Output Type
So, how does it look? Well, it should render an output form like this when editing the TV - I've added some custom values to it as well:
So we'll save this, and then let's go edit in in a Resource. We'll specify two buttons, separating with ||. We could also just do one button. And, we'llhave the first button have a custom 'name' attribute as well:
Great. Now let's preview the resource, and we'll get an output like this:
And we can examine the HTML source:
And there you go! A custom TV output type!
See Also
Properties and Property Sets
What are Properties?What are Property Sets?Assigning Property Sets to ElementsCreating Properties in a Property SetImporting and Exporting PropertiesUsing Properties Programmatically
Using getOptionConclusion
What are Properties?
Properties are simply values that can be configured for any Element via . An example of a Property is the token 'debug' in this SnippetTag Syntaxcall:
[[Quip? &debug=`1`]]
'debug' is the Property, where '1' is the Property Value. They are passed to the Element's parser and interpreted there. Snippets and Plugins canaccess them through the $scriptProperties array, or straight in their key values, as they are extract()'ed.
What are Property Sets?
Property Sets are user-defined collections of properties for an Element. They can be attached to one, or more, Elements via that Element's editingpage. Those property sets, once attached, then can be called in their Element's call syntax like such:
[[ElementName@PropertySetName]]
So, for an example, let's have a Property Set with two properties - 'debug' set to true, and 'user' set to 2. Then let's call it in a snippet.:
[[TestSnippet@DebugMode?user=`1`]]
This example would call the snippet "TestSnippet", load the Property Set 'DebugMode', and then would set the value 'user' to 1. Since theproperty 'user' is defined as 2 in the Property Set, it will be overridden in the call, and end up as 1. The order of property loading is:
Default Element Properties -> Property Set -> Tag-defined Properties
So, if the default property of 'user' was 0, then it would then be set to 2 by the property set, and then to 1 by the call.
The property set can also contain properties not defined in either the default element properties, or in the tag call. This can be useful to loadElements across the site without having to repeat the tag syntax across the site - and make it much easier to manage your tag calls.
Properties will be passed into the Element just as they were in MODx 0.9.6, but they are also passed in via the $scriptProperties array, for thoseof you wanting more flexibility with knowing what properties are passed in.
Assigning Property Sets to Elements
Property Sets can only be used on Elements that they are assigned to. This can be done via either the element's properties grid, or Tools ->Property Sets.
For example, here's an image of a property set named 'TestPropertySet' in a snippet's editing page:
As you can see here, there is a property set loaded with some properties. Properties in green are default properties that have been overridden inthe property set. Properties that are purple are properties that do not exist in the Element's default properties, but are defined in the Property Set.Properties can also have descriptions, as shown by the + icon to the left. When clicked, the description will appear below.
To add a property set to an Element, you'll simply click the "Add Property Set" toolbar item in the top right of the grid. It will show a window likethis:
From there, you can select the property set you want to add. If you'd like to create a completely new Property Set and automatically attach it tothis element, you can do so by checking the "Create New Property Set" checkbox, and these fields will show:
Then once you save your new Property Set, it will be automatically attached to that Element.
Creating Properties in a Property Set
To create a Property in a Property Set, you'll simply need to just load the Property Set you want to work on, and then click "Create Property". Thatwill load this window:
From there, you can create a property set. Note here that we are creating a property of type "List", which is a dropdown property. You can addoptions to that property from the grid. Once you save the property, it will be added to the property set.
Importing and Exporting Properties
You can also import and export properties using the grid. Simply click on the corresponding buttons at the bottom.
When you import properties, it will overwrite your properties in the grid currently. Make sure that you want to do this beforeimporting!
Using Properties Programmatically
Properties are available in a snippet via the $scriptProperties array:
$prop = $scriptProperties['propertyName'];
Note that if a parameter is sent in the snippet call that has the same name as a property in a property set, the parameter value will override thedefault value in the property set.
Using getOption
You can also get a snippet property with $modx->getOption() like this:
$modx->getOption('propertyName', $scriptProperties, ' ');default
Conclusion
Input and Output Filters
What are Filters?Input FiltersOutput FiltersExamplesSee Also
What are Filters?
Filters in Revolution allow you to manipulate the way data is presented or parsed in a tag. They allow you to modify values from inside yourtemplates.
Input Filters
Currently input filters process tag calls. More documentation to come.
Output Filters
In Revolution, Output Filters behave similarly to PHx calls in MODx Evolution - except they're built into the core. The syntax is like such:
[[element:modifier=`value`]]
They can also be chained (executed left to right):
[[element:modifier:anothermodifier=`value`:andanothermodifier:yetanother=`value2`]]
The list of string modifiers:
Modifier Description Example
cat Appends the option's value (if not empty) to the input value [[+numbooks:cat=` books`]]
lcase Similar to PHP's strtolower [[+title:lcase]]
ucase Similar to PHP's strtoupper [[+headline:ucase]]
ucwords Similar to PHP's ucwords [[+title:ucwords]]
ucfirst Similar to PHP's ucfirst [[+name:ucfirst]]
htmlent Similar to PHP's . Uses the current value the system settinghtmlentities"modx_charset"
[[+email:htmlent]]
esc,escape Safely escapes character values [[+email:escape]]
strip Replaces all linebreaks, tabs and multiple spaces with just one space [[+textdocument:strip]]
stripString Strips string of specified value [[+name:stripString=`Mr.`]]
replace Replaces one value with another [[+pagetitle:replace=`Mr.==Mrs.`]]
stripTags,notags Similar to PHP's strip_tags [[+code:strip_tags]]
len,length Similar to PHP's strlen [[+longstring:strlen]]
reverse Similar to PHP's strrev [[+mirrortext:reverse]]
wordwrap Similar to PHP's . Takes optional value to set wordwrap position.wordwrap [[+bodytext:wordwrap=`80`]]
wordwrap Similar to PHP's , with word cutting enabled. Takes optional value towordwrapset wordwrap position.
[[+bodytext:wordwrap=`80`]]
limit Limits a string to a certain number of characters. Defaults to 100. [[+description:limit=`50`]]
ellipsis Adds an ellipsis to and truncates a string if it's longer than a certain number ofcharacters. Defaults to 100.
[[+description:ellipsis=`50`]]
tag Displays the raw element without the :tag. Useful for documentation. [[+showThis:tag]]
math Returns the result of an advanced calculation (expensive on processor. notrecommended)
add,increment,incr Returns input incremented by option (default: +1) [[+downloads:incr]] [[+blackjack:add=`21`]]
subtract,decrement,decr Returns input decremented by option (default: -1) [[+countdown:decr]] [[+moneys:subtract=`100`]]
multiply,mpy Returns input multiplied by option (default: *2) [[+trifecta:mpy=`3`]
divide,div Returns input divided by option (default: /2) Does not accept 0. [[+rating:div=`4`]]
modulus,mod Returns the option modulus on input (default: %2, returns 0 or 1) [[+number:mod]]
ifempty,default,empty Returns the input value if empty [[+name:default=`anonymous`]]
notempty Returns the input value if not empty [[+name:notempty=`Hello[[+name]]!`]]
nl2br Similar to PHP's nl2br [[+textfile:nl2br]]
date Similar to PHP's . Value is format.strftime [[+birthyear:date=`%Y`]]
strtotime Similar to PHP's . Takes in a date.strtotime [[+thetime:strtotime]]
fuzzydate Returns a pretty date format with yesterday and today being filtered. Takes in adate.
[[+createdon:fuzzydate]]
ago Returns a pretty date format in seconds, minutes, weeks or months ago. Takesin a date.
[[+createdon:ago]]
md5 Similar to PHP's .md5 [[+password:md5]]
cdata Wraps the text with CDATA tags [[+content:cdata]]
userinfo Returns the requested user data. The element must be a modUser ID. The valuefield is the column to grab.
[[+userId:userinfo=`username`]]
isloggedin Returns true if user is authenticated in this context.
isnotloggedin Returns true if user is not authenticated in this context.
urlencode Similar to PHP's urlencode [[+mystring:urlencode]]
urldecode Similar to PHP's urldecode [[+myparam:urldecode]]
Examples
A good example of chaining would be to format a date string to another format, like so:
[[+mydate:strtotime:date=`%Y-%m-%d`]]
Directly accessing the modx_user_attributes table in the database using filters instead of a can be accomplished simply by utilizing theSnippetuserinfo filter. Select the appropriate column from the table and link to it, like so:
User Internal Key: [[+userId:userinfo=`internalKey`]]<br />User name: [[+userId:userinfo=`username`]]<br />Full Name:[[+userId:userinfo=`fullname`]]<br />Role: [[+userId:userinfo=`role`]]<br />E-mail: [[+userId:userinfo=`email`]]<br />Phone: [[+userId:userinfo=`phone`]]<br />Mobile Phone: [[+userId:userinfo=`mobilephone`]]<br />Fax: [[+userId:userinfo=`fax`]]<br />Date of birth: [[+userId:userinfo=`dob`:date=`%Y-%m-%d`]]<br />Gender[[+userId:userinfo=`gender`]]<br />Country: [[+userId:userinfo=`country`]]<br />State: [[+userId:userinfo=`state`]]<br />Zip Code: [[+userId:userinfo=`zip`]]<br />Photo: [[+userId:userinfo=`photo`]]<br />Comment: [[+userId:userinfo=`comment`]]<br />Password: [[+userId:userinfo=`password`]]<br />Cache Password: [[+userId:userinfo=`cachepwd`]]<br />Last Login: [[+userId:userinfo=`lastlogin`:date=`%Y-%m-%d`]]<br />The Login:[[+userId:userinfo=`thislogin`:date=`%Y-%m-%d`]]<br />
of Logins: [[+userId:userinfo=`logincount`]]Number
Note that the user ID and username is already available by default in MODx, so you dont need to use the "userinfo" filter:
[[+modx.user.id]] - Prints the ID[[+modx.user.username]] - Prints the username
Also, can be used as custom modifiers and filters. Simply put the name instead of the modifier. Example with a snippet namedSnippets Snippet'makeDownloadLink':
[[+file:makeDownloadLink=`notitle`]]
This will pass these properties to the snippet:
Param Value Example Result
input The element's value. The value of [[+file]]
options Any value passed to the modifier. 'notitle'
token The type of the parent element. + (the token on `file`)
name The name of the parent element. file
tag The complete parent tag. [[+ `notitle`]]file:makeDownloadLink=
And then the return value of that call would be whatever the snippet returns.
See Also
Properties and Property SetsTemplatesTemplate VariablesSnippets
Administering Your Site
This section contains information about maintaining and administering your MODx site.
Settings
What are Settings?
Settings are site-wide variables that can be used by either the MODx Core or by 3rd-Party Components to provide site, context, or user-levelcustomization.
Usage
They can be referenced at any point via their Tag, for example, for the 'site_start' Setting:
[[++site_start]]
System Settings can also be overridden by Context Settings, which are specific to each . Context Settings can be overridden by UserContextSettings, which are specific to each user. So, the order of relevance is:
System Setting -> Context Setting -> User Setting
Let's say I set the System Setting named 'use_editor' to 0. However, I created a Context Setting called 'use_editor' for the 'mgr' context and set itto 1. This would mean that any time I'm in the mgr context, the setting would be 1, overriding the System Setting.
Further, I've got a user named 'johndoe' who I don't want to use the editor. I create a User Setting 'use_editor' for his user and set it to 0. Now,John Doe's "use_editor" setting will be 0, overriding the Context Setting.
Settings can also be specific to , as well. This allows you to easily group your settings for each of your different Components.Namespaces
Retrieving Settings in PHP
Getting settings is simple in MODx Revolution; simply use . For example, to get the setting 'site_start', simply:getOption
$siteStartId = $modx->getOption('site_start');
Now, all settings are overridable by Context and User, as described above, and getOption respects that. So, if in the above code example, if youhad set site_start as a Context Setting that overrode the System Setting, getOption will respect that - but only if you're executing the PHP in thatContext that has the Setting.
For example, if I were using that code block in a Context called 'boo', and I had added a Context Setting for site_start in the 'boo' Context, and setit to 3, the above code would output '3'.
Now if I were in the 'web' context, and site_start was still '1' (from the System Setting), getOption would return 1.
Default values with getOption
getOption supports 3 parameters:
1. The key of the setting2. An array to search first before looking for the setting3. A default value should the setting not be found.
So, for example, if I were in a Snippet and wanted some default properties at the top, I could use getOption. automatically pass in anSnippetsarray of all the Properties attached to that snippet (or specified in the Tag call) via the $scriptProperties array. So, you can use that array to checkfor default properties. This example sets a default value to the 'showPublished' property should it not be specified:
$showPublished = $modx->getOption('showPublished',$scriptProperties, );true
Now, assuming the Snippet doesnt have showPublished as a , if you called the Snippet via the tag call:default property
[[mySnippet]]
showPublished will be set to true. If it did have the default Property attached to it that set the value to 0, or the showPublished property wasspecified as 0 in the tag, then showPublished would be 0.
Additional Information
Only use getObject if you're checking for an existing setting in the DB because you want to create onegetOption uses the settings cache (its much faster)getOption will also check User -> Context -> System settings (allowing you to override system settings with context and further with usersettings)
See Also
System Settings
MODx comes with a flexible amount of system settings. They are found in System -> System Settings, and can easily be edited and changed.
Settings List
A description of each setting follows:
allow_duplicate_alias
allow_duplicate_alias
Name: Allow Duplicate Aliases: Yes/NoType
: NoDefault
If set to 'yes', this will allow duplicate aliases to be saved.
This option should be used with set to 'Yes' in order to avoid problems when referencing a resource.use_alias_path
allow_multiple_emails
allow_multiple_emails
Name: Allow Duplicate Emails for Users: Yes/NoType
: YesDefault
If enabled, Users may share the same email address.
allow_tags_in_post
allow_tags_in_post
Name: Allow Tags in Post: Yes/NoType
: YesDefault
If set to true, will allow POST requests to contain HTML form tags.
Do not change this value for the manager context. Only use this in a Context if you specifically want to. This can causeproblems in a MODx install if you change it without an explicit purpose.
auto_check_pkg_updates
auto_check_pkg_updates
Name: Automatic Check for Package Updates: Yes/NoType
: YesDefault
If 'Yes', MODx will automatically check for updates for packages in Package Management. This may slow the loading of the Packages grid.
auto_check_pkg_updates_cache_expire
auto_check_pkg_updates_cache_expire
Name: Cache Expiration Time for Automatic Package Updates Check: NumberType
: 15Default
The number of minutes that will cache the results when checking for package updates.Package Management
auto_menuindex
auto_menuindex
Name: Menu Indexing Default: Yes/NoType
: YesDefault
Select 'Yes' to turn on automatic menu index incrementing by default.
blocked_minutes
blocked_minutes
Name: Blocked Minutes: NumberType
: 60Default
The number of minutes that a user will be blocked for if they reach their maximum number of allowed failed login attempts.
cache_action_map
cache_action_map
Name: Enable Action Map Cache: Yes/NoType
: YesDefault
When enabled, actions (or controller maps) will be cached to greatly reduce manager page load times.
cache_context_settings
cache_context_settings
Name: Enable Context Setting Cache: Yes/NoType
: YesDefault
When enabled, context settings will be cached to reduce load times.
cache_db
cache_db
Name: Enable Database Cache: Yes/NoType
: NoDefault
When enabled, objects and raw result sets from SQL queries are cached to significantly reduce database loads.
This can take up a good amount of hard drive space. Only do this if you have enough space to scale the requests.
cache_db_expires
cache_db_expires
Name: Expiration Time for DB Cache: NumberType
: 0Default
Default time for the expiration of the database cache. If set to '0', the cache will never expire unless a record is updated.
cache_default
cache_default
Name: Cacheable Default: Yes/NoType
: YesDefault
Makes all new resources cacheable by default.
cache_disabled
cache_disabled
Name: Disable Global Cache Options: Yes/NoType
: NoDefault
If true, disables all MODx caching features.
This feature is . MODx recommends not turning off caching site-wide, as it can significantly slow down your site.experimental
cache_handler
cache_handler
Name: Cache Handler Class
: StringType: xPDOFileCacheDefault
The class name of the type handler to use for caching. Can be set to a custom caching class you provide, or xPDOMemCache if you havememcached installed.
cache_json
cache_json
Name: Cache JSON Data: Yes/NoType
: NoDefault
If Yes, caching will be stored JSON format.
cache_json_expires
cache_json_expires
Name: Expiration Time for JSON Cache: NumberType
: 0Default
Expiration time for JSON format cached data. A value of zero means the cache never expires.
cache_lexicon_topics
cache_lexicon_topics
Name: Cache Lexicon Topics: Yes/NoType
: YesDefault
When enabled, all Lexicon Topics will be cached so as to greatly reduce load times for Internationalization functionality. MODx stronglyrecommends leaving this set to Yes, as setting it to No will greatly slow down your manager load times.
cache_noncore_lexicon_topics
cache_noncore_lexicon_topics
Name: Cache Non-Core Lexicon Topics: Yes/NoType
: YesDefault
When disabled, non-core Lexicon Topics will be not be cached. This is useful to disable when developing your own Extras.
cache_resource
cache_resource
Name: Enable Partial Resource Cache: Yes/NoType
: YesDefault
Partial Resource caching is configurable by resource when this feature is enabled. Disabling this feature will disable it globally.
cache_resource_expires
cache_resource_expires
Name: Expiration Time for Partial Resource Cache: NumberType
: 0Default
Expiration time for the Partial Resource Cache setting. A value of 0 means the cache will never expire.
cache_scripts
cache_scripts
Name: Enable Script Cache: Yes/NoType
: YesDefault
When enabled, MODx will cache all Scripts (Snippets and Plugins) to file to reduce load times. MODx recommends leaving this set to 'Yes'.
compress_css
compress_css
Name: Use Compressed CSS Stylesheets: Yes/NoType
: YesDefault
When this is enabled, MODx will use a compressed version of its custom CSS stylesheets. This greatly reduces load and execution time. Disableonly if you are modifying core elements.
This setting will not work in Git deployments of Revolution. Leave it at 'No'.
compress_js
compress_js
Name: Use Compressed Javascript Libraries: Yes/NoType
: YesDefault
When this is enabled, MODx will use a compressed version of its custom JavaScript libraries. This greatly reduces load and execution time.Disable only if you are modifying core elements.
This setting will not work in Git deployments of Revolution. Leave at 'No'.
concat_js
concat_js
Name: Use Concatenated Javascript Libraries: Yes/NoType
: YesDefault
When this is enabled, MODx will use a concatenated version of its custom core JavaScript libraries. This greatly reduces load and execution time.Disable only if you are modifying core elements.
This setting will not work in Git deployments of Revolution. Leave set to 'No'.
container_suffix
container_suffix
Name: Container Suffix: StringType
: /Default
Sets the container suffix for the site. This is the suffix added to the Friendly URL when a Resource is checked as a container.
Example
A container suffix of / will render a Resource with an alias of 'test' as:
www.mysite.com/test/
whereas a container suffix of '.html' will render:
www.mysite.com/test.html
cultureKey
cultureKey
Name: Language: StringType
: enDefault
Select the language for all non-manager Contexts, including web.
custom_resource_classes
custom_resource_classes
Name: Custom Resource Classes: StringType
:Default
A comma-separated list of custom Resource classes. Specify with lowercase_lexicon_key:className (Ex: wiki_resource:WikiResource). Allcustom Resource classes must extend modResource. To specify the controller location for each class, add a setting with[nameOfClassLowercase]_delegate_path with the directory path of the create/update php files. Ex: wikiresource_delegate_path for a classWikiResource that extends modResource.
default_template
default_template
Name: Default Template: TemplateType
: 1Default
The default to use for new Resources. You can still select a different Template in the Resource editing page; this setting just pre-selectsTemplateone of the Templates.
editor_css_path
editor_css_path
Name: Path to CSS file: StringType
:Default
The path to your CSS file that you wish to use within the editor. The best way to enter the path is to enter the path from the root of your server, forexample: /assets/site/style.css. If you do not wish to load a style sheet into the editor, leave this field blank.
editor_css_selectors
editor_css_selectors
Name: CSS Selectors for Editor
: StringType:Default
emailsender
emailsender
Name: Site Admin Email Address: StringType
: Set at InstallDefault
The e-mail address used when sending users their usernames and passwords.
emailsubject
email_subject
Name: Email Subject: StringType
: Your login detailsDefault
The subject line for the default signup email.
error_page
error_page
Name: Error Page: NumberType
: 1Default
The ID of the resource you want to send users to if they request a Resource which doesn't actually exist.
failed_login_attempts
failed_login_attempts
Name: Failed Login Attempts: NumberType
: 3Default
The number of times a user can attempt to login before being blocked.
fe_editor_lang
fe_editor_lang
Name: Front-end Editor Language: StringType
: englishDefault
The language for the editor to use when used as a front-end editor.
This setting is and no longer in use.deprecated
feed_modx_news
feed_modx_news
Name: MODx News Feed URL: StringType
: Default http://feeds.feedburner.com/modx-announce
Set the URL for the RSS feed for the MODx News panel in the manager.
feed_modx_news_enabled
feed_modx_news_enabled
Name: Enable MODx News Feed: Yes/NoType
: YesDefault
If 'No', MODx will hide the News feed in the welcome section of the manager.
feed_modx_security
feed_modx_security
Name: MODx Security Feed URL: StringType
: Default http://feeds.feedburner.com/modxsecurity
Set the URL for the RSS feed for the MODx Security panel in the manager.
feed_modx_security_enabled
feed_modx_security_enabled
Name: Enable MODx Security Feed: Yes/NoType
: YesDefault
If 'No', MODx will hide the Security feed in the welcome section of the manager.
filemanager_path
filemanager_path
Name: File Manager Path: stringType
:Default
Determines the root of the file browser for the currently-logged in user in the manager backend.
Usage
Specify either a relative path from the MODx root directory, or an absolute path. It can be outside of the webroot.
friendly_alias_urls
friendly_alias_urls
Name: Use Friendly URL Aliases: Yes/NoType
: NoDefault
Turns on friendly URLs, which generate SEO-friendly URL mappings for your Resources. The URL map is determined by the Resource's place inthe site tree, and its "alias" field.
For example, a Resource with alias 'blog' and a Content Type of "HTML" will be rendered as www.mysite.com/blog.html if it's not a container. Ifthe blog Resource was under another Resource with an alias of 'test', then the blog Resource's URL would be:
www.mysite.com/test/blog.html
This allows for completely customizable and automatically built SEO-friendly URLs.
You'll need to rename the ht.access file to .htaccess in your site root, and change the RewriteBase parameter in it to your site'sbase URL to get this to work.
friendly_url_prefix
friendly_url_prefix
Name: Friendly URL Prefix: StringType
:Default
This setting is and is no longer available in MODx Revolution.deprecated
Here you can specify the prefix to use for friendly URLs. For example, a prefix setting of 'page' will turn the URL /index.php?id=2 to the friendlyURL /page2.html (assuming the suffix is set to .html). This way you can specify what your users (and search engines) see for links on your site.
friendly_url_suffix
friendly_url_suffix
Name: Friendly URL Suffix: StringType
: .htmlDefault
This setting is and is no longer in use. See for how suffixes are now handled.deprecated Content Types
Here you can specify the suffix for Friendly URLs. Specifying '.html' will append .html to all your friendly URLs.
friendly_urls
friendly_urls
Name: Use Friendly URLs: Yes/NoType
: NoDefault
This allows you to use search engine friendly URLs with MODx. Please note, this only works for MODx installations running on Apache, and you'llneed to write a .htaccess file for this to work. See the .htaccess file included in the distribution for more info.
mail_charset
mail_charset
Name: Mail Charset: StringType
: UTF-8Default
The (default) charset for e-mails, e.g. 'iso-8859-1' or 'UTF-8'.
mail_encoding
mail_encoding
Name: Mail Encoding: StringType
: 8bitDefault
Sets the Encoding of the message. Options for this are "8bit", "7bit", "binary", "base64", and "quoted-printable".
mail_smtp_auth
mail_smtp_auth
Name: Use SMTP Authentication: Yes/NoType
: NoDefault
Sets SMTP authentication. Utilizes the and settings.mail_smtp_user mail_smtp_pass
mail_smtp_helo
mail_smtp_helo
Name: SMTP Helo Message: StringType
:Default
Sets the SMTP HELO of the message (Defaults to the hostname).
mail_smtp_hosts
mail_smtp_hosts
Name: SMTP Hosts: StringType
: localhostDefault
Sets the SMTP hosts. All hosts must be separated by a semicolon. You can also specify a different port for each host by using this format:hostname:port (e.g. "smtp1.example.com:25;smtp2.example.com"). Hosts will be tried in order.
mail_smtp_keepalive
mail_smtp_keepalive
Name: SMTP User: Yes/NoType
: NoDefault
Prevents the SMTP connection from being closed after each mail sending. Not recommended.
mail_smtp_pass
mail_smtp_pass
Name: SMTP Password: StringType
:Default
The password to authenticate to SMTP against.
mail_smtp_port
mail_smtp_port
Name: SMTP Port: StringType
: 587Default
Sets the default SMTP server port.
mail_smtp_prefix
mail_smtp_prefix
Name: SMTP Connection Prefix: StringType
:Default
Sets connection prefix. Options are "", "ssl" or "tls".
mail_smtp_single_to
mail_smtp_single_to
Name: SMTP Single-To: Yes/NoType
: NoDefault
Provides the ability to have the TO field process individual emails, instead of sending to entire TO addresses.
mail_smtp_timeout
mail_smtp_timeout
Name: SMTP Timeout: NumberType
: 10Default
Sets the SMTP server timeout in seconds. This function will not work in win32 servers.
mail_smtp_user
mail_smtp_user
Name: SMTP User: StringType
:Default
The user to authenticate to SMTP against.
mail_use_smtp
mail_use_smtp
Name: Use SMTP in Mail: Yes/NoType
: NoDefault
If true, MODx will attempt to use SMTP in mail functions.
manager_date_format
manager_date_format
Name: Manager Date Format: StringType
: Y-m-dDefault
The format string, in PHP date() format, for the dates represented in the manager.
manager_direction
manager_text_direction
Name: Manager Text Direction: StringType
: ltrDefault
The direction that the text will be rendered in the Manager, left to right or right to left.
manager_lang_attribute
manager_lang_attribute
Name: Manager HTML and XML Language Attribute: StringType
: enDefault
The language code that best fits with your chosen manager language. This will ensure that the browser can present content in the best format foryou.
manager_language
manager_language
Name: Language for the Manager: StringType
: enDefault
The language for the MODx Manager interface. This will override any cultureKey setting for the mgr context.
manager_theme
manager_theme
Name: Manager Theme: StringType
: defaultDefault
The current Theme for the backend Manager.
manager_time_format
manager_time_format
Name: Manager Time Format: StringType
: g:i aDefault
The format string, in PHP date() format, for the time settings represented in the manager.
modx_charset
modx_charset
Name: Character Encoding: stringType
: UTF-8Default
The character encoding used. Please note that MODx primarily works with UTF-8.
new_file_permissions
new_file_permissions
Name: New File Permissions: stringType
: 0644Default
When uploading a new file in the File Manager, the File Manager will attempt to change the file permissions to those entered in this setting. Thismay not work on some setups, such as IIS, in which case you will need to manually change the permissions.
new_folder_permissions
new_folder_permissions
Name: New Folder Permissions: StringType
: 0755Default
When creating a new folder in the File Manager, the File Manager will attempt to change the folder permissions to those entered in this setting.This may not work on some setups, such as IIS, in which case you will need to manually change the permissions.
password_generated_length
password_generated_length
Name: Password Auto-Generated Length: NumberType
: 8Default
The length of the auto-generated password for a User.
password_min_length
password_min_length
Name: Password Minimum Length: NumberType
: 8Default
The minimum length in characters for a password for a User.
phpthumb_cache_maxage
phpthumb_cache_maxage
Name: phpThumb Max Cache Age: NumberType
: 30 (days)Default
Delete cached thumbnails that have not been accessed in more than X days.
phpthumb_cache_maxfiles
phpthumb_cache_maxfiles
Name: phpThumb Max Cache Files: NumberType
: 10000 (10k files)Default
Delete least-recently-accessed thumbnails when cache has more than X files.
phpthumb_cache_maxsize
phpthumb_cache_maxsize
Name: phpThumb Max Cache Size: NumberType
: 100 (mb)Default
Delete least-recently-accessed thumbnails when cache grows bigger than X megabytes in size.
phpthumb_cache_source_enabled
phpthumb_cache_source_enabled
Name: phpThumb Cache Source Files: Yes/NoType
: NoDefault
Whether or not to cache source files as they are loaded. Recommended to leave at No.
phpthumb_far
phpthumb_far
Name: phpThumb Force Aspect Ratio: Yes/NoType
: NoDefault
The default far setting for phpThumb when used in MODx. Defaults to C to force aspect ratio toward the center.
phpthumb_zoomcrop
phpthumb_zoomcrop
Name: phpThumb Zoom-Crop: StringType
: 0Default
The default zc setting for phpThumb when used in MODx. Defaults to 0 to prevent zoom cropping.
proxy_auth_type
proxy_auth_type
Name: Proxy Authentication Type: StringType
: BASICDefault
Supports either BASIC or NTLM.
proxy_host
proxy_host
Name: Proxy Host: StringType
:Default
The hostname of your proxy server. If this is specified, MODx will attempt to use the proxy when connecting to the RSS feeds or Package.Management
proxy_password
proxy_password
Name: Proxy Password: StringType
:Default
The password to authenticate with on your proxy server.
proxy_port
proxy_port
Name: Proxy Port: StringType
:Default
The port for your proxy server.
proxy_username
proxy_username
Name: Proxy Username: StringType
:Default
The username to authenticate against with your proxy server.
publish_default
publish_default
Name: Published Default: Yes/NoType
: NoDefault
If true, makes all new resources published by default.
rb_base_dir
rb_base_dir
Name: Resource Path: StringType
:Default
The physical path to the resource directory. This setting is usually automatically generated. If you're using IIS, however, MODx may not be able towork the path out on its own, causing the Resource Browser to show an error. In that case, you can enter the path to the images directory here(the path as you'd see it in Windows Explorer).
This setting is in MODx Revolution. Please use instead.deprecated filemanager_path
rb_base_url
rb_base_url
Name: Resource Browser Base URL: StringType
:Default
Enter the virtual path to resource directory. This setting is usually automatically generated. If you're using IIS, however, MODx may not be able towork the URL out on it's own, causing the Resource Browser to show an error. In that case, you can enter the URL to the images directory here(the URL as you'd enter it on Internet Explorer).
This setting is in MODx Revolution. Please use instead.deprecated filemanager_path
request_controller
request_controller
Name: Request Controller Filename: StringType
: index.phpDefault
The filename of the main request controller from which MODx is loaded. Most users can leave this as index.php.
request_param_alias
request_param_alias
Name: Request Alias Parameter: StringType
: qDefault
The name of the GET parameter to identify Resource aliases when redirecting with FURLs.
request_param_id
request_param_id
Name: Request ID Parameter: StringType
: qDefault
The name of the GET parameter to identify Resource IDs when not using FURLs.
search_default
search_default
Name: Published Default: Yes/NoType
: YesDefault
If true, makes all new resources searchable by default.
server_offset_time
server_offset_time
Name: Server Offset Time: NumberType
: 0Default
Select the number of hours time difference between where you are and where the server is.
server_protocol
server_protocol
Name: Server Protocol: StringType
: httpDefault
If your site is on a normal http or secure https connection.
session_cookie_domain
session_cookie_domain
Name: Session Cookie Domain: StringType
: localhostDefault
Use this setting to customize the session cookie domain.
This setting isn't in MODx by default, as it's best to let PHP calculate this on its own. Only set this if you are sure of what you aredoing.
session_cookie_lifetime
session_cookie_lifetime
Name: Session Cookie Lifetime: NumberType
: 604800Default
Use this setting to customize the session cookie lifetime in seconds. This is used to set the lifetime of a client session cookie when they choosethe 'remember me' option on login.
session_cookie_path
session_cookie_path
Name: Session Cookie Path: StringType
: /Default
Use this setting to customize the cookie path for identifying site specific session cookies.
This setting isn't in MODx by default. It's best to let PHP figure this out on its own. Only set this if you know what you are doing.
session_cookie_secure
session_cookie_secure
Name: Session Cookie Secure: Yes/NoType
: NoDefault
Enable this setting to use secure session cookies.
session_handler_class
session_handler_class
Name: Session Handler Class: StringType
: modSessionHandlerDefault
For database managed sessions, use 'modSessionHandler'. Leave this blank to use standard PHP session management, or set to your owncustom session handling class.
session_name
session_name
Name: Session Name: StringType
: modxcmssessionDefault
Use this setting to customize the session name used for the sessions in MODx.
This setting isn't set by default in MODx, as it's best to let PHP calculate this on its own. Only set this if you know what you aredoing.
settings_version
settings_version
Name: MODx Version: StringType
: modx-2.0.0-plDefault
The current version of MODx.
Do change this value! Changing it could seriously affect your system.not
signupemail_message
signupemail_message
Name: Signup Email Message: StringType
:Default
Hello [[+uid]]
Here are your login details [[+sname]] Content Manager:for
Username: [[+uid]]Password: [[+pwd]]
Once you log into the Content Manager ([[+surl]]), you can change your password.
Regards,Site Administrator
Here you can set the message sent to your users when you create an account for them and let MODx send them an e-mail containing theirusername and password.
The following placeholders are replaced by the Content Manager when the message is sent:
[[+sname]] - Name of your web site[[+saddr]] - Your web site email address[[+surl]] - Your site url[[+uid]] - User\'s Login name or id[[+pwd]] - User\'s password[[+ufn]] - User\'s full name.
Leave the [[+uid]] and [[+pwd]] in the e-mail, or else the username and password won't be sent in the mail and your users won'tknow their username or password!
site_name
site_name
Name: Site Name: StringType
: MODx RevolutionDefault
The name of your MODx site.
site_start
site_start
Name: Site Start: NumberType
: 1Default
The ID of the resource you want to use as your homepage.
Make sure the ID you enter belongs to an existing Resource that has been published!
site_status
site_status
Name: Site Status: Yes/NoType
: YesDefault
If false, your visitors will see the 'Site unavailable message', and won't be able to browse the site.
Note that users with the 'view_offline' Permission (such as users in the Administrator group) will still be able to browse the site in offline mode.
site_unavailable_message
site_unavailable_message
Name: Site Unavailable Message: StringType
:Default
Message to show when the site is offline or if an error occurs.
This message will only be displayed if the option is set to No.site_status
site_unavailable_page
site_unavailable_page
Name: Unavailable Page: NumberType
: 1Default
Enter the ID of the Resource you want to use as an offline page here.
Make sure this ID you enter belongs to an existing resource that has been published!
strip_image_paths
strip_image_paths
Name: Rewrite Browser Paths: Yes/NoType
: YesDefault
If this is set to 'No', MODx will write file browser resource src's (images, files, flash, etc.) as absolute URLs. Relative URLs are helpful should youwish to move your MODx install, e.g., from a staging site to a production site. If you have no idea what this means, it's best just to leave it set to'Yes'.
This setting is in MODx Revolution and is no longer in use.deprecated
tree_root_id
tree_root_id
Name: Tree Root ID: StringType
:Default
Set this to a comma-separated list of valid IDs of Resources to start the left Resource tree at below those nodes as the root. The user will only beable to see Resources that are children of those specified Resources.
udperms_allowroot
udperms_allowroot
Name: Allow Root Resource Creation: Yes/NoType
: NoDefault
If true, allows users to create new resources in the root of the site.
This setting is in MODx Revolution. It has been replaced by the 'new_document_in_root' in thedeprecated PermissionAdministrator Policy.
unauthorized_page
unauthorized_page
Name: Unauthorized Page: NumberType
: 1Default
The ID of the resource you want to send users to if they have requested a secured or unauthorized resource.
This will only work if you have a 'load' Permission (via a Load policy or custom policy) set to the context that the Resource being accessed is in.
Make sure the ID you enter belongs to an existing resource that has been published and is publicly accessible!
upload_maxsize
upload_maxsize
Name: Maximum Upload Size: NumberType
: 1048576Default
The maximum file size that can be uploaded via the file manager. Upload file size must be entered in bytes.
Large files can take a very long time to upload. Also ensure that your PHP setup can handle large files in its max post sizesetting in the php.ini.
use_alias_path
use_alias_path
Name: Use Friendly Alias Path: Yes/NoType
: NoDefault
Setting this option to 'yes' will display the full path to the document if the document has an alias.
For example, if a document with an alias called 'child' is located inside a container document with an alias called 'parent', then the full alias path tothe document will be displayed as '/parent/child.html'.
use_browser
use_browser
Name: Use File Browser: Yes/NoType
: YesDefault
If set to Yes, enables the resource browser. This will allow your users to browse and upload resources such as images, flash and media files onthe server.
This setting is in MODx Revolution. Use the file_tree and file_manager instead.deprecated Permissions
use_editor
use_editor
Name: Enable RichText Editor: Yes/NoType
: YesDefault
If Yes, will enable any installed Rich Text Editor in the manager.
use_multibyte
use_multibyte
Name: Use Multibyte Extension: Yes/NoType
: Determined at InstallDefault
Set to true if you want to use the mbstring extension for multibyte characters in your MODx installation. Only set to true if you have the mbstringPHP extension installed.
Turning this on will use the setting for the charset in the mb_ PHP functions.modx_charset
welcome_screen
welcome_screen
Name: Show Welcome Screen: Yes/NoType
: NoDefault
If set to true, the welcome screen will show on the next successful loading of the welcome page, and then not show after that.
which_editor
which_editor
Name: RichText Editor to Use
1.
2.
: Richtext ComboType:Default
Selects which rich text editor to use when editing Resources. You can download and install additional Rich Text editors from Package.Management
which_element_editor
which_element_editor
Name: Which Element Richtext Editor: Richtext ComboType
:Default
Select which RTE you would like to use when editing an Element.
Using Friendly URLs
Summary:
You can have firendly URLs fully functioning in under two minutes by following a simple three step process:
1) Working .htaccess sample
MODx supplies an ht.access file for you to edit to match your server settings. This file will need to renamed to .htaccess when uploaded to theroot of your site (can be anywhere above the MODx installation or at the top of the site). You can also simply cut and paste this one into any texteditor and edit it accordingly:
change the DOMAINNAME to your domain name in lowercase.For me: shawnwilkersonchange the TLD to the com, net, org, info, whatever you haveFor me: com
# Friendly URLs PartRewriteEngine OnRewriteBase /RewriteCond %{HTTP_HOST} .# Force all pages to go to www.domain.com SEOforRewriteCond %{HTTP_HOST} !^www\.DOMAINNAME\.TLD [NC]RewriteRule (.*) http://www.DOMAINANME.TLD/$1 [R=301,L]# Friendly URLsRewriteCond %{REQUEST_FILENAME} !-fRewriteCond %{REQUEST_FILENAME} !-dRewriteRule ^(.*)$ index.php?q=$1 [L,QSA]# Additional Settings FollowExpiresActive OnExpiresByType image/gif A2592000ExpiresByType image/jpeg A2592000ExpiresByType image/png A2592000BrowserMatch brokenvary=1"MSIE"BrowserMatch brokenvary=1"Mozilla/4.[0-9]{2}"BrowserMatch !brokenvary"Opera"SetEnvIf brokenvary 1 force-no-vary
/htdocs is fine or /public_html or what ever. your server usesThe .htacess has to be at the same level as the main index.php or above the document MODx is presenting.
Be aware some control panels like to write their own .htaccess just above the site level so the best place to put it is where thehome page of the site points to (view image above)
The RewriteBase should end with a /
This works fine for Redhat Linux 5 / Apache setups. If it is not working for you contact your hosting provider -- send them this code and ask themwhat you need to get it working. Italicized content left to show entire file, but is not necessary for Friendly URLS.
2) Configure MODx Revolution
Next change the settings in the Friendly URLs Area of the System Settings in the System menu of the Revolution Manager to something like thefollowing image indicates, changing the "Automatically generate alias" to YES from the default NO (shown below).
I personally had issues with linking directly to my resume.pdf file so I turned off . More than likely it was something Iautomatically generate aliaswas not doing correctly, as my resume was the first thing I did after turning on Friendly URLS and even before building the first page or templates.
3) Edit your template(s)
Make sure you have the following between the <head></head> tags:
<base href= />"[[!++site_url]]"
4) Clear the MODx Revolution cache
And you're done!
Contexts
Contexts allow MODx configuration settings to be overridden, extended, isolated, or shared across domains, sub-domains, sub-sites, multi-sites,cultural-specific sections, specific web applications, etc.
You can easily create a context from the Contexts menu under Tools. The context will then show up in your resource tree. Resources can easilybe dragged between contexts to move them from one context to another.
Note: there's nothing fundamentally about resources in different contexts, except that they now inherit the configurationdifferentsettings of the context they are in. So, if you create a new context, you'll have to override the context settings in the context forany real, distinguishable change to appear.
Creating a Context
First, go to the Contexts page, via System -> Contexts. Then, click on "Create New" in the grid. This will prompt you for a key and description.From there, right-click on your newly-created context, and click "Update Context".
This will bring you to a screen displaying the Context, and an empty grid of settings. From here you can add Context-specific settings that willoverride any System Settings. Your new context will be completely empty, requiring you to include any and all settings you will be using.
See Also
Creating a Subdomain from a Folder using Virtual Hosts
This case study will show an example usage of Contexts to create a separate subdomain manageable in a single Revolution install. We're goingto create a subdomain at dev.modxcms.com (of course, not really, but you get the idea).
Creating the Context
First off, go to System -> Contexts. Then, create a new context. We'll call it "dev".
Go ahead and create a "Dev Home" Resource in the Resource tree to the left. Place it in the root of the "dev" context. Also, while you're at it,create a "Dev Docs" Resource as well with an alias of "documentation". We'll use this to test our context links.
Your tree should look something like this:
From there, go ahead and right-click on the "dev" Context in the tree, and click "Edit Context". From here we'll see a Context Settings tab. Click onit, and you'll need to add a few settings:
site_start - Set this to the ID of your "Dev Home" resource.base_url - Set this to "/" (no quotes) since we're making the root of the URL our base.http_host - Set this to "dev.modxcms.com" (or your subdomain name)site_url - Set this to "http://dev.modxcms.com/" (or your subdomain url). Don't forget the trailing slash. Remember this setting is(scheme+http_host+base_url).
You can add other context-specific settings, such as error_page, unauthorized_page, and others if you so choose.
Note: If you're going to be linking back to the 'web' context from this context, you'll want to add those same Context Settings(with 'web'-specific values, of course) to the 'web' context. This allows MODx to know where to redirect 'web' context URLs backto.
After creating the settings, clear your site cache.
Creating the Virtual Host
Now we need to do some Apache work. (If you're not using Apache, you can at least follow the same idea and customize it to your server.) Go toApache's httpd.conf file, and add these lines, changing where necessary for your domain name:
NameVirtualHost dev.modxcms.com<VirtualHost dev.modxcms.com> ServerAdmin [email protected] DocumentRoot /home/modxcms.com/public_html/dev ServerName dev.modxcms.com ErrorLog logs/devmodxcms-error_log TransferLog logs/devmodxcms-access_log</VirtualHost>
Some Apache installs prefer you to put the IP address of the server in the VirtualHost and NameVirtualHost parameters - this isfine; the important field is the ServerName.
Obviously, if you're creating a different subdomain than dev.modxcms.com, you'd want to change these values.
Great! Restart your server (apachectl graceful).
The Subdomain Files
Now we're going to need to create the actual files to load the subdomain. Go create a "dev/" subdirectory in /home/modxcms.com/public_html/ (orwhatever base path your webroot is in).
You'll need to copy 3 files from your MODx Revolution's root directory:
index.php.htaccessconfig.core.php
Copy those to the dev/ directory.
Now, you'll need to edit a couple of them.
index.php
Edit index.php, and find this line (near the end):
$modx->initialize('web');
Change 'web' to 'dev'. Save the file and close.
.htaccess
You'll only need to edit one line here (and maybe not at all). Find this line (near the top):
RewriteBase /
Make sure that's set to /, not anything else. It should match the context setting you set up earlier.base_url
Final Steps
Clear your site cache again, refresh the Resource tree, and click 'Preview' on your "Dev Home" document. You should now be showing the pageat the following URL:
http://dev.modxcms.com/
Create a [[~135]] link to the "Dev Docs" Resource in the "Dev Home" Resource. Reload your page. Note the link properly builds to:
http://dev.modxcms.com/documentation.html
And you're done!
See Also
Contexts
Using One Gateway Plugin to Manage Multiple Domains
You have a choice when sharing a single database and manager across multiple domains. You can choose to use the primary front-end context(known as 'web') to handle all domains or you can create a unique gateway file for each domain to directly initialize a specific context. If you use asingle gateway, you would use a plugin to switch contexts registered to the OnHandleRequest event, something like so:
<?php ($modx->getOption('http_host')) {switch
'domain2.tld:80':case 'domain2.tld':case // the http_host is of a specific domain, the contextif switch$modx->switchContext('domain2.tld'); ;break :default // by , don't anythingdefault do
;break}?>
Alternatively, you would simply copy the index.php file from the default web context (along with the core.config.php and .htaccess for rewrite rulesaltered appropriately) to another directory and change the line
$modx->initialize('web');
to
$modx->initialize('aContextNameOfYourChoice');
Note that you could also just copy the index.php in the same directory and rename it to do this, but your rewrite rules would have to be smartenough to route requests to the appropriate context gateway, and you would need to configure the request_controller option in Context Settingsappropriately.
You can also still use a custom core location in either of these scenarios; this is independent of the context-driven multi-site capabilities.
Customizing the Manager
What is Form Customization?What are Rules?What Forms can I Customize?ExamplesSee Also
What is Form Customization?
Form Customization is a new feature that allows users to create Rules, which govern how manager pages render their forms in the MODxRevolution Manager. It is similar to ManagerManager in MODx Evolution.
What are ?Rules
Rules in MODx Revolution are simply generic rules that apply to any (manager page). They also can be tied to User Groups, wherein aActionRule will only be applied if the User is part of that User Group.
A rule can be one of the following:
What Forms can I Customize?
Technically any manager page can be customized. Form Customization bases its methods off of , which are database representations ofActionsany MODx Revolution Manager page. This also means that can also have Rules applied to them, should the CMPs beCustom Manager Pagesusing .MODExt
A full list of documented customizable forms, along with their fields and other info, can be found here:
Examples
An example set of Rules:
will make the Resource Update page look like so:
and TV part look like:
See Also
Form Customization Pages
Documented Form Customization Pages
FC-Resource
Resource Create/UpdateAvailable FieldsAvailable TabsHiding the Content FieldTVs
This page is under construction.
Resource Create/Update
These pages encompass the following Actions:
resource/updateresource/create
Available Fields
Field Name Containing Panel
Page Title pagetitle modx-panel-resource
Published published modx-panel-resource
Long Title longtitle modx-panel-resource
Description description modx-panel-resource
Introtext introtext modx-panel-resource
Link Attributes link_attributes modx-panel-resource
Alias alias modx-panel-resource
Menu Title menutitle modx-panel-resource
Menu Index menuindex modx-panel-resource
Hide from Menus hidemenu modx-panel-resource
Container isfolder modx-panel-resource
Rich Text richtext modx-panel-resource
Published On publishedon modx-panel-resource
Publish Date pub_date modx-panel-resource
Un-Publish Date unpub_date modx-panel-resource
Searchable searchable modx-panel-resource
Cacheable cacheable modx-panel-resource
Deleted deleted modx-panel-resource
Empty Cache syncsite modx-panel-resource
Content Type content_type modx-panel-resource
Content Disposition content_dispo modx-panel-resource
Class Key class_key modx-panel-resource
Available Tabs
These tabs are available for renaming/hiding:
Tab Name (ID) Containing TabPanel
Create/edit Resource modx-resource-settings modx-resource-tabs
Page Settings modx-page-settings modx-resource-tabs
Template Variables modx-panel-resource-tv modx-resource-tabs
Access Permissions modx-resource-access-permissions modx-resource-tabs
Hiding the Content Field
Use these settings:
Field: modx-resource-content: modx-panel-resourceContaining Panel
: Field VisibleRule: 0Value
TVs
Affecting TVs for a Resource is fairly straightforward - just set the "Name" attribute of the Rule to "tv#", and replace # with the ID of the TV you'dlike to affect. You can leave the Containing Panel blank.
FC-Template
This page is under construction.
FC-Chunk
This page is under construction.
FC-Snippet
This page is under construction.
FC-Plugin
This page is under construction.
Form Customization Rules
What is a Rule?What are the Rules available?Creating a RuleConstraintsSee Also
What is a Rule?
Rules in MODx Revolution are simply generic rules that apply to any Action (manager page). They also can be tied to User Groups, wherein aRule will only be applied if the User is part of that User Group.
What are the Rules available?
The available Rules are:
Creating a Rule
First off, go to the Form Customization page, under the Security main menu. From there, click "Add" in the grid's toolbar. You'll be presented witha window:
Let's go into the fields.
Field Description Optional
Action This field lets you select any available . The rule will only apply to that Action, and no other.Action No
User Group Here you can optionally choose a to tie this Rule to. If a User Group is chosen, the Rule will only applyUser Groupto of that User Group.Users
Yes
Description An optional, for you, description of the Rule. Yes
Field The name of the field or tab to apply the Rule to. No
ContainingPanel
The name of the containing panel that the Field or Tab exists in. Most all Rules will need this value. No
Value The value you'd like to set the Rule to. The type of value can vary depending on the Rule. No
ConstraintClass
If set, will restrict the Rule to a field value (via Constraint Field and Constraint) on this class. Example: modResourcefor the Resource Update screen.
Yes
ConstraintField
If set, will restrict the Rule to the field specified, with the value of Constraint Yes
Constraint If set, will restrict the Rule to the value of the field in the object specified in the constraint options. Yes
Constraints
Rule Constraints are special abilities that let you restrict the Rule to a certain value on an object's field. Let's say you want to restrict a Rule forresource/update to only occur on Resource's with a Template ID of 4. You'd simply set the Constraints like so:
Constraint Class: modResourceConstraint Field: templateConstraint: 4
Or, further, if you wanted to restrict the Rule to Resources with a menutitle of "boo":
Constraint Class: modResourceConstraint Field: menutitleConstraint: boo
Constraints allow a superior level of flexibity to .Form Customization Rules
See Also
Field Default
The Field Default Rule
The Field Default Rule, when set, will automatically set the default value of a field.
This will only work on "create" pages, "update" pages.not
Examples
An example Rule of setting the default Category for a for all in the User Group HR Department would look like this:Snippet Users
See Also
The root page Form Customization could not be found in space MODx Revolution 2.0.
Field Label
The Field Label Rule
The Field Label Rule, when set, will change a Label of a field to any text value.
Examples
An example Rule of changing the introtext's label to "Comments" for the in the "Marketing" would look like this:Users User Group
See Also
The root page Form Customization could not be found in space MODx Revolution 2.0.
Field Visible
The Field Visible Rule
The Field Visible Rule, when set, will declare a field "visible" or not to a user.
Examples
An example Rule of hiding the introtext field from the in the "HR Department" would look like this:Users User Group
See Also
The root page Form Customization could not be found in space MODx Revolution 2.0.
Move TV to Tab
The Move TV to Tab Rule
The Move TV to Tab rule will move any TV to the tab you specify.
Usage
Specify the ID of the tab to move to in the "name" field of the Rule. Then specify the TV's ID prefixed with 'tv' in the "value" field.
The list of available tabs is:
ID Description
modx-resource-settings The first tab, or Create/Edit resource tab.
modx-page-settings The second tab, or Page Settings tab.
You can also create a tab with the rule and move a TV there.New Tab
Examples
An example rule for moving the TV 1 to the first tab in the Resource create page would look like so:
See Also
New Tab
The New Tab Rule
The New Tab Rule, when set, will create a new tab in the panel.
Usage
Specify the ID of this new tab in the "Name" field.Specify the tabpanel ID in the "Containing Panel" fieldSet the Rule to "New Tab"Set the title of the new tab in the "Value" field
Available TabPanels
Here are the IDs for the corresponding tab panels on various manager pages.
ID Corresponding Page
modx-resource-tabs The Resource edit/create page.
modx-chunk-tabs The Chunk page.
modx-snippet-tabs The Snippet page.
modx-plugin-tabs The Plugin page.
modx-template-tabs The Template page.
modx-tv-tabs The Template Variable page.
modx-user-tabs The User page.
modx-usergroup-tabs The User Group page.
modx-context-tabs The Context page.
Tab Panels on non-Resource pages are only available in 2.0.0-pl and up.
Examples
An example rule for creating a new tab in the Resource edit page would look like so:
See Also
The root page Form Customization could not be found in space MODx Revolution 2.0.
Tab Title
The Tab Title Rule
The Tab Title Rule will change the title of any Tab.
Usage
The values for the Rule should be as follows:
Name: The ID of the tab to change.Containing Panel: The ID of the tabpanel to change.Rule: Tab TitleValue: The new text to change the tab title to.
For a list of tabs available for each page, see .Form Customization Pages
Examples
An example Rule of changing the title for the Page Settings tab for all to "Custom Fields" would look like this:Users
See Also
The root page Form Customization could not be found in space MODx Revolution 2.0.
Tab Visible
The Tab Visible Rule
The Tab Visible Rule, if set to 0, will hide a tab from a User.
Examples
An example Rule of hiding the Page Settings tab for all would look like this:Users
See Also
The root page Form Customization could not be found in space MODx Revolution 2.0.
TV Default
The TV Default Value Rule
The TV Default Value Rule, if set, will set the default value for a TV.
This only works on "create" Actions, "update" Actions.not
Usage
The values for the Rule should be as follows:
Name: The ID of the TV prefixed with 'tv'; for example, for the TV with ID 23, 'tv23'Containing Panel: 'modx-panel-resource'Rule: 'TV Default Value'Value: The value you want to set as the default.
Examples
An example Rule of setting the default value for a TV with ID 23 to "test" is:
See Also
The root page Form Customization could not be found in space MODx Revolution 2.0.
TV Title
The TV Title Rule
The TV Title Rule will change the title of a TV.
Examples
An example Rule of setting the label to "Boo!" for a TV with ID of 1 for all would look like this:Users
See Also
The root page Form Customization could not be found in space MODx Revolution 2.0.
TV Visible
The TV Visible Rule
The TV Visible Rule, if set to 0, will hide a TV from a User.
Examples
An example Rule of hiding the a TV with ID of 1 for all would look like this:Users
See Also
The root page Form Customization could not be found in space MODx Revolution 2.0.
Security
Security in MODx Revolution
Security in MODx Revolution is primarily driven by an Attribute-Based Access Control (ABAC) paradigm.
Each user in MODx has a object, which can be assigned to any number of . Each User Group then has attributes assigned to itUser User Groupsvia (ACLs). These ACLs take a variety of names depending on how they are applied, but all share one common principle -Access Control Liststhey contain a list of . These Permissions allow access to different areas or actions within MODx.Permissions
ACLs usually have the following:
Principal - The object that is getting the access permissions. This is in MODx, by default, a User Group.Target - The object they apply to, for example, a Context or Resource Group.Access Policy - The list of Permissions that is gained by this ACL.Authority - The minimum Authority level required to use this ACL (see ).Roles
Access is in MODx, meaning that access is "open" by default. Once an ACL is applied to an object, such as aallow/denyContext or Resource Group, those Contexts or Resource Groups will now only be accessible to the objects with appropriatePermissions.
Security Tutorial Video
Demonstrates by applying concepts to the MODx Sample Site to:
Restrict RSS feed to Directors and upRestrict Blog to Staff onlyCreate a 'secure' context for Directors and up onlyRestrict some element categories to administrators only
Example: Context Access
A good example is creating a Context named 'test', and assigning an ACL to it. This can be done by editing the Context, and going to the 'AccessPermissions' tab. From here, you can give a User Group (say, 'HR Dept') explicit access to this Context by selecting the User Group, the
'Administrator' Access Policy, and specifying a required Authority (say, 9999 for 'Member') to have:
This will restrict the 'test' Context to users who are a Member (or with more authority) of the 'HR Dept' User Group.Role
See Also
Users
What is a User?User SettingsUsers in the Front-EndUser FieldsGrabbing the User via the APISee Also
What is a User?
A User is simply a representation of a login in MODx Revolution.
Users can also be assigned to User Groups, which can have attached to them to provide Access Controls.ACLs
User Settings
User Settings in MODx Revolution will automatically override for that user any System or Context settings with the same key. They can also becompletely unique settings as well. The order of inheritance for Settings is:
System Settings -> Context Settings -> User Settings
Users in the Front-End
When a user is logged into the frontend of your site, their username and ID can be accessed by the following :Properties
[[+modx.user.id]] - Prints the ID[[+modx.user.username]] - Prints the username
If a user is not logged in, ID will be blank, and Username will be "(anonymous)".
User Fields
Users contain the following fields:
Name Description
id The ID of the user.
username The username of the user.
password The user's encrypted password.
active Either 1 or 0. If not active, the user will not be able to log in.
remote_key A remote user Key used by remote authentication apps.
remote_data A JSON array of data used by remote authentication apps.
Users also have a Profile attached to them. It contains the following fields:
Name Description
internalKey The ID of the user.
fullname The full name of the user.
email The email of the user.
phone The phone number.
mobilephone The cellphone number.
fax The fax number.
blocked Either 1 or 0. If blocked is set to true, the user will not be able to log in.
blockeduntil A timestamp that, when set, will prevent the user from logging in until this date.
blockedafter A timestamp that, when set, will prevent the user from logging in after this date.
logincount The number of logins for this user.
lastlogin The last time the user logged in.
thislogin The time the user logged in in their current session.
failedlogincount The number of times the user has failed to log in since last logging in.
sessionid The User's session ID that maps to the session table.
dob The date of birth.
gender 0 for neither, 1 for male and 2 for female.
address The physical address.
country The country of the user.
city The city of the user.
zip The zip or postal code for the user.
state The physical state or province of the user.
photo An optional field for a photo. Not used in the UI.
comment An optional comment field for comments on the User.
website The website of the user.
extended A JSON array that can be used to store extra fields for the User.
Grabbing the User via the API
The current user can be gotten in the API via the $modx->user reference. For example, this snippet outputs the username of the user:
return $modx->user->get('username');
Note that to grab Profile fields, you'll need to first get the modUserProfile object via the Profile alias. For example, this snippet grabs the email ofthe user and returns it:
$profile = $modx->user->getOne('Profile'); $profile ? $profile->get('email') : '';return
If the User is not logged in, $modx->user will still be available as an object, but will return 0 as the ID and (Anonymous) as the username.
See Also
User Groups
What is a User Group?
A User Group is simply a collection of Users.
Usage
Go to Security -> Access Controls. From there you will see a tree of User Groups and their respective Users. You can assign a User to a UserGroup by right-clicking on the User Group and either:
Adding the User via the context menu itemEditing the User Group and adding a User on the grid there
Assigning Policies
A quick clarification on which policies to use:
Policies assigned on the Context Access tab should be based on the standard Administrator policy.Policies assigned on the Resource Group Access tab should be based on the standard Resource policy.Policies assigned on the Element Category Access tab should be based on the standard Element policy.
Roles in User Groups
Users can have specific Roles within a User Group, should you choose. They can also exist in the User Group without a Role. Roles allow you tofine-tune your permissions more than in previous MODx versions.
Say you want to only allow Supervisors in the "HR Department" User Group access to some Resources; no problem. Just create a Role called"Supervisor", set its authority to some number below 9999 (let's say 3), and then add in the Users to the User Group "HR Department" (via theUser Group editing screen), setting any would-be supervisors to the Supervisor Role.
Then, you'll just add a Resource Policy (the packaged-in-with-modx one will do fine) to the Resource Group you want to restrict access to. It willlook something like this:
And you've got a role-based access permission! This specific ACL will limit all Resources (aka Documents) in the web context and in the resourcegroup "TestResourceGroup4" to only Users in the "HR Department" User Group with at least a Role of Supervisor. Roles with lower authoritynumbers would also inherit access - say you had a Coordinator Role with an authority of 2; Users in this User Group would have access to thisACL as well.
See Also
Resource Groups
What is a Resource Group?
A Resource Group is a collection of Resources, e.g. "Members Only" pages.
Usage
You can add or delete members of a Resource Group in two different ways:
Option 1
Go to Security -> Resource Groups. From there you will see a tree of Resource Groups and a tree of Resources.
then drag the Resources from the right tree directly into Resource Groups in the left tree:
Option 2
When you are editing a Resource directly, click on its "Access Permissions" tab, and check which Resource Groups it is a member of.
See Also
Roles
What is a Role?
A role is a position or status held within a certain situation. In MODx, it can be used to group Users into a position or status within a User Group,e.g. "Editor" or "Front-end Read Only".
Roles in MODx use an integer value called "Authority". E.g. a Role with Authority 10 will inheritLower numbers represent a stronger authority.any and all Group Policies assigned to itself and to any roles defined with Authority 11, but a user Role with Authority 11 does NOT inherit any ofthe Group Policies from Role 10.
Be sure you clarify your language when talking about Authority because this inverse relationship can lead to some confusing sentences.
It helps to think of "Authority" as ordinal numbers: first, second, third, etc. is the first authority and trumps Authority=1 (i.e. the second authority).Authority=2
You should generally avoid duplicate authority numbers.
Usage
One common example is to create Roles that mimic a basic employee position structure. Let's say we create the following Roles and Authoritylevels:
Administrator - 0Director - 1Coordinator - 2Supervisor - 3Employee - 9999
We can then create a User Group called "HR Department". Within that User Group, we'll assign Users to those Roles (you can have multipleUsers per Role, as well).
Now, let's say John has a Role of Coordinator. Mark has a Role of Supervisor. We're going to give Mark's "HR Deparment" User Group an AccessPolicy (which is a set of Permissions) called "AccountPolicy" that has the following Access Permissions in it:
view_accountssave_accounts
We've assigned this Policy to the "web" context for our User Group "HR Department". We then set its Minimum Role value to "Supervisor":
This means that Mark has these Permissions, since he's in the User Group, and has at least the Role of "Supervisor" (which is the Role he has,specifically).
But this means that John has these Permissions as well, since he is a "Coordinator" which has a stronger Authority level than "Supervisor".alsoSo, John as Coordinator has "inherited" the Permissions than Mark had as Supervisor.
See Also
Policies
What is an Access Policy?
An Access Policy is a set of containing one or many Permissions, as defined in the manager.Permissions
Creating and Editing
To create an Access Policy in the manager, navigate to the "Security" menu and select "Access Policies". From there you can add new policies.To edit an Access Policy in the manager, simply right-click the Policy you want to edit.
Usage
Policies can be used in a myriad of different ways. Here are 3 example usages that come by default in MODx:
Context Access
Access Policies can be assigned as (ACLs) to a Context and User Group, with a specified Minimum . When done, thisAccess Control Lists Rolemeans that all the Users in that User Group with at least the Role specified as the Minimum Role can use the Permissions in the Policy in theContext specified in the .ACL
MODx comes with a default that contains all the one would use in a Context ACL. It's best to duplicate this"Administrator" Policy Permissionspolicy when creating a custom access policy for restricting manager users.
Resource Group Access
They can also be Resource ACLs, that limit access to Resources based on Roles and Resource Groups. MODx comes packaged with a default that contains all the basic Permissions one would use in a Resource Group ACL."Resource" Policy
An example would be to assign the "Resource" policy to a Resource Group called 'HR Documents'. Then, you would give a User Group called"HR Department" access to this Resource Group via the Resource ACL:
This would restrict all Resources in the "HR Documents" Resource Group to Users only in the "HR Department" group.
Element Category Access
Elements can be restricted from view by ACLs on Categories. For example, if you had a User Group called 'Developers', and wanted Users in thatgroup to be the only Group that could see Elements in the Category 'Gallery', you would create an ACL like such, in the "Element CategoryAccess" tab when editing the User Group:
This would allow only Users in the "Developers" User Group access to see Elements in the "Gallery" Category.
Examples
Here's an example custom policy:
and its permissions:
Any User that had access to this Policy would have the permissions 'view_accounts' and 'save_accounts'.
See Also
Permissions
What is a Permission?
A Permission in Revolution is simply a single access control that allows you to deny or allow access to a task. They are collected in Access into a list, which is called an Access Control List (or ACL). From there, the defines all the Permissions that the list contains.Policies Access Policy
An example Permission is "content_types" - if the Policy does not contain this Permission, then users with that Policy will not be able to view theContent Types page.
Usage
Permissions are assigned in Policies in access control lists. To do so, simply edit any available Access Policy, and from there you can view andmanage existing Permissions, as well as create new ones.
See Also
Permissions - Administrator Policy
The Administrator Policy
This policy is packaged into MODx and is given to users on the 'mgr' context who want to have full access to managing MODx content.
Default Permissions
Name Description of Access
about The About page.
access_permissions Any Access Permission-related pages and actions.
action_ok
actions The page.Actions
change_password User can change their user password.
change_profile User can change their profile.
content_types The page.Content Types
create Basic "create" access on objects.
credits View the Credits page.
customize_forms View and manage the page.Customizing the Manager
database The System Info page
database_truncate The ability to truncate a database table.
delete_category To delete or remove any Categories.
delete_chunk To delete or remove any .Chunks
delete_context To delete or remove any .Contexts
delete_document To delete or remove any .Resources
delete_eventlog To empty the Event Log.
delete_plugin To delete or remove any .Plugins
delete_snippet To delete or remove any .Snippets
delete_template To delete or remove any .Templates
delete_tv To delete or remove any .Template Variables
delete_role To delete or remove any .Roles
delete_user To delete or remove any .Users
edit_category To edit any Categories.
edit_chunk To edit any .Chunks
edit_context To edit any .Contexts
edit_document To edit any .Resources
edit_locked Allows a user to override a lock and edit a locked Resource.
edit_parser
edit_plugin To edit any .Plugins
edit_role To edit any .Roles
edit_snippet To edit any .Snippets
edit_template To edit any .Templates
edit_tv To edit any .Template Variables
edit_user To edit any .User
element_tree The ability to view the Elements Tree on the left nav.
empty_cache To empty the site cache.
export_static To export the site to static HTML.
file_manager To use the file manager, including creating/deleting files.
file_tree To view the Files Tree on the left nav.
flush_sessions Can flush Sessions across the site.
frames To use the MODx Manager UI at all.
help To view the Help page.
home To view the Welcome page.
import_static To view or use the Import pages.
languages To edit or view Lexicon Languages.
lexicons To edit or view Lexicons and .Internationalization
list Basic permission to "list" any object. List means to get a collection of objects.
load Basic permission to "load" any object, or be able to return it as an instance at all.
logout To be able to logout as a user.
logs To view the logs, such as error and manager logs.
menus To edit or save any top Menu items.
messages To send or view any personal Messages.
namespaces To edit or view .Namespaces
new_category To create a new Category.
new_chunk To create a new .Chunk
new_context To create a new .Context
new_document To create a new .Resources
new_plugin To create a new .Plugin
new_role To create a new .Role
new_snippet To create a new .Snippet
new_template To create a new .Template
new_tv To create a new .Template Variable
new_user To create a new .User
packages To use any Transport Packages in the system.Package Management
property_sets To view and edit .Properties and Property Sets
providers To view and edit across the site.Providers
publish_document To publish or unpublish any Resource.
purge_deleted To empty the Recycle Bin.
remove Basic permission to remove any object.
remove_locks To remove all existing Locks throughout the site.
resource_tree To view the Resource Tree in the left nav.
save Basic save permission for any object.
save_category To save any Categories.
save_chunk To save any .Chunks
save_context To save any .Contexts
save_document To save any .Resources
save_plugin To save any .Plugins
save_role To save any .Roles
save_snippet To save any .Snippets
save_template To save any .Templates
save_tv To save any .Template Variables
save_user To save any .User
search To use the Search page.
settings To view and edit any System Settings.
steal_locks To "steal" locks, overriding a current lock on a document.
unlock_element_properties To be able to edit the default properties for any Element.
view Basic permission to "view" any object.
view_category To view any Categories.
view_chunk To view any .Chunks
view_context To view any .Contexts
view_document To view any .Resources
view_eventlog To view the Event Log.
view_offline
view_plugin To view any .Plugins
view_role To view any .Roles
view_snippet To view any .Snippets
view_template To view any .Templates
view_tv To view any .Template Variables
view_unpublished To view any unpublished .Resources
view_user To view any .User
workspaces To utilize .Package Management
See Also
Permissions - Resource Policy
The Resource Policy
This policy is packaged into MODx and is given to users on any context who want to have basic object access to content. The Permissions aregeneric and apply across all MODx objects.
Default Permissions
Name
add_children
create
delete
list
load
move
publish
remove
save
steal_lock
undelete
unpublish
view
See Also
ACLs
What is an ACL (Access Control List)?Usage
Context ACLResource ACL
See Also
What is an ACL (Access Control List)?
An ACL, or Access Control List, is a set of attached to an object. More information on ACLs can be found .Permissions here in Wikipedia
Usage
In MODx, ACLs can be applied to any modAccessibleObject. Primarily MODx Revolution 2.0 allows for ACLs on Resources and Contexts.
Context ACL
A Context ACL is referenced of 4 parts:
A ContextA User GroupA Minimum RoleAn Access Policy
This means that one can assign a ACL to a Context that will apply to:
All the Users in a User Group...with at least the Minimum Role specified...that will give the Users all the Permissions in the Access Policy assigned.
Resource ACL
Resource ACLs behave a bit differently, and basically allow you to restrict access to Resources (such as Documents, Weblinks, etc) by ResourceGroups. They are comprised of 5 Parts:
A Resource GroupA User GroupA Minimum RoleAn Access PolicyA Context
This means that an ACL applied to a Resource Group will:
Effect all the Users in the specified User Group... with at least the Minimum Role specified... give the Resource Permissions (save, load, delete, etc) in the Policy specified... to all the Resources in the Resource Group
See Also
Security Tutorials
Security Tutorials
Here are some tutorials designed to help you get started with Security in MODx Revolution.
Giving a User Manager Access
The Problem
You want a User to have manager editing access, but not have all the of an Administrator user. This tutorial, partially written byPermissionsBobRay, will help you through that.
1. 2. 3. 4. 5. 6.
a. b.
7.
1. 2. 3. 4.
5. 6. 7.
The Solution
First off, create your user. Then:
Create a new role (say, Editor) with an authority of say, 10.Add the User to the admin group with a role of EditorIn Access Controls | Policies, duplicate the administrator policy and rename it to whatever you want, say "AdminLite".Edit the AdminLite Policy to use whatever you want the User to have.PermissionsIn Access Controls | User Groups, right click on the admin group and select "Update Group."On the Context Access tab, add two new ACL entries to the grid:
Context: 'mgr' Minimum Role: 'Editor', Access Policy 'AdminLite'Context: 'web' Minimum Role: 'Editor', Access Policy 'AdminLite'
Click on Security -> Flush Sessions and re-login.
See Also
Making Member-Only Pages
IntroductionStep-by-step explanation
1. Create a Resource Group2. Link your member-only resource to the Resource Group3. Create a User Group4. Add Context access5. Add Resource access6. Add users to the user group7. Flush permissions
Help! I can't get this to work, still!See Also
Work in progressPlease note that this document is a work in progress and may still be inaccurate. It should, however, be possible to set up amember only page using this documentation.
Introduction
MODx Revolution uses a whole new set of security systems to allow you more flexibility while giving your users access (or denying) to Managerand Web resources. As there seems to be need for a proper tutorial to get you into the basics of working with this advanced system this documenthas been written.
For those that are savvy enough, below follows a simple list to help you through the maze or to remind you how it works. If you need moreinformation and would like some examples, scroll down to the related subsection below.
Create a Resource Group (Security -> Resource Groups -> Create Resource Group)Link your member-only resource to the Resource Group. (By editing the resource, or by dragging them from the resource tree to the right)Create a User Group (Security -> Access Controls -> User Groups -> New User Group)Edit the user group you created to add Context Access (Security -> Access Controls -> User Groups -> Right click your user group -> onthe Context Access tab add context "web", minimum role 'Member (9999)', access policy 'Resource' (if you want to modify permissions,duplicate that policy. Do not edit it directly!))Add a resource group entry on the Resource Group Access tab (context: web, minimum role: Member (9999), access policy: resource)Add users to the user group with the role of Member. (Security -> Manage Users)Flush permissions (Security -> Flush Permissions) and try it in another browser (not just another browser window: another browser)
Step-by-step explanation
Work in progressThis section is still being worked on and may be incomplete or incorrect.
If you're not quite as savvy, or would rather also know what happens when you set a certain permission or make an access entry, you might findthis section interesting.
1. Create a Resource Group
1. 2. 3. 4. 5.
A is a collection of resources which you can link to user groups and access list entries. When you have created a resource group,resource groupyou can easily classify pages to be only visible for certain user groups, or roles within user groups.
To create a resource group, navigate to Security -> Resource Groups and click on the Create Resource Group button. In the popup you areprompted to enter a name for the resource group. In the tutorial we expect you named it "Protected".
2. Link your member-only resource to the Resource Group
Now that you have a resource group, you should add resources to it. There are two ways to achive this.
First of all, you can go to Security -> Resource Groups and drag resources from the right resource tree to the left resource groups ("Protected").The second option is editing your resource, and ticking the right box on the "Access Permissions" tab.
It is important to realize that as soon as you have protected a resource by assigning it to one or more resource groups, those pages will no longershow up for users that are not linked to the resource group. The default behaviour in that case is displaying the 404-error page. If you wouldrather return the 403-error, you will need to give the anonymous user group "load" permission for the resource group. More about this in a latertutorial. At this moment in the tutorial, your page will not be visible as you have not yet added it to a user group.
3. Create a User Group
You have a resource group with resource applied to them, now you'll need to decide who will be able to view the resources. For this, we'll make anew user group.
To do this, go to Security -> Access Controls. On the (default) User Groups tab, click on the New User Group button. Choose a name for thegroup, and submit the form.
4. Add Context access
At this point the user group means nothing, it has zero access. To start with, we'll add context access to the user group. When a user group hasaccess to a context, it can view (unprotected) resources within that context.
While still being on the Access Controls page, viewing the User Group tab, right click the user group you just made and click on "Update UserGroup". You will find five different tabs:
General Information: this contains the name and if applicable the parent group.Users: shows an overview of users in the user group and offers you to add new users.Context Access: we will discuss this in a minute.Resource Access: we will discuss this in step five.Element Category Access: will not be covered in this tutorial, but can be used to limit access to elements.
Go to the Context Access tab again. At the moment it should still be empty, so the user has no access to any contexts. Click on "Add Context".
As we want our users to be able of accessing the "web" context (that means your resources in the Web context), select "web" from the contextdropdown. The Minimum role refers to the role the user should have within the user group to receive the access to the context. As we want all users, we'll setthis to Member (9999). Note that the number refers to the authority: a user at authority 20 will receive all access list entries that require a role withan authority of 20, 21, 22...9998, 9999. To make it applicable to all users in that group, choose an authority level of 9999.The Access Policy is key in this form. That determines what a user can actually do. As with everything MODx, this is highly flexible. Per defaultthere are several Access Policies added. In this case, we'll set it to Resource. This Access Policy includes all default permissions for resourceaccess. If you ever want to change some of the permissions (which may be covered in a different tutorial or article) always duplicate the accesspolicy and don't modify it directly, as there's no way to recover changes made.
So, to sum up, apply this context access:
Context: webMinimum role: Member (9999)Access Policy: Resource
Now, your user group has access to your Web context, as long as access to resources is not being blocked by resource groups.
5. Add Resource access
Move on to the Resource Access tab. This tab defines the resources your user has access to if they are protected by resource groups. Three outof four fields are similar to the Context Access groups, namely the Context, Minimum Role and Access Policy. A new one is Resource Groupwhich, as you probably guessed, defines the resource group the user group can access.
The settings:
Resource Group: whatever you named it, for example "Protected"Context: webMinimum role: Member (9999)Access Policy: Resource
6. Add users to the user group
Now add some users to the user group. You can do this by editing the user, or by going back to the Users tab and adding them from there. It willask for the User Group, as well as the Role. As we assumed the Member role with an authority of 9999, you can simply use that one.
When using a websignup snippet, make sure it automaticly puts them in the right user group.
7. Flush permissions
Now that all settings are done, you will need to flush permissions (Security -> Flush Permissions) before you will see an effect. Also make surethat, if you go to test it front-end, you use a different browser all together. Don't use a different tab or browser window, as it will still use yourManager login to check for permissions.
Please note that in some cases it is also neccesary to clear the site cache, specifically for the mgr (manager) context, as elements and resourcesmay cache their permissions.
Help! I can't get this to work, still!
Make sure you followed everything step by step and that you flushed permissions properly. If everything seems to be alright, check again andthen go to the Forums to ask for help. If you think the tutorial is misleading or inaccurate, please visit the forum topic (linked below) and post aboutwhat is incorrect so it can be fixed.
See Also
Bob's permissions guide: http://bobsguides.com/revolution-permissions.html
Forum topic discussing this tutorial: http://modxcms.com/forums/index.php/topic,51259.0.html
Security: http://svn.modxcms.com/docs/display/revolution/Security
Installing a Package
Installing a Package
This page will guide you through the process of installing a Package via .Package Management
Downloading packages through requires cURL or PHP Sockets. MODx will let you know if you don'tPackage Managementhave either of these. If you are still having problems with Package Management after confirming these are installed, see
.Troubleshooting Package Management
Go to System -> .Package Management
Then click the Download Extras link.
Browse the available packages, opening the folders to expose the individual packages. Click Download to download whichever packages you'dlike to download. You may download multiple packages at one time.
The package will be downloaded to the proper directories in your MODx installation. Now you can view your new package, and click Install tochoose to install it.
Providers
You can select the location from which to download packages, add a new location, or select packages on your local machine. Use the Add NewPackage link, to the left of the Download Extras link. By default, the modxcms.com/extras repository is available as a remote provider.
Manual Installation
If you prefer, you can manually copy the package into the core/packages directory. The package must be a transport.zip archive, such aswayfinder-2.1.1-beta1.transport.zip. Then, click on 'Add New Package' in the packages grid. From there, select the 'Scan Local' option. Thepackage will now be visible in the Packages list, and you can install it as usual, by right-clicking and selecting Install Package from the pop-upmenu.
See Also
Troubleshooting Package Management
Troubleshooting Package Management
This page is dedicated to problems with Package Management, specifically with downloading and installing packages.
Most issues can be resolved by making sure you have cURL installed, and that the core/packages/ directory is writable by PHP.
Upgrading MODx
Upgrading MODx Revolution 2.0
This document assumes you are upgrading from a standard install. For Git users, please see .Git Installation
Uploading the Files
For traditional distribution users, simply upload the files over the existing ones, and then run setup. For the advanced distribution, do the same,but you'll only need to do so for the core/ and setup/ directories.
Make sure that you don't overwrite core/config/config.inc.php, and that it's writable.
Beginning Setup
Simply follow the upgrade process, selecting whichever upgrade you want to perform (normal or database).
If you are upgrading using the distribution, make sure you have the "Core Package has been manually unpacked" and "Files in-place"Advancedcheckboxes unchecked, and that the core/, manager/ and connectors/ directories are writable.
If you get errors during setup, please read .Troubleshooting Installation
After Setup
Make sure to remove the setup/ directory via the last option after setup has completed, so that no one can run setup after you and possibly breakyour site.
Version-Specific Changes
For changes relating to specific versions, please see the following pages:
For Upgrades Coming From Prior to 2.0.0-rc2
Upgrades after 2.0.0-rc-2 should run smoothly without issues.
See Also
Upgrading from MODx Evolution
Automated upgrading from 0.9.6.*/Evolution is not yet available in MODx Revolution 2.0.0. A migration tool will be providedsometime after the release of MODx Revolution 2.0.0.
With that in mind, if you'd _still_ like to upgrade, you're free to do so. However, we . Oncestrongly recommend backing up your data firstyou've done so, simply run the upgrade mode in the setup/ program, and your database tables will be upgraded.
From there, a few things will happen. One, you'll probably notice most of your 3rd party scripts will be broken. You'll need to convert them to theRevolution core, as well as all of your tags to the new . Component developers will hopefully already be converting their scripts by thisTag Syntaxpoint, so you may be able to find Revolution-compatible scripts via , or on or in the .Package Management modxcms.com forums
Also, it's worth noting that there are no more "web users" or "manager users" - only Users. And the is vastly differentnew permissions schemethan in 0.9.6/Evolution.
Again, we don't recommend this, but if you're a *brave* soul, feel free to backup and try it.
Extras Changes from Evolution
Some Extras in Evolution have been discontinued or are no longer in active development. Below is a list of Evolution Extras and their Revolutionequivalents:
Evolution Revolution
Ditto getResources, , , getPage tagLister Archivist
Jot Quip
SiteMap GoogleSiteMap
MaxiGallery Gallery
eForm FormIt
Wayfinder Wayfinder
DocManager Batcher
AjaxSearch SimpleSearch
WebLogin Login
See Also
Bob's Guide to Upgrading to Revolution
Functional Changes from Evolution
Changes from MODx Evolution to MODx Revolution
Much has changed from MODx Evolution in the new Revolution release. This document will attempt to address some of the major ones.
Tag Syntax
Tags have changed their basic syntax. You can view the .Tag Syntax changes here
Parsing Order
In Evolution, pages were parsed via eval and done as a whole - in Revolution, we implemented "Source Order" parsing. This means tags areparsed in the order that they occur.
So what does that mean? Well, a few things:
Don't put Snippet calls that assign placeholders at the end of a Resource, or after the Resource. The placeholders will simply be blank,since the haven't executed yet.SnippetTags can now have tags within their properties. [[mySnippet? &tag=`test[call]`]] is now 100% a-okay._Using =,?,!,* is now OK in a Snippet property.
No More 5000-Document limit
Although this has been mostly remedied in later versions of Evolution, there is still a performance hit in those versions. This, caching-wise, hasbeen fixed in Revolution.
That said, if you're creating a site that has over 10,000 Resources, chances are you're not designing it right. Consider writing custom Snippetsthat pull from custom database tables instead for similar pages (such as inventories or e-commerce).
Security
The access permissions system has been completely rewritten into a new ABAC-based system. You can read more about it Security.
Error Page vs Unauthorized Page
This is a change from MODx Evolution. In Revolution, if a web page is protected in the front end so that only logged-in users can see it, the
default behavior is for anonymous users to be redirected to the Error (page not found) page rather than the Unauthorized page when they try toaccess the resource. In Revolution, if Users don't have the "load" permission for a resource, it's as if it doesn't exist — thus the "page not found"response. If you would like them to be sent to the Unauthorized page instead, you can do the following:
Create a new Access Policy called "Load" and add a single Permission: Load.Create a new Context Access ACL entry for the anonymous User Group with a Context of "web," a Role of "member" and an AccessPolicy of "Load."
(credit to )Bob's Guides
FURL Suffixes and Prefixes -> Content Types
The settings friendly_url_prefix and friendly_url_suffix are no longer applicable, as Revolution handles those now through .Content Types
Upgrading to Revolution 2.0.0-rc-2
Upgrading to Revolution 2.0.0-rc-2
There are a few changes that have occurred in 2.0.0 RC-2 that will only apply to developers. If you are not:
Writing translatable Extras for RevolutionWriting plugins for Revolution
then you do not have to read this document.
Lexicon Changes
First off, you might ask, "Why such a big change so late in the game?" Well, for one, we didnt realize the limitations of the RC1 lexicon system,and how it hampered Extras development and prevented us from having a stable multi-lingual distribution. What has been changed is:
Dropped entirely the modLexiconTopic and modLexiconLanguage tables.Changed the 'topic' field on modLexiconEntry to be a varchar of the topic name.Refactored the entire modLexiconEntry logic so that now DB records of modLexiconEntry are for overridden entries. Otherwise, theyonlyare cached from the lexicon topic files (the .inc.php files.)Redid the entire Lexicon Management section to now be a grid that only allows overriding of Entries. In other words, you can only editexisting entries, and when you edit them, they show up in , signifying they have been overridden.green
This means that the only Lexicon Entries stored in the database are overrides made by the user.
There are some real benefits to the new approach:
Much, much easier translation abilities.Much faster lexicon loading time, since its file and array based rather than DB and Object based.You can now successfully change any lexicon entry without harming your upgrade path.Cuts down on the size of the core.transport.zip and massively decreases build and setup times.Much easier development. Just put a 'lexicon/' directory in your root of your Namespace's path (like most current Extras do) and build it inthis format: 'lexicon/[language]/[topic].inc.php'. MODx will automatically parse that directory and browse it in Lexicon Management foryou. You no longer need to 'buildLexicon' in your Extra's build scripts. However, this means that all packages using lexicons will need
. All that needs to be changed is that they no longer need to call 'modPackageBuilder::buildLexicon' in theirto be rebuilt for 2.0.0 RC-2build scripts, and their lexicon directories must be under the namespace path with the directory name 'lexicon' (similar to ).this componentWe apologize for the inconvience, but we promise that you'll find the change much, much easier to develop in.
This also means that we will be packaging in core translations into SVN. All core translations will be committed there, similar to Evolution.
Plugin Changes
Deprecated Plugin Events have been removed in RC-2, and a few new events have been introduced. Please view the in-progress documentationon these events on the page, or view an exhaustive list via the .System Events code here
Note that some of these events are model-centric. This means they are executed from within the mod* classes. These are usually:
On*SaveOn*BeforeSaveOn*RemoveOn*BeforeRemove
This means they will fire regardless of where they are executed. This allows you to fire events even when 3rd Party Components modify thoseobjects, such as when a 3PC creates a user. Please see the documentation on each respective event for more information.
See Also
Moving Your Site to a New Server
MODx Revolution is a database-driven web application, so moving it to a new server involves the typical porting over of both the database and allthe site's files. Those of you familiar with MODx Evolution may be in the habit of zipping up the files, grabbing a dump of the database, and thendeploying them on the new server... but in Revolution, you may have discovered that maybe something broke in the process... maybe themanager only comes up as a white page. It's more or less the same process, but there are couple extra tidbits to watch out for.
So here's the official documentation of how to move your site to a new location. (Normally, this is to a new server, but the steps here also apply ifyou move your site to a new folder on your current web server).
Packaging up your Files
Any time you pack up a site and move, it's best to package the files into "boxes" – when you move out of your apartment, you put everything intoboxes; the same concept is true with files: package them well. If you simply drag and drop files from one server to another using a GUI interface,chances are good that the GUI will omit hidden files such as the vital file. Besides, transferring hundreds of files via FTP can take a long.htaccesstime because with many servers, each file must undergo some sort of authentication; in other words copying over a hundred 1 megabyte filestakes a lot longer than copying over a single 100 megabyte file.
On a UNIX style system, you can create a compressed file using the tar command:
tar -czf /path/to/backups/modx_revo_site.tar.gz /path/to/modx_doc_root/
Forget me NotA good mnemonic for the "-czf" option is reate ip ile.C Z F
Once you arrive on the other end, it's good to put the zipped file into its own directory before you extract it. The idea here is that if it explodes andyou have to scrape the files off the walls, it's easier to clean up a mess if it's contained in its own directory.
On a UNIX style system, you can unpackage a .tar.gz file using the following commands from a command line:
gunzip modx_revo_site.tar.gztar xvf modx_revo_site.tar
Once you've extracted the files, you can move the whole directory into the correct place. Again, be careful about moving files in bulk: you mightinadvertently forget to copy those hidden files. It's better to rename or move the containing directory instead.
Dumping your Database
In the future, MODx will be able to run on other databases, but for now it runs on MySQL. You can dump your MySQL database using a GUI toolsuch as phpMyAdmin, or you can run the command-line utility.mysqldump
mysqldump -u username -p your_revo_db > /path/to/backups/my_revo_db.sql
If you use , be sure you use a username that has SELECT and LOCK permissions on all your MODx Revolution database tables –mysqldumpusually it's best to simply use the same username and password that are defined in your configuration file ( )./core/config/config.inc.phpRemember that will prompt you for the password after you execute this command: when you type it (or paste it), you won't seemysqldumpanything in the terminal window.
On the new server, you can simply use the "mysql" command to slurp the dump file into the new target database:
mysql -u username -p target_db < my_revo_db.sql
You can also use phpMyAdmin, but remember that web-based tools like this are subject to the same memory limits as PHP, so you're usuallybetter off using a command-line tool if possible.
Updating your Config File
Once you've deployed files to the new server, you need to update the main configuration file: . You have to update pathscore/config/config.inc.phpto different resources. Open the file and update the values for the following variables doing a find and replace:6
/* PATHS */$modx_core_path= '/path/to/modx_doc_root/core/';$modx_processors_path= '/path/to/modx_doc_root/core/model/modx/processors/';$modx_connectors_path= '/path/to/modx_doc_root/connectors/';$modx_manager_path= '/path/to/modx_doc_root/manager/';$modx_base_path= '/path/to/modx_doc_root/';$modx_assets_path= '/path/to/modx_doc_root/assets/';
/* HOST (used command-line PHP stuff) */for$http_host='yoursite.com';
PermissionsBefore you can edit your config file, you may need to loosen up the permissions. After you've edited it, be sure you restore theread-only permissions on the file.
Update your Database
Sometimes developers structure their development and production servers to use the same path information, but for most of us, the file pathexactinformation will change when we move our MODx web site to a new server. Take a look inside your Revolution database and see for yourself thatpath information is stored there. Type the following query into phpMyAdmin, a MySQL command line, or any other application that allows you toexecute queries on your database:
SELECT `path` FROM `your_revo_db`.`workspaces`;
Change "your_revo_db" to your database name, and add an appropriate prefix to the "workspaces" table if necessary.
On the new server you need to update this record. You can edit it using a GUI editor (like SQL-Yog or phpMyAdmin), or you can execute thefollowing command (again, you need to customize the query depending on your database, prefix, and the path to your data):
UPDATE `your_revo_db`.`workspaces` SET path='/path/to/modx_doc_root/core/' WHERE id='1';
Don't Forget the DatabaseMODx stores some path data in its database! When you move servers, you may have to update the workspaces table,otherwise the manager page may show a white page.
Update .htaccess
When you change servers, you frequently wind up changing domain names. Make sure you update any references to the old domain to the newone.
Log into the Manager: Clear your Cache and Sessions
By now you should be able to log into the manager, but when you first login you'll see an error message:
Could not find action file at: /path/to/manager/controllers/ /welcome.phpdefault
That's because the old path is still cached; MODx Revolution caches a lot of information in the database. Once you've gotten the files and
database transferred over to the new server, make sure you clear your site's cache of any lingering data that may contain the old file paths.
Log into the manager, then: Site --> Clear Cache
Finally, clear your sessions: Security --> Flush All Sessions
Your site should now be up and running in its new location!
Developing in MODx
This section contains information on starting development in MODx.
Code Standards
Code StandardsGeneral Practices
Indentation and Line BreaksTrailing SpacesCompression
HTMLValidationInline HTML in SnippetsSelf-closing ElementsTerseness
DoctypeTags and AttributesQuotes
CSSInline StylesCSS ValidationCSS FormattingPixels vs. EmsInternet Explorer BugsShorthand
Margin & PaddingHex ColorsBackgroundBorderFontLonghand
JavascriptType CoercionWhite-spaceVariables, ID & ClassQuotesEvent ListenersEvent DelegationClosures & ScopeObjects & Arrays
PHPGeneralParenthesisClassesVariablesFunction Arguments and Class VariablesArraysConstantsFile StructurePrefixing
SQL
Code Standards
This page describes the MODx coding standards for any MODx Component, Extension or core code. These are not de-facto rules, but guidelinesfor easier development and collaboration between developers.
This page was heavily borrowed from . Thanks!Fellowship One's Design Standards
General Practices
Indentation and Line Breaks
All indentation must be done with , not tabs. Make sure to change your editor settings to reflect this. Line breaks must be in format.4 spaces UNIX
Trailing Spaces
MODx recommends removing any trailing spaces in a line in code, unless that spacing is for design purposes.
Compression
MODx suggests packaging both compressed and uncompressed JS/CSS. MODx recommends using the compressed JS/CSS in productionenvironments, but allowing users the option to toggle between compressed and uncompressed JS/CSS. This allows for easier debugging.
MODx does not advocate PHP compression.
HTML
HTML5 is a new version of HTML and XHTML. The HTML5 draft specification defines a single language that can be written in HTML and XML. Itattempts to solve issues found in previous iterations of HTML and addresses the needs of Web Applications, an area previously not adequatelycovered by HTML. ( )from html5.org
MODx recommends following the HTML5 specs: http://whatwg.org/specs/web-apps/current-work/
Validation
All HTML must be HTML5-validated. MODx recommends using the .W3C Validator
Inline HTML in Snippets
MODx requires that be echo'ed or inline in a Snippet. MODx also recommends externalizing any HTML in PHP code into .no html Chunks
All HTML pages should be verified against the W3C validator, to ensure that the markup is well formed. This in and of itself is not directlyindicative of good code, but it helps to weed out problems that are able to be tested via automation. It is no substitute for manual code review.
Note: In TextMate, Control + Shift + V will check the validity of the currently open HTML document.
Self-closing Elements
Though we are using HTML5, which allows for either HTML or XHTML style syntax, we prefer the strictness of XHTML. Therefore, all tags mustbe properly closed. For tags that can wrap nodes such as text or other elements, termination is a trivial enough task. For tags that are self-closing,the forward slash should have exactly one space preceding it <br /> vs. the compact but incorrect <br/>. The W3C specifies that a single spaceshould precede the self-closing slash (source).
Terseness
Doctype
A nice aspect of HTML5 is that it streamlines the amount of code that is required. Meaningless attributes have been dropped, and the DOCTYPEdeclaration has been simplified significantly. Additionally, there is no need to use CDATA to escape inline JavaScript, formerly a requirement tomeet XML strictness in XHTML.
"HTML5 Doctype"
<!DOCTYPE html>
"XHTML 1.0 Transitional Doctype"
<!DOCTYPE html PUBLIC "- //W3C//DTD XHTML 1.0 Transitional//EN">"http://w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"
Tags and Attributes
All tags and attributes must be written in lowercase. Additionally, we prefer that any attribute values also be lowercase, when the purpose of thetext therein is only to be interpreted by machines. For instances in which the data needs to be human readable, proper title capitalization shouldbe followed, such as:
"For machines"
<meta http-equiv= content= />"content-type" "text/html; charset=utf-8"
"For humans"
<a href="http: title= >Example.com</a>//example.com/" "Description Goes Here"
Quotes
In keeping with the strictness of XHTML code conventions, according to the W3C, all attributes must have a value, and must use double-quotes(source). The following are examples of proper and improper usage of quotes and attribute/value pairs.
"Correct"
<input type= name= disabled= />"text" "email" "disabled"
"Incorrect"
<input type=text name=email disabled>
CSS
Inline Styles
We strive to maintain proper separation of content and design, and therefore highly discourage the use of inline style="..." attributes. This not onlymakes maintenance a nightmare, but inextricably ties the presentation to the data it represents. All of our CSS will be stored in external files, withone master.css file called per page. That single file will incorporate other files as necessary with the @import syntax.
Note: An exception to this rule is style="display:none" for revealing hidden elements via JavaScript.
CSS Validation
All cascading stylesheets should be verified against the W3C validator, to ensure correct syntax and to check for possible accessibility issues withtext and background colors. This in and of itself is not directly indicative of good code, but it helps to weed out problems that are able to be testedvia automation. It is no substitute for manual code review.
Note: In TextMate, Control + Shift + V will check the validity of the currently open CSS document.
CSS Formatting
To ease potential headaches for maintenance, we require that all CSS be written in a consistent manner. For one, all CSS selectors must belisted on their own line. As a general rule of thumb, if there is a comma in CSS, it should immediately be followed by a line break. This way, weknow that all text on a single line is part of the same selector. Likewise, all property/value pairs must be on their own line, with one tab ofindentation. The closing brace must be on the same level of indentation as the selector that began it - flush left.
"Correct"
#selector_1 span,#selector_2 span,#selector_3 span { background: #fff; color: #000;}
"Incorrect"
#selector_1 span, #selector_2 span, #selector_3 span { background: #fff; color: #000;}
"Also incorrect"
#selector { background: #fff; color: #000; }
Pixels vs. Ems
We use the px unit of measurement to define font size, because it offers absolute control over text. We realize that using the em unit for fontsizing used to be popular, to accommodate for Internet Explorer 6 not resizing pixel based text. However, all major browsers (including IE7 andIE8) now support text resizing of pixel units and/or full-page zooming. Since IE6 is largely considered deprecated, pixels sizing is preferred.Additionally, unit-less line-height is preferred because it does not inherit a percentage value of its parent element, but instead is based on amultiplier of the font-size.
"Correct"
/*13 * 1.5 = 19.5 ~ Rounds to 20px.*/#selector { font-size: 13px; line-height: 1.5;}
"Incorrect"
/*Equivalent to 13px font-size and 20px line-height,but only the browser text size is 16px.if default*/#selector { font-size: 0.813em; line-height: 1.25em;}
Internet Explorer Bugs
Inevitably, when all other browsers appear to be working correctly, any and all versions of Internet Explorer will introduce a few nonsensical bugs,delaying time to deployment. While we encourage troubleshooting and building code that will work in all browsers without special modifications,sometimes it is necessary to use conditional if IE comments to serve up specific fixes, which are ignored by other browsers.
"Fixing IE"
<!--[ IE 7]>if<link type= rel= href= />"text/css" "stylesheet" "/assets/styleshseets/ie7.css"<![endif]--><!--[ IE 8]>if<link type= rel= href= />"text/css" "stylesheet" "/assets/styleshseets/ie8.css"<![endif]-->
Shorthand
In general, CSS shorthand is preferred because of its terseness, and the ability to later go back and add in values that are already present, suchas the case with margin and padding. Developers should be aware of the TRBL acronym, denoting the order in which the sides of an element aredefined, in a clock-wise manner: Top, Right, Bottom, Left. If bottom is undefined, it inherits its value from top. Likewise, if left is undefined, itinherits its value from right. If only the top value is defined, all sides inherit from that one declaration.
For more on reducing stylesheet code redundancy, and using CSS shorthand in general:
http://qrayg.com/journal/news/css-background-shorthandhttp://sonspring.com/journal/css-redundancyhttp://dustindiaz.com/css-shorthand
Margin & Padding
"Correct"
#selector { margin: 0 0 10px; padding: 0 0 10px;}
"Incorrect - left attribute unnecessary"
#selector { margin: 0 0 10px 0; padding: 0 0 10px 0;}
Hex Colors
We prefer hex values for all colors, written in lower-case. No upper-case or RGB, please! Additionally, all colors should be written as tersely aspossible. This means that colors such as full blue, which can be written lengthily as #0000FF, should be simply written as #00f. Obviously, forcolors that require more precision, all six characters should be used. For example, a light shade of grayish beige: #f9f9f0.
Background
"Correct - shorthand"
#selector { background: #fff url(../images/file.png) repeat-x fixed left bottom;}
"Incorrect - longhand unnecessary"
#selector { background-color: #fff; background-image: url(../images/file.png); background-repeat: repeat-x; background-attachment: fixed; background-position: left bottom;}
Border
In general, border should be a single line declaration, assuming that the values of the border are the same on all sides of the element. The orderin which values are declared are: width, style, and color.
"Shorthand - method 1"
#selector { border: 1px solid #000;}
If the values of each side differ, then there are two possible ways of using shorthand, and it is up to the discretion of the developer to decide whichto use. Note that method 2 follows the TRBL pattern.
"Shorthand - method 2"
#selector { border-color: #fff #999 #666 #ccc; border-style: solid dashed dotted ;double border-width: 1px 2px 3px 4px;}
"Shorthand - method 3"
#selector { border-top: 1px solid #fff; border-right: 2px dashed #999; border-bottom: 3px dotted #666; border-left: 4px #ccc;double}
By contrast, the same style declaration is extremely verbose using longhand. This should be avoided, except in instances where only oneparticular value needs to be overridden, allowing the rest to flow through.
"Longhand"
#selector { border-top-color: #fff; border-right-color: #999; border-bottom-color: #666; border-left-color: #ccc; border-top-style: solid; border-right-style: dashed; border-bottom-style: dotted; border-left-style: ;double border-top-width: 1px; border-right-width: 2px; border-bottom-width: 3px; border-left-width: 4px;}
Font
Not to be confused with the inadvisable <font> tag, the CSS font property can be written in a few different ways. The shorthand property puts allthe aspects of the font into a single declaration, whereas the longhand splits it out over several lines. While the contrast between methods is notas stark as with that of the border property, there is still space to be saved by using shorthand. While line-height can be defined within the scopeof the font declaration, but when written in longhand it has its own unique property.
Note: Times New Roman is encapsulated in quotes, because the font name itself contains spaces.
"Shorthand"
#selector { font: italic small-caps bold 15px/1.5 Cambria, 'Times New Roman', sans-serif;}
"Longhand"
#selector { font-style: italic; font-variant: small-caps; font-weight: bold; font-size: 15px; line-height: 1.5; font-family: Cambria, 'Times New Roman', sans-serif;}
Longhand
When overriding only parts of a style, longhand declaration is preferred. This way, by sticking to shorthand for initial style declarations, anytime wesee a longhand declaration used, we know that we are specifically overriding only a very precise part of an overall style, thereby leaving otheraspects unaffected.
"Longhand override"
#selector { border: 1px solid #ccc; font: 11px Verdana, sans-serif;}#selector.modifier { border-bottom-color: #333; border-bottom-width: 2px; font-family: Georgia, serif;}
Javascript
Type Coercion
Unlike strongly typed languages such as Java or C#, JavaScript will perform type coercion when evaluating conditional statements. Thissometimes creates awkward scenarios in which numerical values are seen as false or the existence of a string is mistaken for true. This istypically disadvantageous.
To ensure a strict level of comparison, as might be seen in a strongly typed or compiled language, JavaScript (like PHP) has a triple-equalsoperator ===. In similar fashion, it also has a strict negation operator !==. Consider the following examples of potential pitfalls when it comes toevaluating comparisons.
var test_1 = ' ';true test_2 = 0;var (test_1 == ) {if true
// Code here will run.// But it shouldn't.}
(test_1 === ) {if true // Code here won't run.// Correct behavior.}
(test_2 != ) {if false // Code here won't run.// But it should.}
(test_2 !== ) {if false // Code here will run.// Correct behavior.}
As you can see in the example above, simply using == and != is insufficient because it makes for potentially unpredictable results. Therefore, thestricter comparison operators should always be used. There is never a good reason to use the lesser form of comparison operators. To simply forthe existence of elements in the DOM, there is an even more abbreviated way, that leaves no room for ambiguity. If you are unsure if certainelements will be present in an HTML page, use one of the following techniques.
function first_func() { (!document.getElementById('id_name')) {if ;return } // If code gets here, element exists.}function second_func() { (!document.getElementsByTagName('div').length) {if ;return } // If code gets here, one or more exist.}
White-space
In general, the use of whitespace should follow longstanding English reading conventions. Such that, there will be one space after each commaand colon (and semi-colon where applicable), but no spaces immediately inside the right and left sides of parenthesis. In short, we advocatereadability within reason. Additionally, braces should always appear on the same line as their preceding argument.
Consider the following examples of a JavaScript for-loop...
"Correct"
for ( i=0, j=arr.length; i<j; i++) {var // Do something.}
"Incorrect"
for ( i = 0, j = arr.length; i < j; i++ )var{// Do something.}
Variables, ID & Class
All JavaScript variables shall be written in completely lowercase letters, with underscores to separate words if need be. Likewise, all id and classdeclarations in CSS shall be written in the same manner. Neither dashes nor camelCase shall be used, except for words that contain dasheswhen written in plain English.
Quotes
The preferred method of delineating strings is to use single quotes for everything. Since JavaScript exists to manipulate markup, and becauseHTML is generally written with double quotes in W3C specifications, using single quoted strings will better facilitate handling HTML fragments,and keep code more readable.
"Correct"
var my_html = '<img class= src= alt= />';"photo" "/path/file.jpg" "Text"
"Incorrect"
var my_html = photo\ /path/file.jpg\ Text\ ;"<img class=\" " src=\" " alt=\" " />"
Event Listeners
Rather than using attributes such as onload, onfocus, onsubmit, or onclick directly in markup, we will instead attach event listeners to theseelements via unobtrusive techniques. The reasoning for this is the same philosophy that is behind not using inline style="..." declarations. Sodoing inextricably ties the behavior of a web page to its data, and makes maintenance more difficult.
Event Delegation
When assigning unobtrusive event listeners, it is typically acceptable to assign the event listener directly to the element(s) which will trigger someresulting action. However, occasionally there may be multiple elements which match the criteria for which you are checking, and attaching eventlisteners to each one might negatively impact performance. In such cases you should use event delegation instead.
Closures & Scope
To maintain proper scope for variables, it is highly recommended that self-executing anonymous function be used as a closure. For the most part,variables defined correctly using the var syntax, within the scope of a function will not add to global scope pollution. However, from time to time,you may need to access variables via two or more functions. In such cases, multiple functions can be grouped together inside a closure.
"Closure"
(function() { first_variable = 'value 1';var second_variable = 'value 2';var
function first_func() { // Do something.}
function second_func() { // Do something.}})();
Objects & Arrays
Objects can be thought of as tiered variables that contain multiple attributes. Similarly, an array could be described as a list of data that all sharecommon characteristics. The following code snippets show examples of objects and arrays, and the different ways in which they can be defined.Note that values such as John Doe's age and marital status do not have quotation marks around them. This is because age is truely numerical,and true is a Boolean value.
Note also that the commas are before the variable or method declaration. This prevents errors with trailing commas in IE and other browsers.
Objects (and arrays) are an important part of JSON - JavaScript Object Notation, which is a platform and language independent way oftransmitting data, used as an alternative to XML.
"Object literal - preferred"
var john_doe = { first_name: 'John' ,last_name: 'Doe' ,job: 'Everyman Respresentative' ,email: '[email protected]' ,married: true ,age: 30};
"Object dot notation"
/*Could also be written:
john_doe = ();var new Object*/
john_doe = {};varjohn_doe.first_name = 'John';john_doe.last_name = 'Doe';john_doe.job = 'Everyman Representative';john_doe.email = '[email protected]';john_doe.married = ;truejohn_doe.age = 30;
"Array literal - preferred"
var doe_family = [ 'John' ,'James' ,'Jane' ,'Jenny' ,'Jared' ,'Jerome'];
"Array bracket notation"
/*Could also be written:
doe_family = Array();var new*/
doe_family = [];vardoe_family[0] = 'John';doe_family[1] = 'James';doe_family[2] = 'Jane';doe_family[3] = 'Jenny';doe_family[4] = 'Jared';doe_family[5] = 'Jerome';
PHP
General
Beginning brackets do NOT linebreak. They start one space after the end parenthesis, as according to traditional Unix policy.Do not do any real logic in object constructors. Create class methods to do so.null, true and false should always be lowercase.Avoid embedded assignments (ex: $d = ($a = $b + $c) is bad).Never use extract().Avoid using global variables if at all possible.Document EVERYTHING.
Parenthesis
Do not put parenthesis next to keywords. Put a space between.Do put parenthesis next to function names.Do not use parenthesis in return statements when it's not necessary. Example:
if ($test) {}
($test == $other) {while}array_push($one,$two);
$test;return
Do use parenthesis when using include, require, include_once, and require_once.not
Classes
All ''core'' classnames, unless stated otherwise for special conditions, will be prefixed with the "mod" prefix: ie, modChunk, modTemplate,etc.All method names will be camelCase and will start with a lowercase letter.All private methods and variables must be prefixed with the underscore _ character.
class modFactor { $publicVar;public $_privateVar;private function _privateFunc() { }private function publicFunc() { }public}
Variables
Note these are not function arguments.
Use all lowercase letters.Separate words with the underscore.
Function Arguments and Class Variables
The first letter is lowercase, rest are camelCase. Example:
class modFactor { function testFunc($testVar, array &$anotherTest = array()) {public $ ->_privateVar = $testVar;this $local_variable =& $anotherTest; }}
Arrays
Array index names use the underscore _, not the dash as their separator. This prevents errors with magic_quotes.Array index names are always lowercase. Spaces are represented by an underscore.Array index names are always encapsulated with single quotes.Example:
$_lang['chunk_create_text'] = 'Test';
Constants
Constants must be in all UPPERCASE letters.Use only if absolutely necessary.
File Structure
Always name PHP class files in name.class.php format.
Prefixing
Lexicon strings for Components need to be prefixed:
$_lang['mycomponent.welcome_message'] = 'Welcome!';
Always prefix class names; eg: 'finBank', 'finTransaction', etc.Always prefix names; eg: 'finStatement', 'finDeposit'Chunk
SQL
All inline SQL must be capitalized, and table and column names must be enclosed with backticks.
"Correct"
UPDATE `mydatabase`.`mytable`SET `name` = "Johnny"WHERE `id` = 123;
"Incorrect"
update mydatabase.mytable set name='Johnny' where id=12
Overview of MODx Development
This section pertains to developing Extras for MODx Revolution that can help extend your MODx system.
Developer Introduction
What is MODx?What is MVC?
What is MVC²?Connector/Processor Relationships
What is xPDO?What is an ORM?A Brief Overview of Revolution
The ModelThe View
In the front-end, they are Templates, Chunks and Resources.TemplatesChunksResources
In the ManagerThe Controller
SnippetsPlugins
The 2nd C: The ConnectorsSee Also
What is MODx?
MODx Revolution is an Content Application Platform, built for developers, designers and users who want a powerful, scalable system with flexiblecontent management built in.
What is MVC?
MVC is "Model-View-Controller", a common programming paradigm where the data's Model is only accessed through a Controller, whichconnects to a View that can easily be changed without having to change the Model.
What is MVC²?
MVC² is a MODx terminology that is "Model-View-Controller/Connector". It basically adds a new way of accessing the model from the view -Connectors, which are AJAX-based files that "connect" to processors to provide remote CRUD interactions.
Connector/Processor Relationships
Connectors are simply gateway files that hook into specific Processors. They are used mainly to prevent direct accessing of processors, and limituser access to those processors.
What is xPDO?
xPDO is our name for open eXtensions to PDO. It's a light-weight ORB (object-relational bridge) library that works on PHP 4 and 5, and takesadvantage of the newly adopted standard for database persistence in PHP 5.1+, PDO. It implements the very simple, but effective Active Recordpattern for data access, as well as a flexible domain model that allows you to isolate domain logic from database-specific logic, or not, dependingon your needs.
What is an ORM?
As defined by :Wikipedia
An object-relational database (ORD), or object-relational database management system (ORDBMS), is a database managementsystem (DBMS) similar to a relational database, but with an object-oriented database model: objects, classes and inheritanceare directly supported in database schemas and in the query language. In addition, it supports extension of the data model withcustom data-types and methods.
Basically, tables in SQL databases become classes that can contain table-specific methods, inherit from base classes, and much more.
A Brief Overview of Revolution
Revolution at its core is a Content Management Framework. It's not a PHP Application Framework like CodeIgnitor or Symfony, nor does itpurport to be one. With that said, it's much more than a typical CMS like Wordpress or others; it enables you to build Content ManagementApplications with ease and extensibility.
Revolution bases its internal structure on what we call a MVC² design system. It's loosely based on the MVC, or model-view-controllerarchitectural pattern, in programming.
The Model
The stands for , which is the core classes that manipulate data records. These core classes, prefixed with 'mod' in Revolution, handle allM Modelthe Domain logic for MODx Revolution.
This also includes what Revolution calls "processors", which are scripts that handle Domain Logic for MODx Revolution. They are never accesseddirectly, and are used to handle form processing, REST requests, AJAX requests, and more. They resemble basic CRUD(Create-Read-Update-Delete) processing tasks.
The View
Views in MODx Revolution are called 'Templates', but are used differently based on what context we're talking about.
In the front-end, they are Templates, Chunks and Resources.
Templates
Templates are what they sound like. They allow you to create templates that will encapsulate more page-specific data. Think of them likeheaders/footers all rolled into one (and so much more!)
Chunks
Chunks are small pieces of HTML code that can be inserted anywhere. They represent View widgets, in a sense, because of their modularity andease of insertion.
Resources
Resources is the basic representation of a single "webpage" in MODx Revolution. They represent a single page or resource by which the clientaccesses content from the server. They can be files, weblinks, symlinks or just plain-old HTML pages wrapped by .Templates
In the Manager
In the manager-side of MODx Revolution, the View is handled by templates as well, although these are file-based and located inmanager/templates, and currently loaded via Smarty.
The Controller
Controllers in MODx Revolution come in two forms. In the front-end, they are Request Handlers (via the modRequest class) and Snippets andPlugins.
Snippets
Snippets are simply PHP code that can be placed anywhere in a page. They can be placed in , , or Resources. They simplyChunks Templatesexecute PHP code whenever they are called, and return whatever output they would like to push to the page.
Plugins
Plugins are also PHP code, but are targeted at specific System Events that occur throughout the request processing. They can occur before thewebpage is rendered, after it is, before the request is handled, or many more places.
They allow users to write generic code that affects basic page functionality, such as word censoring, automatic link creation, separate cachehandling, context redirection, and more.
The 2nd C: The Connectors
Connectors are a new idea to MODx Revolution; they are access points for processors. The manager system in MODx Revolution uses themextensively; they provide secure locations for AJAX requests to process data on certain objects.
For example, a connector request that loads /modx/connectors/resource/index.php with a GET 'action' parameter of "get", and a GET parameterof 'id', will (assuming the request's client has access) grab the Resource with the ID specified and return it in JSON (or whatever is configured; thisis JSON by default in Revolution) format.
Every Connector request is also secured down by Context permissions loaded on every request. If the user does not have access (via the AccessPolicy assigned to the request's context), the connector will refuse to provide data.
Connectors allow for dynamic, secure JSON requests (and eventually REST-based requests) straight from your MODx manager.
See Also
xPDO, the database layer for RevolutionExplanation of Directory StructureGlossary of Revolution Terms
Getting Started Developing
Programming in MODx Revolution3rd-Party Components (3PCs)core/components and assets/componentsSnippetsPluginsProperties and Property SetsCustom Manager Pages (CMPs)Using MODx Externally
Programming in MODx Revolution
MODx Revolution is an OOP Framework, built around the database ORM .xPDO
3rd-Party Components (3PCs)
3rd-Party Components (3PCs) are collections of any sort of MODx Objects. They can be a collection of Snippets, Plugins and Chunks, or a singleSnippet, or just a collection of files. They are usually transported and installed via .Transport Packages
core/components and assets/components
MODx doesn't necessarily limit where you can put your custom 3rd party component files, but we do have some recommendations. For files thatdon't need to be in the webroot (config files, .php, etc), we recommend putting them in:
core/components/myname
So, if you had a component named 'test', you would put its non-webroot files in "core/components/test/". For files that need to be web-accessible,such as css, js and other files, we recommend:
assets/components/myname
Ergo, for 'test', "assets/components/test". This standardization of paths makes it easier for other developers using your components to find yourfiles easily.
Snippets
Snippets are simply php scripts that can be executed on any page or other Element. They are the cornerstone of MODx Development and
dynamic customization. You can read more about Snippets .here
Plugins
Plugins are similar to snippets in that they are snippets of code that have access to the MODx API - however the big difference is that plugins areassociated to specific system events. For example, in an average MODx page request, several events happen at certain points within the pageparsing process and plugins can be attached to any of these events to fulfill a desired function. aren't just limited to front-end processingPluginsthough, there are many events that are available in the MODx Manager.
Properties and Property Sets
Properties are simply placeholders on Elements (Snippets/Plugins/Chunks/TVs/Templates), which can be parsed by each individual Element.They allow customization and argument passing for each Element.
Property Sets are user-defined groupings of Properties that can be used to quickly centralize custom tag syntax calls.
More on Property Sets can be found .here
Custom Manager Pages (CMPs)
Custom Manager Pages, or CMPs, are custom pages in the manager built by 3rd Party developers to allow backend management ofComponents. They use the modAction and modMenu objects to dynamically create manager pages that can be easily found and added with nohacking of the core.
Using MODx Externally
Using the MODx object (and all of its respective classes) is quite simple. All you need is this code:
require_once '/absolute/path/to/modx/config.core.php';require_once MODX_CORE_PATH.'config/'.MODX_CONFIG_KEY.'.inc.php';require_once MODX_CORE_PATH.'model/modx/modx.class.php';$modx = modX();new$modx->initialize('web');
This will initialize the MODx object into the 'web' . Now, if you want to access it under a different (and thereby changing its accessContext Contextpermissions, policies, etc), you'll just need to change 'web' to whatever you want to load.Context
Extras Directories
Extras are most commonly stored in 2 directories when they are installed:
core/components/ - This is the location for all the PHP and non-web-accessible files for the Extra.assets/components/ - This is the location for the web-accessible files for the Extra, such as CSS, JS and images.
Why the separation? Well, since MODx users can move the core outside the webroot, separating out the non-accessible files intocore/components allows MODx developers to add an extra level of security to their Extras.
See Also
Setting up a Development Environment
Recommended Development Tools and Environments for MODx Revolution
In developing MODx Revolution, the MODx Team has found the following environments invaluable:
Netbeans
Netbeans 6.8Netbeans Subversion and JIRA plugins
Eclipse 3.6 Helios and EclipsePDT
Currently phpEclipse appears to have issues running under Helios
Install Eclipse IDE for Java EE DevelopersThis will give you the Web Tools Platform in a single step
Start up EclipseAt this point it is not suggested to use a previous workspace with live projectsFor this install we simply selected the default (and empty) workspace, which was located in the current user space
Go to the Help menu | Install New SoftwareSelect "-All Additional Sites-" from the "Work with:" drop down
Wait for the site list to populate the displayOpen the "Programming Languages" categorySelect "PHP Development Tools"
Install all suggested software-- Alternately you can directly install the PDT tools in a much smaller package
Restart EclipseAt this point you can start importing sites into the workspace
Note: With the MODx project being housed on there is an additional and optional eclipse project yougithub.com/modxcms eGitmay also wish to install. opengeek (Jason Coward) strongly suggests learning and using git from the tocommand linemaximize your flexibility and potential. It may also be suggested to set up a local git repo and simply clone the respectiveMODx and xPDO repositories, working from local copies. SVN is discouraged from continued usage in regards to future MODxrelated development.
Additional suggestions: Install your IDE in a location which is static and remains consistent for long periods of time. You mayalso want to isolate your workspace to a dedicated partition/drive, especially in operating systems (such as Soalris, Linux, andMac OS) which do not require erasure/formatting of the entire drive to install. By placing the development tools and projects indedicated spaces it will be much easier to make backups and to get back to work in the case of a system install.
It is not advised to install Eclipse and point to a workspace with existing projects, as many of its internal system settings (suchas repos and file type associations with specific tools) are stored in the workspace and may actually inadvertently cause issuesif an alternate tool is being used in place of an older one.
Eclipse versions before 3.6
Eclipse 3.2.+ (recommend latest 3.5.1)Web Standard Tools Project (WST) 2.0.1 ( )http://download.eclipse.org/webtools/updates/Subclipse 1.6.5 ( )http://subclipse.tigris.org/update_1.6.xPHPEclipse 1.2.3 ( )http://update.phpeclipse.net/update/nightlySpket IDE 1.6.18 ( )http://spket.com/update/
Installation
Simply install the latest Eclipse ClassicStart up eclipse / select a workspaceUse the Install Software option under the help menuRight click and copy each of the links above (doing them in order doesn't hurt)Click the "Add" buttonName the "repo" WST, Subclipse, PHPEclipse, or Spket, as it relates to the URLPaste the URLClick OKRepeat for each of the links above as necessaryIndividual notes:
WST - select the latest Web Tools Platform (takes quite a while)Subclipse - simply install the Subclipse optionPHPEclipse - install everything offeredSpket - Install everything offered
Other IDEs
For Mac:
TextMate - IDECoda - IDEVersions - SVN clientsvnX - SVN client
For PC:
UltraEdit - IDEE - IDETortoiseSVN - SVN clientKate - IDE for Linux / KDE
Development Server Environments
We also MacPorts, XAMPP and MAMP, and the following tools/libraries in the development of MODx Revolution:
PHPUnit - this drives the PHP 5.1+ unit testing framework for xPDO, and we'll be adding a test harness to MODx soonSimpleTest - this drives the PHP 4/5.0.x unit testing framework for xPDO, and we'll be adding a test harness to MODx soonPHPDocumentor - all of the classes in MODx Revolution are documented in PHPDoc format, and we'll be developing tutorials and otherextended documentation for inclusion in the PHPDocs in DocBook XML formatPhing - will be used to allow automation of nightly builds, various distribution builds, unit testing, and many other development tasks
Basic Development
This section is geared at familiarizing developers with basic MODx development principles, and using the structures in MODx to create dynamic,script-driven content.
Snippets
OverviewWhat is a Snippet?How Do They Work?Simple ExamplePassing Values Into a Snippet
Database Interaction in SnippetsWhy an ORM?Example DB CodeFurther Database Reading
Recommended Methods and TipsWrite your Snippets outside of MODx.Don't try to mix PHP and HTML in a Snippet.Don't Work on Live SnippetsUse Default Properties
See Also
Overview
Snippets are the method by which MODx allows you to run dynamic PHP code in any of your pages. They are the main development vehicle formost developers.
What is a Snippet?
According to one definition, a "snippet" is "a short reusable piece of computer source code". Some people have a hard time distinguishing thisfrom a "chunk", so a helpful mnemonic might lie in the p's... as in "PHP", e.g. sni-"P(h)P"-et.
How Do They Work?
First off, most Snippets are , meaning they're stored as a temporary, dynamic function in the cache. If they're flagged as uncached, thencachedthey are not parsed until the parser has done all of the other cached content.
Then, once they're up to be cached, Snippets are then parsed by the MODx Parser. They have access to the $modx object.
Simple Example
Here's the perfunctory super-basic example of what a Snippet might look like:
1.
2.
3.
<?php 'Hello, World!';return
?>
If you named this , you could call this snippet by using [[helloWorld]] in your documents, templates, or Chunks."helloWorld"
Note how we returned the code rather than 'echo'ed the content out. in a Snippet - always return the output.Never use echo
Passing Values Into a Snippet
Snippets can take input values using a modifed CGI web-form type notation. For example, if your Snippet looks something like this:
<?php 'My input was: ' . $input;return
?>
You might call it using something like this:
[[!mySnippetName? &input=`Hello World`]]
Notice that the variable names in the calling bit need to match the variable names in the Snippet EXACTLY (case matters... i.e. 'Input' not 'input').Secondly, don't forget the '&' in front of the would be variable names. And last but most certainly not least, take note that those are backticks, notsingle quotes!
Database Interaction in Snippets
Accessing the database layer in MODx is quite simple; MODx uses an Object Relational Model (ORM) called for database connectivity.xPDO
Why an ORM?
You might be asking, why use an ORM instead of just straight SQL? Well, a few reasons:
SQL Abstraction - This means that you can write code that works in a variety of different database types, such as MySQL, sqllite,postegresql, and more, as MODx expands to those databases. All without having to rewrite a single line of code.Parameter Escaping - No more having to worry about SQL injection; xPDO uses PHP's PDO to escape all variables passed in to theSQL call to prevent any malicious calls.Cleaner, shorter Code - What could be done in 40+ lines in mysql_* calls can now be done in 10 or less.
There are more reasons, but that's for brevity. Let's look at a few examples:
Example DB Code
Let's get a chunk named 'LineItem', and change the placeholders in it (done with [[+placeholderName]] syntax) to some custom values:
$chunk = $modx->getObject('modChunk',array( 'name' => 'LineItem',));
(!$chunk) 'No line item chunk!';if return
$chunk->process(array(return 'name' => 'G.I. Joe', 'grenades' => 42,));
That code would get a chunk with the name of 'LineItem', and return it processed with the placeholders set. The $chunk variable there is actuallyan , which is an object representation of the Resource.xPDOObject
What about more complex queries? Like, say, getting the first 10 Resources with a parent of 23, 24 or 25. And let's make it so they aren't hiddenfrom menus or deleted, are published, and sort them by menuindex. That's when we use the powerful $modx->newQuery() method:
$c = $modx->newQuery('modResource');$c->where(array( 'parent:IN' => array(23,24,25), 'deleted' => ,false 'hidemenu' => ,false 'published' => ,true));$c->sortby('menuindex','ASC');$c->limit(10);$resources = $modx->getCollection('modResource',$c);
Note how we first create an xPDOQuery object ($c) using $modx->newQuery(). We passed in the name of the class we wanted to build the queryfrom - here 'modResource', or Resources - and then used our where() function to add some restrictions. Then we sorted and limited them.
And finally, we called getCollection which - unlike getObject - returns a collection, or array, of xPDOObjects. We could then iterate over thoseusing foreach and do whatever we want with them.
Further Database Reading
For further reading on xPDO, read up on these:
xPDO at the spacexPDORetrieving Objects in xPDOThe ObjectxPDOQuery
Recommended Methods and Tips
Write your Snippets outside of MODx.
This is pretty easy to do - just create an 'include' snippet, but make its content be this:
if (file_exists($file)) { $o = include $file;} { $o = 'File not found at: '.$file; }else
$o;return
You can use the include snippet on a page like such:
[[!include? &file=`/absolute/path/to/my/snippet.php`]]
And run your Snippets externally while you develop them!
Then you can test them to make sure they work (e.g. on the bash command line, you can use the command to check thephp -l my_script.phpscript for syntax errors). Depending on your environment, you may also get some useful error messages to help you with debugging. Copy andpaste the code into MODx only when you're sure it's working.
Don't try to mix PHP and HTML in a Snippet.
Snippets execute PHP code. They should always begin with a and end with a For<?php ?> You cannot mix PHP and HTML in a Snippet!example, the following code won't work:
<p>This is a horrible mixture of HTML and PHP</p><?php
;return "<p>and PHP! Don't it! It's bad architecture and it won't work!!</p>"try?>
You'll find that MODx will append PHP tags to beginning and end of the snippet, creating an invalid syntax, e.g.:
<?php <?php //something here ?> ?>
If you need to do something like this, - separate the PHP into a Snippet, load its output into a placeholder with the use a Chunk modx APIplaceholder functions or chunk processing, and include the Snippet's placeholders in the Chunk:
$output = $modx->getChunk('myChunk',array( 'placeholderOne' => 'test', 'name' => 'Harry', 'scar' => 'Lightning',));
$output;return
Don't Work on Live Snippets
If you're writing new versions of Snippets, the old version! That way you can go back to the old version of the code if something doesn'tduplicatework correctly! MODx doesn't inherently do versioning control, so you have to backup code yourself.
Use Default Properties
Consider adding your properties for your snippet into the Properties grid, so that the user can add custom to override them.Property Sets
See Also
modX.runSnippetmodX.setPlaceholdermodX.regClientCSS
Templating Your Snippets
Templating Snippets
One of the best practices in Snippet design is to make sure that you never write HTML directly in the Snippet, but template out the HTML intoChunks. This tutorial shows you how to do that in a Snippet.
Templating SnippetsOur Initial SnippetTemplating the SnippetAdding A Row ClassPassing a Custom ID
See Also
Our Initial Snippet
Let's take a case scenario; say you want to iterate across the published, non-deleted Resources that are children of the Resource with ID 390,sorted by menuindex, and then output them as LI tags with the pagetitle and a link to click them.
Go ahead and create a snippet called 'ResourceLister', and put this inside:
/* first, build the query */$c = $modx->newQuery('modResource');/* we only want published and undeleted resources */$c->where(array( 'published' => ,true 'deleted' => ,false));/* get all the children of ID 390 */$children = $modx->getChildIds(390);
(count($children) > 0) {if $c->where(array( 'id:IN' => $children, ));}/* sort by menuindex ascending */$c->sortby('menuindex','ASC');/* get the resources as xPDOObjects */$resources = $modx->getCollection('modResource',$c);
$output = '';foreach ($resources as $resource) { $output .= '<li><a href="'.$modx->makeUrl($resource->get('id')).'">'.$resource->get('pagetitle').'</a></li>';}
$output;return
This does what we want, but puts the HTML inline. We don't want that. It doesn't let the user control the markup, or change it if they want to. Wewant more flexibility.
Templating the Snippet
First off, let's create a chunk that we'll use for each item in the result set. Call it "ResourceItem", and make this its content:
<li><a href= >[[+pagetitle]]</a></li>"[[~[[+id]]]]"
Basically, we make an LI tag, and put some placeholders were our content was. We have available any field in the Resource, and here we're justusing the ID and pagetitle fields. The [[~ tells MODx to make a link from the ID passed in the [[+id]] property. Now let's add a default property tothe snippet, called 'tpl', to the top of our snippet code:
$tpl = $modx->getOption('tpl',$scriptProperties,'ResourceItem');
This gets us the &tpl= property from the Snippet call, since $scriptProperties just holds all the properties in the Snippet call. If 'tpl' doesn't exist,getOption defaults the value to ResourceItem (the Chunk we just made).
And then, change the foreach loop in the Snippet to this:
foreach ($resources as $resource) { $resourceArray = $resource->toArray(); $output .= $modx->getChunk($tpl,$resourceArray);}
The code first turns the modResource object into an array of field=name pairs (ie, $resourceArray['pagetitle'] is the pagetitle) via the toArray()method. Then, we use $modx->getChunk() to pass our tpl Chunk and the resource array into it as properties. MODx parses the chunk, replacesthe properties, and returns us some content.
Now the user can call the snippet this way to override the chunk for each Resource with this call:
[[!ResourceLister? &tpl=`MyOwnChunk`]]
Meaning they can template their results however they want - using LIs, or table rows, or whatever! You've now created a flexible, powerfulsnippet.
Adding A Row Class
What if we want the user to be able to specify a CSS class for each LI row, but not have to make their own custom chunk? Simple, we just add adefault property 'rowCls' to our snippet code at the top, below our first getOption call:
$rowCls = $modx->getOption('rowCls',$scriptProperties,'resource-item');
This tells MODx to default the &rowCls property for the snippet to 'resource-item'. Let's go edit our ResourceItem chunk:
<li class= ><a href= >[[+pagetitle]]</a></li>"[[+rowCls]]" "[[~[[+id]]]]"
And finally, change our foreach loop to this:
foreach ($resources as $resource) { $resourceArray = $resource->toArray(); $resourceArray['rowCls'] = $rowCls; $output .= $modx->getChunk($tpl,$resourceArray);}
Note how we're explicitly setting the 'rowCls' variable into our $resourceArray property array. We do this because we've already gotten the valueof rowCls earlier in the snippet (with the getOption call), and we know that it's not going to vary per row.
Passing a Custom ID
What if we want the user to be able to pass in what parent to grab resources from? Again, we just add a default property 'id' to our snippet code atthe top, below our getOption calls:
$id = ( )$modx->getOption('id',$scriptProperties,390);int
Basically, allow the user to override the parent ID for the Snippet - to say Resource 123, with an &id=`123` property - in their snippet call. But wewant it to default to 390. And then we'll change the getChildIds line to this:
$children = $modx->getChildIds($id);
Obviously, you could add more options to this snippet, such as firstRowCls (for only the first row in the results), lastRowCls, firstRowTpl, sortBy,sortDir, limit, or anything else you could dream up. We could even make it so the 'published' filter is a property as well, or hide resources that arefolders, etc. The important part is that now you have the general idea.
For reference, our final code looks like this:
$tpl = $modx->getOption('tpl',$scriptProperties,'ResourceItem');$id = ( )$modx->getOption('id',$scriptProperties,390);int$rowCls = $modx->getOption('rowCls',$scriptProperties,'resource-item');
$c = $modx->newQuery('modResource');$c->where(array( 'published' => ,true 'deleted' => ,false));$children = $modx->getChildIds($id);
(count($children) > 0) {if $c->where(array( 'id:IN' => $children, ));}$c->sortby('menuindex','ASC');$resources = $modx->getCollection('modResource',$c);
$output = '';foreach ($resources as $resource) { $resourceArray = $resource->toArray(); $resourceArray['cls'] = $rowCls; $output .= $modx->getChunk($tpl,$resourceArray);}
$output;return
See Also
Adding CSS and JS to Your Pages Through Snippets
Learning How to Register CSS and JS
So, you've got a Snippet that you've been writing and want to add CSS and/or JavaScript to your pages, but don't want to have to setup a customTemplate Variable and edit it on every Resource your Snippet is used on. You want the Snippet to do it, dagnabbit! Well, it's pretty easy, actually,using some MODx API methods.
Adding to the HEAD
There are a few methods that automatically add CSS and/or JavaScript to the HEAD of the current page. They will run in the order that they'readded, so if you need them in a certain order, make sure you execute the methods in that order as well.
regClientCSS
This function lets you register any CSS file to the HEAD of the content by providing the URL in the method:
$modx->regClientCSS('assets/css/my-custom.css');
regClientStartupScript
This function lets you register any custom JavaScript to the HEAD of the document:
$modx->regClientStartupScript('assets/js/site.js');
regClientStartupHTMLBlock
This function is useful if you need to set some JS variables, or output some HTML into the HEAD:
$modx->regClientStartupHTMLBlock('<meta tag= />"here"<script type= >"text/javascript"
myCustomJSVar = 123;var</script>');
Adding Before the BODY End
There are also methods that can be used to insert Javascript or HTML at the end of every page, right before the BODY tag closes. They are oftenuseful for custom analytics scripts, or JS that needs to be run at the body-level rather than in the HEAD.
regClientScript
Similar to except that it runs before the closing BODY tag:regClientStartupScript
$modx->regClientScript('assets/js/footer.js');
regClientHTMLBlock
Similar to except that it runs before the closing BODY tag:regClientStartupHTMLBlock
$modx->regClientStartupHTMLBlock('<div>custom stuff here</div><script type= >"text/javascript"runAnalytics();</script>');
Conclusion
MODx offers Extras developers many options on how to insert custom CSS/JS into their pages at the Snippet level. However, MODx alsorecommends in any Extras you are distributing, to make sure inserting CSS or JS into a page is a toggleable option, so that the user cancustomize the content or javascript framework should they so choose.
See Also
Plugins
What is a Plugin?The Event ModelHandling an EventPlugin Examples
Word FilterPage-Not-Found Redirector:
See Also
What is a Plugin?
Plugins are similar to snippets in that they are snippets of code that have access to the MODx API - however the big difference is that Plugins areassociated with specific System Events. For example, in an average MODx page request, several events happen at certain points within the pageparsing process and plugins can be attached to any of these events to fulfill a desired function. That means that when those events "fire," controlis transferred to any Plugin "listening" for that event. Once the Plugin's code has executed, control returns to the point after the spot where theSystem Event was triggered.
Plugins aren't limited to front-end processing, there are many events that are available in the MODx Manager. There is a list of MODx SystemEvents .here
The Event Model
MODx invokes System Events across its code processes to allow you to modify core functionality without hacking the core. These System Eventscan have any number of Plugins attached to them, and will execute each Plugin in rank according to it's priority.
Handling an Event
In your Plugin, how you handle the output depends on the System Event you are in. For some system events, you return a value from the Plugin.For others, you access the output directly and modify it.
If you need to know which event triggered your plugin (say, for a plugin that listens to more than one event), you can access the Event's name likeso:
$eventName = $modx->event->name;
The code for a Plugin listening to more than one event looks like this:
$eventName = $modx->event->name;($eventName) {switch
'OnWebPageInit':case /* something */do ;break 'OnWebPagePrerender':case /* something */do else ;break}
Plugin Examples
Plugins can be used for a variety of different applications, below are a couple of examples:
Word Filter
Description: Filter words from a document before it's displayed on the web OnWebPagePrerenderSystem Events:
$words = array( , ); "snippet" "template" // words to filter$output = &$modx->resource->_output; // get a reference to the output$output = str_replace($words, ,$output);"<b>[filtered]</b>"
Page-Not-Found Redirector:
Description: Redirects a user to selected document and sends a message OnPageNotFoundSystem Events:
System Settings:
pnf.page: Error Resource IDpnf.mailto: Mail To Addresspnf.mailfrom: Mail From Address
if ($modx->event->name == 'OnPageNotFound') { $errorPage = $modx->getOption('pnf.page'); (empty($errorPage)) {if $modx->sendErrorPage(); } {else $mailto = $modx->getOption('pnf.mailto'); (!empty($mailto)) {if // send a message to a local account$resourceId = $modx->resource->get('id'); $subject = 'Page not found'; $body = 'Someone tried to access document id '.$resourceId; $modx->getService('mail', 'mail.modPHPMailer'); $modx->mail->set(modMail::MAIL_BODY, $body); $modx->mail->set(modMail::MAIL_FROM, '$modx->getOption('pnf.mailfrom')); $modx->mail->set(modMail::MAIL_FROM_NAME, 'MODx'); $modx->mail->set(modMail::MAIL_SENDER, 'MODx'); $modx->mail->set(modMail::MAIL_SUBJECT, $subject); $modx->mail->address('to',$mailto); $modx->mail->setHTML( );true $modx->mail->send(); } $url = $ ->makeUrl($scriptProperties['page']);this $modx->sendRedirect($url, 1); exit; }}
See Also
System Events
What are System Events?The Model of a System Event
Service TypesAvailable EventsSee Also
What are System Events?
System Events are the events in MODx that are registered to. They are 'fired' throughout the MODx code, allowing Plugins to interact withPluginsMODx code and add custom functionality without hacking core code.
The Model of a System Event
The system events table is found under {table_prefix}_system_eventnames, and has the following fields:
id - The unique ID of the event.name - The name of the event. This is how they are referenced in code, via the method.modX.invokeEventservice - What type of system event this event is.groupname - Used for user interfaces, primarily for filtering, grouping and sorting of events. Not used explicitly in the modx model.
Service Types
The 'service' field in the System event is a number; the numbers reference different types of System Events. They are:
1 - Parser Service Events2 - Manager Access Events3 - Web Access Service Events4 - Cache Service Events5 - Template Service Events6 - User Defined Events
3 is not fired in the 'mgr' context; 2 is not fired in any context but 'mgr'.
Available Events
This is not an exhaustive list as events are still being documented. Thank you for your patience. The TV, Template and Snippetevents are still to be documented. For a complete list, please either view a Plugin in the manager and see the System Eventstab, or view . Note also that all WUsr (web-user) events have been removed.here
See Also
OnBeforeCacheUpdate
Event: OnBeforeCacheUpdate
Fired before the entire site cache is cleared.
Service: 4 - Cache Service EventsGroup: None
Event Parameters
None.
See Also
System EventsPlugins
OnBeforeChunkFormDelete
Event: OnBeforeChunkFormDelete
Fires before a chunk is deleted.
Service: 1 - Parser Service EventsGroup: Chunks
Event Parameters
Name Description
chunk A reference to the modChunk object.
id The ID of the Chunk.
See Also
System EventsPlugins
OnBeforeChunkFormSave
Event: OnBeforeChunkFormSave
Fires after a form is submitted but before a Chunk is saved in the manager.
Service: 1 - Parser Service EventsGroup: Chunks
Event Parameters
Name Description
mode Either 'upd' or 'new', depending on the circumstance.
chunk A reference to the modChunk object.
id The ID of the chunk. Will be 0 for new chunks.
See Also
System EventsPlugins
OnBeforeDocFormDelete
Event: OnBeforeDocFormDelete
Fires before a Resource is deleted via the manager.
Service: 1 - Parser Service EventsGroup: Documents
Event Parameters
Name Description
resource A reference to the modResource object.
id The ID of the Resource.
children An array of IDs of children of this resource which will also be deleted.
See Also
System EventsPlugins
OnBeforeDocFormSave
Event: OnBeforeDocFormSave
Fires before a Resource is saved in the manager via the editing form.
Service: 1 - Parser Service EventsGroup: Documents
Event Parameters
Name Description
mode Either 'new' or 'upd', depending on the circumstances.
resource A reference to the modResource object.
id The ID of the Resource. Will be 0 for new Resources.
See Also
System EventsPlugins
OnBeforeManagerLogout
Event: OnBeforeManagerLogout
Fires before a user is logged out of the manager.
Service: 2 - Manager Access Service EventsGroup: None
Event Parameters
Name Description
user A reference to the modUser object of the user.
userid The user ID of the user. (deprecated)
username The username of the user. (deprecated)
See Also
System EventsPlugins
OnBeforeSaveWebPageCache
Event: OnBeforeSaveWebPageCache
Fired after the Resource is loaded and before the Resource is cached. If the Resource is not cacheable, this event will not fire.
Service: 4 - Cache Service EventsGroup: None
Event Parameters
None. The resource can be referenced via $modx->resource.
See Also
System EventsPlugins
OnBeforeWebLogout
Event: OnBeforeWebLogout
Fires right before a user logs out of a non-mgr context.
Service: 3 - Web Access Service EventsGroup: None
Event Parameters
Name Description
user A reference to the modUser object of the user.
userid The user ID of the user.
username The username of the user.
See Also
System EventsPlugins
OnCacheUpdate
Event: OnCacheUpdate
Fired after the cache is cleared at any time.
Service: 4 - Cache Service EventsGroup: None
Event Parameters
Name Description
results The results of the clearing.
paths An array of paths that were to be cleared.
options An array of options passed to the cache clearing method.
See Also
System EventsPlugins
OnChunkFormDelete
Event: OnChunkFormDelete
Fires after a chunk is deleted.
Service: 1 - Parser Service EventsGroup: Chunks
Event Parameters
Name Description
chunk A reference to the modChunk object.
id The ID of the Chunk.
See Also
System EventsPlugins
OnChunkFormPrerender
Event: OnChunkFormPrerender
Occurs before the chunk modification form is rendered, but after the JS is registered. Can be used to render custom Javascript for the mgr.
Service: 1 - Parser Service EventsGroup: Chunks
Event Parameters
Name Description
mode Either 'new' or 'upd', depending on the circumstance.
id The ID of the Chunk. This will be 0 for new chunks.
chunk A reference to the modChunk object. Will be null in new chunks.
See Also
System EventsPlugins
OnChunkFormRender
Event: OnChunkFormRender
Fires during the rendering of a form. Useful for rendering HTML straight into the Chunk form.
Service: 1 - Parser Service EventsGroup: Chunks
Event Parameters
Name Description
mode Either 'new' or 'upd', depending on the circumstance.
id The ID of the Chunk. This will be 0 for new chunks.
chunk A reference to the modChunk object. Will be null in new chunks.
See Also
System EventsPlugins
OnChunkFormSave
Event: OnChunkFormSave
Fires after a Chunk is saved in the manager.
Service: 1 - Parser Service EventsGroup: Chunks
Event Parameters
Name Description
mode Either 'upd' or 'new', depending on the circumstance.
chunk A reference to the modChunk object.
id The ID of the chunk.
See Also
System EventsPlugins
OnDocFormDelete
Event: OnDocFormDelete
Fires after a Resource is deleted via the manager.
Service: 1 - Parser Service EventsGroup: Documents
Event Parameters
Name Description
resource A reference to the modResource object.
id The ID of the Resource.
children An array of IDs of children of this resource which were deleted.
See Also
System EventsPlugins
OnDocFormPrerender
Event: OnDocFormPrerender
Fires before a Resource editing form is loaded in the manager.
Service: 1 - Parser Service EventsGroup: Documents
Event Parameters
Name Description
mode Either 'new' or 'upd', depending on the circumstance.
resource A reference to the modResource object. Will be null for new Resources.
id The ID of the Resource. Will be 0 for new Resources.
See Also
System EventsPlugins
OnDocFormRender
Event: OnDocFormRender
Fires after a Resource editing form is loaded in the manager. Useful for inserting HTML into forms.
Service: 1 - Parser Service EventsGroup: Documents
Event Parameters
Name Description
mode Either 'new' or 'upd', depending on the circumstance.
resource A reference to the modResource object. Will be null for new Resources.
id The ID of the Resource. Will be 0 for new Resources.
See Also
System EventsPlugins
OnDocFormSave
Event: OnDocFormSave
Fires after a Resource is saved in the manager via the editing form.
Service: 1 - Parser Service EventsGroup: Documents
Event Parameters
Name Description
mode Either 'new' or 'upd', depending on the circumstances.
resource A reference to the modResource object.
id The ID of the Resource. Will be 0 for new Resources.
See Also
System EventsPlugins
OnDocPublished
Event: OnDocPublished
Called when a Resource is published via the Publish context menu.
Service: 5 - Template Service EventsGroup: None
Event Parameters
Name Description
docid The ID of the resource being published. (deprecated)
id The ID of the resource being published.
resource A reference to the modResource object being published.
See Also
System EventsPlugins
OnDocUnPublished
Event: OnDocUnPublished
Called when a Resource is unpublished via the Unpublish context menu.
Service: 5 - Template Service EventsGroup: None
Event Parameters
Name Description
docid The ID of the resource being unpublished. (deprecated)
id The ID of the resource being unpublished.
resource A reference to the modResource object being unpublished.
See Also
System EventsPlugins
OnLoadWebPageCache
Event: OnLoadWebPageCache
Fires after a Resource is loaded from the cache. If the Resource is not cached, this event will not fire.
Service: 4 - Cache Service EventsGroup: None
Event Parameters
None.
See Also
System EventsPlugins
OnManagerLogin
Event: OnManagerLogin
Fires anytime a user successfully logs into the manager.
Service: 2 - Manager Access Service EventsGroup: None
Event Parameters
Name Description
user A reference to the modUser object.
attributes An array of:
rememberme - Boolean set if user wants password to be remembered.lifetime - The session cookie lifetime for this login.loginContext - The context key this login is occurring in.
See Also
System EventsPlugins
OnManagerLogout
Event: OnManagerLogout
Fires after a user is logged out of the manager and their context session is removed.
Service: 2 - Manager Access Service EventsGroup: None
Event Parameters
Name Description
user A reference to the modUser object of the user.
userid The user ID of the user. (deprecated)
username The username of the user. (deprecated)
See Also
System EventsPlugins
OnSiteRefresh
Event: OnSiteRefresh
Fires after the cache for the entire site is cleared.
Service: 1 - Parser Service EventsGroup: None
Event Parameters
Name Description
results An array of results.
See Also
System EventsPlugins
OnUserChangePassword
Event: OnUserChangePassword
Fires anytime the user properly changes their password.
Service: 3 - Template Service EventsGroup: None
Event Parameters
Name Description
user A reference to the modUser object of the user.
newpassword The new password being set.
oldpassword The old password being overridden.
userid The user ID of the user. (deprecated)
username The username of the user. (deprecated)
userpassword The new password being set. (deprecated)
See Also
System EventsPlugins
OnWebLogin
Event: OnWebLogin
Fired anytime a user logs into a non-mgr context.
Service: 3 - Web Access Service EventsGroup: None
Event Parameters
Name Description
user A reference to the modUser object.
attributes An array of:
rememberme - Boolean set if user wants password to be remembered.lifetime - The session cookie lifetime for this login.loginContext - The context key this login is occurring in.
See Also
System EventsPlugins
OnWebLogout
Event: OnWebLogout
Fires right after the user logs out of a context and their context session is removed.
Service: 3 - Web Access Service EventsGroup: None
Event Parameters
Name Description
user A reference to the modUser object of the user.
userid The user ID of the user.
username The username of the user.
See Also
System EventsPlugins
OnWebPagePrerender
Event: OnWebPagePrerender
Fired after a Resource is parsed, but before it is rendered.
Content Type headers have not yet been sent, nor has the output been flushed.
Service: 5 - Template Service Events
Group: None
Event Parameters
None.
See Also
System EventsPlugins
OnBeforeEmptyTrash
Event: OnBeforeEmptyTrash
Fires before the trash is emptied for the site.
Service: 1 - Parser Service EventsGroup: Documents
Event Parameters
Name Description
ids An array of Resource IDs that will be permanently deleted.
See Also
System EventsPlugins
OnBeforeManagerLogin
Event: OnBeforeManagerLogin
Fires before the login process is started for a user when logging in to the manager context.
Service: 3 - Web Access EventsGroup: None
Event Parameters
Name Description
username The provided username.
password The provided password.
attributes An array of:
rememberme - Boolean set if user wants password to be remembered.lifetime - The session cookie lifetime for this login.loginContext - The context key this login is occurring in.
See Also
System EventsPlugins
OnBeforeManagerPageInit
Event: OnBeforeManagerPageInit
Loaded right before a manager controller is run.
Service: 2 - Manager Access EventsGroup: None
Event Parameters
Name Description
action The ID of the action being loaded.
filename The filename of the controller being loaded.
See Also
System EventsPlugins
OnBeforePluginFormDelete
Event: OnBeforePluginFormDelete
Fires before a plugin is deleted in the manager.
Service: 1 - Parser Service EventsGroup: Plugins
Event Parameters
Name Description
plugin A reference to the modPlugin object.
id The ID of the Plugin.
See Also
System EventsPlugins
OnBeforePluginFormSave
Event: OnBeforePluginFormSave
Fires after a form is submitted but before a Plugin is saved in the manager.
Service: 1 - Parser Service EventsGroup: Plugin
Event Parameters
Name Description
mode Either 'upd' or 'new', depending on the circumstance.
plugin A reference to the modPlugin object.
id The ID of the plugin. Will be 0 for new plugins.
See Also
System EventsPlugins
OnBeforeSnipFormDelete
Event: OnBeforeSnipFormDelete
Fires before a Snippet is deleted in the manager.
Service: 1 - Parser Service EventsGroup: Snippets
Event Parameters
Name Description
snippet A reference to the modSnippet object.
id The ID of the Snippet.
See Also
System EventsPlugins
OnBeforeSnipFormSave
Event: OnBeforeSnipFormSave
Fires after a form is submitted but before a Snippet is saved in the manager.
Service: 1 - Parser Service EventsGroup: Snippets
Event Parameters
Name Description
mode Either 'upd' or 'new', depending on the circumstance.
snippet A reference to the modSnippet object.
id The ID of the Snippet. Will be 0 for new Snippets.
See Also
System EventsPlugins
OnBeforeTempFormDelete
Event: OnBeforeTempFormDelete
Fires before a Template is deleted in the manager.
Service: 1 - Parser Service EventsGroup: Templates
Event Parameters
Name Description
template A reference to the modTemplate object.
id The ID of the Template.
See Also
System EventsPlugins
OnBeforeTempFormSave
Event: OnBeforeTempFormSave
Fires after a form is submitted but before a Template is saved in the manager.
Service: 1 - Parser Service EventsGroup: Templates
Event Parameters
Name Description
mode Either 'upd' or 'new', depending on the circumstance.
template A reference to the modTemplate object.
id The ID of the Template. Will be 0 for new Templates.
See Also
System EventsPlugins
OnBeforeTVFormDelete
Event: OnBeforeTVFormDelete
Fires before a TV is deleted in the manager.
Service: 1 - Parser Service EventsGroup: Template Variables
Event Parameters
Name Description
tv A reference to the modTemplateVar object.
id The ID of the TV.
See Also
System EventsPlugins
OnBeforeTVFormSave
Event: OnBeforeTVFormSave
Fires after a form is submitted but before a Chunk is saved in the manager.
Service: 1 - Parser Service EventsGroup: Template Variables
Event Parameters
Name Description
mode Either 'upd' or 'new', depending on the circumstance.
tv A reference to the modTemplateVar object.
id The ID of the TV. Will be 0 for new TVs.
See Also
System EventsPlugins
OnBeforeUserActivate
Event: OnBeforeUserActivate
Never fired in MODx core; can be used by 3rd Party Components (such as ) when a User is being activated.Login
Service: 1 - Parser Service EventsGroup: modUser
Event Parameters
Depends on implementation.
See Also
System EventsPlugins
OnBeforeUserFormDelete
Event: OnBeforeUserFormDelete
Fires before a User is deleted in the manager.
Service: 1 - Parser Service EventsGroup: Users
Event Parameters
Name Description
user A reference to the modUser object.
id The ID of the User.
See Also
System EventsPlugins
OnBeforeUserFormSave
Event: OnBeforeUserFormSave
Fires after a form is submitted but before a User is saved in the manager.
Service: 1 - Parser Service EventsGroup: Users
Event Parameters
Name Description
mode Either 'upd' or 'new', depending on the circumstance.
user A reference to the modUser object.
id The ID of the User. Will be 0 for new Users.
See Also
System EventsPlugins
OnBeforeWebLogin
Event: OnBeforeWebLogin
Fires before the login process is started for a user when logging in via a non-manager context.
Service: 3 - Web Access EventsGroup: None
Event Parameters
Name Description
username The provided username.
password The provided password.
attributes An array of:
rememberme - Boolean set if user wants password to be remembered.lifetime - The session cookie lifetime for this login.loginContext - The context key this login is occurring in.
See Also
System EventsPlugins
OnCategoryBeforeRemove
Event: OnCategoryBeforeRemove
Happens right before a category is removed.
Service: 2 - Manager Access EventsGroup: Categories
Event Parameters
Name Description
category A reference to the modCategory object.
See Also
System EventsPlugins
OnCategoryBeforeSave
Event: OnCategoryBeforeSave
Happens right before a category is saved.
Service: 2 - Manager Access EventsGroup: Categories
Event Parameters
Name Description
id The ID of the modCategory object.
category A reference to the modCategory object.
See Also
System EventsPlugins
OnCategoryRemove
Event: OnCategoryRemove
Happens after a category is removed.
Service: 2 - Manager Access EventsGroup: modCategory
Event Parameters
Name Description
category A reference to the modCategory object.
See Also
System EventsPlugins
OnCategorySave
Event: OnCategorySave
Happens after a category is saved.
Service: 2 - Manager Access EventsGroup: modCategory
Event Parameters
Name Description
category A reference to the modCategory object.
See Also
System EventsPlugins
OnChunkBeforeRemove
Event: OnChunkBeforeRemove
Fires right before a Chunk is removed.
Service: 1 - Parser Service EventsGroup: modChunk
Event Parameters
Name Description
chunk A reference to the modChunk object.
See Also
System EventsPlugins
OnChunkBeforeSave
Event: OnChunkBeforeSave
Fires right before a Chunk is saved.
Service: 1 - Parser Service EventsGroup: modChunk
Event Parameters
Name Description
chunk A reference to the modChunk object.
See Also
System EventsPlugins
OnChunkRemove
Event: OnChunkRemove
Happens after a chunk is removed.
Service: 2 - Manager Access EventsGroup: modChunk
Event Parameters
Name Description
chunk A reference to the modChunk object.
See Also
System EventsPlugins
OnChunkSave
Event: OnChunkSave
Happens after a chunk is saved.
Service: 2 - Manager Access EventsGroup: modChunk
Event Parameters
Name Description
chunk A reference to the modChunk object.
See Also
System EventsPlugins
OnContextBeforeRemove
Event: OnContextBeforeRemove
Happens right before a context is removed.
Service: 2 - Manager Access EventsGroup: modContext
Event Parameters
Name Description
context A reference to the modContext object.
See Also
System EventsPlugins
OnContextBeforeSave
Event: OnContextBeforeSave
Happens right before a context is saved.
Service: 2 - Manager Access Events
Event Parameters
Name Description
context A reference to the modContext object.
mode Either 'new' (modSystemEvent::MODE_NEW) or 'upd' (modSystemEvent::MODE_UPD) depending on whether is a new object or anexisting one.
See Also
System EventsPlugins
OnContextFormPrerender
Event: OnContextFormPrerender
Fires prior to the context editing form loading. Useful for running custom javascript.
Service: 2 - Manager Access EventsGroup: modContext
Event Parameters
Name Description
key The key of the context.
context A reference to the modContext object.
mode Either 'upd' or 'new', depending on the situation.
See Also
System EventsPlugins
OnContextFormRender
Event: OnContextFormRender
Fires after the context editing form has loaded.
Service: 2 - Manager Access EventsGroup: modContext
Event Parameters
Name Description
key The key of the context.
context A reference to the modContext object.
mode Either 'upd' or 'new', depending on the situation.
See Also
System EventsPlugins
OnContextRemove
Event: OnContextRemove
Happens after a context is removed.
Service: 2 - Manager Access EventsGroup: modContext
Event Parameters
Name Description
context A reference to the modContext object.
See Also
System EventsPlugins
OnContextSave
Event: OnContextSave
Happens whenever a context is saved.
Service: 2 - Manager Access Events
Event Parameters
Name Description
context A reference to the modContext object.
mode Either 'new' (modSystemEvent::MODE_NEW) or 'upd' (modSystemEvent::MODE_UPD) depending on whether is a new object or anexisting one.
See Also
System EventsPlugins
OnEmptyTrash
Event: OnEmptyTrash
Fires after the trash is emptied for the site.
Service: 1 - Parser Service EventsGroup: Documents
Event Parameters
Name Description
ids An array of Resource IDs that were attempted to be permanently deleted.
num_deleted The number of Resources actually deleted.
See Also
System EventsPlugins
OnFileManagerUpload
Event: OnFileManagerUpload
Fires after any files are uploaded via the manager.
Service: 1 - Parser Service EventsGroup: None
Event Parameters
Name Description
files An array of files from the PHP $_FILES array.
directory A reference to the modDirectory object that the files are being uploaded to.
See Also
System EventsPlugins
OnHandleRequest
Event: OnHandleRequest
Fires at the beginning of a request handler.
Service: 5 - Template Service EventsGroup: None
Event Parameters
None.
See Also
System EventsPlugins
OnInitCulture
Event: OnInitCulture
Fires after a Culture and Lexicon has been initialized.
Service: 1 - Parser Service EventsGroup: None
Event Parameters
None.
See Also
System EventsPlugins
OnLoadWebDocument
Event: OnLoadWebDocument
Fires directly before the Response is sent and after a Resource is loaded.
Service: 5 - Template Service EventsGroup: None
Event Parameters
None. The resource can be referenced via $modx->resource.
See Also
System EventsPlugins
OnManagerAuthentication
Event: OnManagerAuthentication
Fires right before the user is authenticated or its session is added to the manager context. This event can be used to provide externalauthentication support.
If its output is true, or an array where at least one index is set to true, then MODx will assume that the user has successfully logged in and bypassthe default authentication via password.
Service: 2 - Manager Access EventsGroup: None
Event Parameters
Name Description
user A reference to the modUser object.
password The provided password.
rememberme Whether or not to remember the user via cookie.
lifetime The lifetime of the session cookie.
See Also
System EventsPlugins
OnManagerLoginFormPrerender
Event: OnManagerLoginFormPrerender
Fires before the login form is rendered for the MODx manager.
Service: 2 - Manager Access EventsGroup: None
Event Parameters
None.
See Also
System EventsPlugins
OnManagerLoginFormRender
Event: OnManagerLoginFormRender
Fires when the login form is rendered for the MODx manager. Useful for inserting custom HTML into the login form.
Service: 2 - Manager Access EventsGroup: None
Event Parameters
None.
See Also
System EventsPlugins
OnManagerPageInit
Event: OnManagerPageInit
Fired in the manager request handler, before the manager page response is loaded.
Service: 2 - Manager Access EventsGroup: None
Event Parameters
Name Description
action The ID of the action currently being loaded.
See Also
System EventsPlugins
OnPageNotFound
Event: OnPageNotFound
Fires immediately before the user is forwarded to the error page if attempting to view a non-existent Resource.
Service: 1 - Parser Service EventsGroup: None
Event Parameters
Name Description
response_code The response code to send. Defaults to "HTTP/1.1 404 Not Found"
error_type The type. Defaults to 404.
error_header The header being sent: Defaults to "HTTP/1.1 404 Not Found"
error_pagetitle The pagetitle of the error page.
error_message The message being sent in the error page.
See Also
System EventsPlugins
OnPageUnauthorized
Event: OnPageUnauthorized
Fires immediately before the user is forwarded to the unauthorized page if attempting to view a non-accessible Resource.
Service: 1 - Parser Service EventsGroup: None
Event Parameters
Name Description
response_code The response code to send. Defaults to "HTTP/1.1 401 Unauthorized"
error_type The type. Defaults to 401.
error_header The header being sent: Defaults to "HTTP/1.1 401 Unauthorized"
error_pagetitle The pagetitle of the unauthorized page.
error_message The message being sent in the unauthorized page.
See Also
System EventsPlugins
OnParseDocument
Event: OnParseDocument
Fires on each time the Element tags are parsed. This can happen many times during the loading of a Resource. To reference the content of theResource, use $modx->documentOutput.
Service: 1 - Template Service EventsGroup: None
Event Parameters
None.
See Also
System EventsPlugins
OnPluginBeforeRemove
Event: OnPluginBeforeRemove
Happens right before a plugin is removed.
Service: 2 - Manager Access EventsGroup: modPlugin
Event Parameters
Name Description
plugin A reference to the modPlugin object.
See Also
System EventsPlugins
OnPluginBeforeSave
Event: OnPluginBeforeSave
Happens right before a plugin is saved.
Service: 2 - Manager Access EventsGroup: modPlugin
Event Parameters
Name Description
plugin A reference to the modPlugin object.
See Also
System EventsPlugins
OnPluginEventRemove
Event: OnPluginEventRemove
Fires after a plugin is disassociated directly from an event.
Service: 1 - Parser Service EventsGroup: None
Event Parameters
Name Description
id The id of the modPluginEvent object
pluginEvent A reference to the modPluginEvent object
See Also
System EventsPlugins
OnPluginFormDelete
Event: OnPluginFormDelete
Fires after a plugin is deleted.
Service: 1 - Parser Service EventsGroup: Plugins
Event Parameters
Name Description
chunk A reference to the modPlugin object.
id The ID of the Plugin.
See Also
System EventsPlugins
OnPluginFormPrerender
Event: OnPluginFormPrerender
Occurs before the plugin modification form is rendered, but after the JS is registered. Can be used to render custom Javascript for the mgr.
Service: 1 - Parser Service EventsGroup: Plugins
Event Parameters
Name Description
mode Either 'new' or 'upd', depending on the circumstance.
id The ID of the Plugin. Will be 0 for new plugins.
plugin A reference to the modPlugin object. Will be null in new plugins.
See Also
System EventsPlugins
OnPluginFormRender
Event: OnPluginFormRender
Fires during the rendering of a form. Useful for rendering HTML straight into the Plugin form.
Service: 1 - Parser Service EventsGroup: Plugins
Event Parameters
Name Description
mode Either 'new' or 'upd', depending on the circumstance.
id The ID of the Plugin. This will be 0 for new plugins.
chunk A reference to the modPlugin object. Will be null in new plugins.
See Also
System EventsPlugins
OnPluginFormSave
Event: OnPluginFormSave
Fires after a Plugin is saved in the manager.
Service: 1 - Parser Service EventsGroup: Plugins
Event Parameters
Name Description
mode Either 'upd' or 'new', depending on the circumstance.
chunk A reference to the modPlugin object.
id The ID of the plugin.
See Also
System EventsPlugins
OnPluginRemove
Event: OnPluginRemove
Happens right after a plugin is removed.
Service: 2 - Manager Access EventsGroup: modPlugin
Event Parameters
Name Description
plugin A reference to the modPlugin object.
See Also
System EventsPlugins
OnPluginSave
Event: OnPluginSave
Happens right after a plugin is removed.
Service: 2 - Manager Access EventsGroup: modPlugin
Event Parameters
Name Description
plugin A reference to the modPlugin object.
See Also
System EventsPlugins
OnPropertySetBeforeRemove
Event: OnPropertySetBeforeRemove
Fires right before a Property Set is removed.
Service: 1 - Parser Service Events
Event Parameters
Name Description
propertySet A reference to the modPropertySet object.
See Also
System EventsPlugins
OnPropertySetBeforeSave
Event: OnPropertySetBeforeSave
Fires right before a Property Set is saved.
Service: 1 - Parser Service Events
Event Parameters
Name Description
propertySet A reference to the modPropertySet object.
See Also
System EventsPlugins
OnPropertySetRemove
Event: OnPropertySetRemove
Fires after a Property Set is removed.
Service: 1 - Parser Service Events
Event Parameters
Name Description
propertySet A reference to the modPropertySet object.
See Also
System EventsPlugins
OnPropertySetSave
Event: OnPropertySetSave
Fires right after a Property Set is saved.
Service: 1 - Parser Service Events
Event Parameters
Name Description
propertySet A reference to the modPropertySet object.
See Also
System EventsPlugins
OnResourceGroupBeforeRemove
Event: OnResourceGroupBeforeRemove
Fires right before a Resource Group is removed.
Service: 1 - Parser Service Events
Event Parameters
Name Description
group A reference to the modResourceGroup object.
See Also
System EventsPlugins
OnResourceGroupBeforeSave
Event: OnResourceGroupBeforeSave
Fires right before a Resource Group is saved.
Service: 1 - Parser Service EventsGroup: modResourceGroup
Event Parameters
Name Description
group A reference to the modResourceGroup object.
See Also
System EventsPlugins
OnResourceGroupRemove
Event: OnResourceGroupRemove
Fires after a Resource Group is removed.
Service: 1 - Parser Service Events
Event Parameters
Name Description
group A reference to the modResourceGroup object.
See Also
System EventsPlugins
OnResourceGroupSave
Event: OnResourceGroupSave
Fires after a Resource Group is saved.
Service: 1 - Parser Service EventsGroup: modResourceGroup
Event Parameters
Name Description
group A reference to the modResourceGroup object.
See Also
System EventsPlugins
OnRichTextBrowserInit
Event: OnRichTextBrowserInit
Used to handle MODx.Browser's filesystem implementation for custom RTEs.
Simply pass in a Javascript function that will handle the return url for the file selected from MODx.Browser to your RTE.
Service: 1 - Parser Service EventsGroup: Richtext Editor
Event Parameters
None.
See Also
System EventsPlugins
OnRichTextEditorInit
Event: OnRichTextEditorInit
Renders anytime a Richtext Editor could be used.
Service: 1 - Parser Service EventsGroup: Richtext Editor
Event Parameters
Name Description
editor The specified editor that the user wants to use.
elements An array of elements to transform into an RTE.
Other properties might be passed, such as:
forfrontend If passed, this will indicate to the plugin that this is to be loaded in a front-end context, not the manager.
width The requested width of the RTE.
height The requested height of the RTE.
See Also
System EventsPlugins
OnRichTextEditorRegister
Event: OnRichTextEditorRegister
Renders during any dropdown or select for available richtext editors for MODx.
If you are developing a custom RTE, simply return the name of the RTE that you are developing. This will then be matched to the System Setting'which_editor', which will allow users to select your RTE to use.
Service: 1 - Parser Service EventsGroup: Richtext Editor
Event Parameters
None.
See Also
System EventsPlugins
OnSiteSettingsRender
Event: OnSiteSettingsRender
Fires before the system settings page is rendered. Useful for adding custom HTML into the System Settings page.
Service: 1 - Parser Service EventsGroup: System Settings
Event Parameters
None.
See Also
System EventsPlugins
OnUserActivate
Event: OnUserActivate
Never fired in MODx core; can be used by 3rd Party Components (such as ) when a User is being activated.Login
Service: 1 - Parser Service EventsGroup: modUser
Event Parameters
Depends on implementation.
See Also
System EventsPlugins
OnUserBeforeRemove
Event: OnUserBeforeRemove
Fires right before a User is removed.
Service: 1 - Parser Service EventsGroup: modUser
Event Parameters
Name Description
user A reference to the modUser object.
See Also
System EventsPlugins
OnUserBeforeSave
Event: OnUserBeforeSave
Fires right before a User is saved.
Service: 1 - Parser Service EventsGroup: modUser
Event Parameters
Name Description
user A reference to the modUser object.
mode Either 'new' (modSystemEvent::MODE_NEW) or 'upd' (modSystemEvent::MODE_UPD) depending on whether is a new object or anexisting one.
See Also
System EventsPlugins
OnUserFormDelete
Event: OnUserFormDelete
Fires after a User is deleted.
Service: 1 - Parser Service EventsGroup: modUser
Event Parameters
Name Description
user A reference to the modUser object.
id The ID of the user.
See Also
System EventsPlugins
OnUserFormSave
Event: OnUserFormSave
Fires after a User is updated via the manager form.
Service: 1 - Parser Service EventsGroup: modUser
Event Parameters
Name Description
user A reference to the modUser object.
id The ID of the user.
See Also
System EventsPlugins
OnUserNotFound
Event: OnUserNotFound
Fires when a user is not found during login.
Can be used to provide external authentication by returning an array, where one of the indexes in the array is an instance of (or extending) amodUser object.
Service: 6 - User Defined EventsGroup: modUser
Event Parameters
Name Description
username The specified username.
password The specified password.
attributes An array of:
rememberme - Boolean set if user wants password to be remembered.lifetime - The session cookie lifetime for this login.loginContext - The context key this login is occurring in.
See Also
System EventsPlugins
OnUserRemove
Event: OnUserRemove
Fires after a User is removed.
Service: 1 - Parser Service EventsGroup: modUser
Event Parameters
Name Description
user A reference to the modUser object.
See Also
System EventsPlugins
OnUserSave
Event: OnUserSave
Fires when a User is saved.
Service: 1 - Parser Service EventsGroup: modUser
Event Parameters
Name Description
user A reference to the modUser object.
mode Either 'new' (modSystemEvent::MODE_NEW) or 'upd' (modSystemEvent::MODE_UPD) depending on whether is a new object or anexisting one.
See Also
System EventsPlugins
OnWebAuthentication
Event: OnWebAuthentication
Fires right before the user is authenticated or its session is added for any non-manager context. This event can be used to provide externalauthentication support.
If its output is true, or an array where at least one index is set to true, then MODx will assume that the user has successfully logged in and bypassthe default authentication via password.
Service: 3 - Web Access EventsGroup: None
Event Parameters
Name Description
user A reference to the modUser object.
password The provided password.
rememberme Whether or not to remember the user via cookie.
lifetime The lifetime of the session cookie.
See Also
System EventsPlugins
OnWebPageComplete
Event: OnWebPageComplete
Fires after the Resource is loaded, response is sent, cache is stored (if applicable) and execution is completed.
Service: 2 - Template Service EventsGroup: None
Event Parameters
None.
See Also
System EventsPlugins
OnWebPageInit
Event: OnWebPageInit
Fires during the initialization process of a Resource, after modRequest::beforeRender is called, but before config placeholders are set and any404 or unauthorized page checking are done, or a response is sent.
Service: 5 - Template Service EventsGroup: None
Event Parameters
None.
See Also
System EventsPlugins
xPDO
What is xPDO?
OpenExpedio is the name for PDO. It's a light-weight ORB (object-relational bridge) (also see ). It implements the veryopen eXtensions to ORMsimple, but effective, Active Record pattern for data access. The library works on PHP 4 (xPDO 1.0 only) and 5, and takes advantage of the newlyadopted standard for database persistence in PHP 5.1+.
Documentation for xPDO
Documentation on xPDO can be found here:
xPDO 2.0 Documentation
See Also
More information about xPDO can be found at the .xPDO website
Advanced Development
This section is for advanced techniques and tools in MODx development.
Namespaces
What are Namespaces?
Namespaces are organizational elements for Components. They relate lexicon strings and packages to one another, as well as provide a basicway for Revolution to know what objects belong to what package.
Usage
Revolution uses namespace paths to determine where to load 3rd Party Component files for custom manager pages, as well as managing customlanguage strings for those 3rd Party Components.
How to get a Namespace
You can access a Namespace, and its specified path, via:
$namespace = $modx->getObject('modNamespace','mynamespace');echo $namespace->get('name').' has a path of: '.$namespace->get('path');
See Also
Package ManagementInternationalizationSettings
Caching
How is Caching Done in Revolution?Configuration CacheContext Configuration, Event Map, and Plugin CachingResource (Partial-Page) CachingElement CachingxPDO Object and Database Result Set Caching
Programmatic CachingUsing modCacheManagerUsing modCacheManager::clearCache
See Also
There are many different caching features in Revolution. Different aspects of the caching can be extended with user-defined classes, so it ispossible to implement different kinds of features in custom component cache mechanisms (i.e. where you cache your own custom data), just notthroughout the entire core.
How is Caching Done in Revolution?
First, lets take a look at all of the things in the core/cache/ directory of Revolution (or core/cache/MODX_CONFIG_KEY/ if you have that set tosomething other than 'config')
Configuration Cache
In the root you will find the config.cache.php which contains the system settings which are loaded on any request.
Context Configuration, Event Map, and Plugin Caching
In a subdirectory for each context (i.e. web/ or mgr/ or connector/) you will find a context.cache.php file containing the context settings, a pluginevent map for the context, and the plugin elements that are registered in the event map.
Resource (Partial-Page) Caching
Within the web context (or another custom front-end context) there will be a subdirectory for resources/ containing files like 1.cache.php where the1 represents the id of the resource. These files contain the resource object as well as any cached element output used by the resource.
Element Caching
Within each context you will also find an elements/ subdirectory containing cache files for various kinds of elements (especially snippets andplugins). These contain various kinds of cache files which the element classes make use of when they are being processed. For instance,snippets and plugins both cache a generated global function which is included and made available at runtime.
xPDO Object and Database Result Set Caching
If you enable this, xPDO can cache query result sets representing any objects and collections in the objects subdirectory. This can be enabled inenvironments where database access is more expensive than PHP include time. This is separate from all of the other caching mechanisms whichare very specific to the operation of MODx.
Programmatic Caching
xPDOCacheManager and the modCacheManager class derived from it provide some useful features for caching any kind of data to just aboutany cache system and includes a memcached implementation. There is no tagging feature here as in the programmatic features of theZend_Cache library but there are definitely some ways to clear specific parts of any custom caching, as well as various parts of theMODx-specific caching mechanisms described above.
Using modCacheManager
// write some data to a cache file$colors = array('red','blue','green');$modx->cacheManager->set('colors',$colors); /* writes to core/cache/colors.cache.php */
// get that data$colors = $modx->cacheManager->get('colors');foreach ($colors as $color) { echo $color.'-';} /* echoes 'red-blue-green' */ this?>
You can also set time expirations on cached files:
$str = 'My test cached data.';$modx->cacheManager->set('testdata',$str,7200);// caches the data 2 hours.this for
Using modCacheManager::clearCache
Now, here are some examples of using modCacheManager->clearCache():
<?php// clear all the usual stuff by (all files with the extension .cache.phpdefault// in the cachePath + all object caches)$modx->cacheManager->clearCache();
// clear only cache files with extension .php or .log in the web/ custom/// or logs/ paths; no objects are cleared$paths = array('web/', 'custom/', 'logs/');$options = array('objects' => , 'extensions' => array('.php', '.log'));null$modx->cacheManager->clearCache($paths, $options);
// clear all cache files with extension .php in the cachePath// + all objects + execute the timed publishing checks$paths = array('');$options = array('objects' => '*', 'publishing' => , 'extensions' => array('.php'));true$modx->cacheManager->clearCache($paths, $options);
See Also
Caching in xPDOxPDOCacheManager
Custom Manager Pages
What is a CMP?Namespaces
Creating a NamespaceUsing modAction and modMenu
Creating the modActionCreating the modMenu
Returning the PageSmartyPlain-Old HTMLScripts and CSS
Brief Overview of MODExtCustom ConnectorsConclusionSee Also
What is a CMP?
CMP stands for Custom Manager Page, and it is simply a custom page that a developer or user can create that can be accessed from within theMODx Revolution manager. It can be used to create custom administration interfaces for 3rd Party Components (Extras/3PCs), or it can simplyadd functionality to the Revolution core.
In MODx Evolution, CMP's were handled by , but Revolution does not use Modules.Modules
Namespaces
Because CMPs are generated by code on the filesystem, you have to define a path to tell MODx where to look for the custom PHP controllers toload the Custom Manager Page. These paths are called 'Namespaces', and in case you're not familiar with MVC nomenclature, the generic termfor a script or function which generates a page is a 'controller'. The Manager will search for the controller file in the path defined by theNamespace. How the CMP handles redirection from there on is up to the developer.
Creating a Namespace
You can create a Namespace through System -> Namespaces. From there, you can give it a name and path. Because the Namespace name isoften used as part of a URL, MODx recommends to make the Namespace name lowercase; this helps avoid inconsistent behavior that may occurwith some webservers that may handle capitalization differently.
The following window is an example of what information makes up a Namespace:
In the path, you can also use placeholders for MODx paths:
{core_path} - Resolves to the . This is set in the at the root of the site, often it is MODX_CORE_PATH /config.core.php/home/username/modx_location/core/{base_path} - Resolves to the , often MODX_BASE_PATH /home/username/modx_location/
Using modAction and modMenu
Actions (modAction) and Menus (modMenu) work together to allow CMP developers to create Manager pages that directly hook into the defaultManager, without hacking the MODx core. modMenu objects are the actual menu items you see on the navigation bar in the Manager. modActionobjects tell the menu items to do, usually in the form of sending a request to a controller file.what
Creating the modAction
To create a modAction, go to System -> Actions. Right-click your Namespace from the 'Actions' tree and select "Create Action Here. The followingwindow will show something similar to the following:
1.
2. 3. 4.
5.
6.
There are several noteworthy fields here:
Controller: This is where you'll put the name of the controller file to look for. . For example, if inMake sure to leave off the .php extensionyour Namespace path, there is a index.php that you'll want to use to handle your CMPs, set this field to 'index'.Namespace: This is the name of the Namespace the new action belongs to. Make sure this is set to the Namespace for your Component.Parent Controller: This is currently only for organizational purposes, and poses no programmatic change if set to something else. Load Headers: If this is checked, MODx will load the Manager header and footer files. This is recommended unless you want to have acompletely separate view for your CMP.Language Topics: A comma-separated list of Lexicon Topics to load prior to the page load. They are in the standard loadingLexiconformat.Assets: A placeholder field for whatever you want to put in. It is not currently used in MODx Revolution 2.0.0.
MODx will automatically load for the Action any Lexicon Topics you specify in the "Language Topics" field you put here.
Creating the modMenu
From there, you can create your menu item by right clicking on an already-existing menu item (MODx recommends placing custom CMP's under'Components'), and clicking 'Place Action Here'. This will load a window where you can enter details for the new modMenu:
1.
2. 3. 4. 5.
6.
7.
There are also several noteworthy fields here:
Text: The Lexicon Entry key* that will be the menu item's displayed value. MODx will automatically load your Namespace's "default"Lexicon Topic, should it exist; so place your Lexicon Entry in that Topic.Description: The Lexicon entry key* that will be used for the menu item's description.Action: The action that connects your menu to the appropriate connector. Select the modAction you just created.Icon: This field is currently not in use in MODx Revolution 2.0.0.Parameters: Allows you to specify other GET parameters to be added to the Menu's href URL should you want to, e.g. . Steer&x=1&y=2clear of the variable and other . Note that each variable must be prepended with an ampersand (&); this isa Reserved Parametersbecause the contents of this field will simply be appended to a manager url, e.g. http://yourdomain.com/manager/index.php?a=65&x=1&y=2Handler: Allows you add a Javascript handler to execute instead of the default page loading action. If this is specified, the Menu willdefault to that handler and ignore the HREF attribute entirely. Use this if you just want to execute a JavaScript action instead of load apage.Permissions: If you'd like to restrict view access to this menu item, you can do so here. Just specify the name you'd wantPermissionusers to have to have to see this menu item.
The Lexicon Entry you specify for the Text or Description be in the "default" Topic for your Namespace.must
Returning the Page
Creating a CMP is very similar to a ; you'll just return the page content using the PHP 'return' statement. MODx recommends you do notSnippetuse 'echo', since this will load the page content before the headers have a chance to load.
Smarty
One way to create page content is by the use of templates. The MODx Manager is powered by Smarty, a templating engine whichSmartyfocuses on making it easy for developers to create their own custom templates. To use a template in the Manager, you simply use the following tooutput the content of your list.tpl onto the page:
return $modx->smarty->fetch( '/path/to/templates/list.tpl' );
One common use of Smarty is to assign MODx configuration settings and lexicons to a "placeholder" which can then be used in your templates.For example, in your controller file you might place the following code:
$modx->smarty->assign( '_lang', $ ->modx->lexicon->fetch() );this
1. 2.
1. 2.
$modx->smarty->assign() takes two parameters:
$tplVar [string] - The name of the placeholder.$value [string|array] - An string or associative array of data (key => value) to load into the placeholder.
Here's an example of using placeholders:
<h2 class= >"modx-page-header" {$_lang.mycomponent}</h2>
This would output a standard MODx Manager page header with the content of the lexicon matching the "mycomponent" key.
Plain-Old HTML
Of course, you don't need to use Smarty if you don't want to. One could simply return the HTML code in their controller, instead of calling$modx->smarty->fetch():
$o = '<div class= ><h2>My Component</h2></div>';"test"
$o;return
Scripts and CSS
Since ExtJS plays an important part in the MODx Manager, you will probably need to include your own JavaScript files for your components.
The best way to include a JavaScript file on your page is to use $modx->regClientStartupScript(). This function takes two parameters:
$src [string] - The path to your JavaScript file, or the content of the script to output.$plaintext [boolean] - Whether the $src content is a path to a file, or the actual script content. Defaults to false (file path).
You can also output custom CSS files in the same way. Simply use $modx->regClientCSS(), which accepts a single parameter: the path to yourCSS file.
However, you don't to use ExtJS in your Custom Manager Pages - you can use plain HTML, or another JS framework, if you like. If you dohavedecide to use another JS framework, MODx recommends that you not set "Load Headers" to true on the modAction, since this will load the ExtJSscripts. You'll need to create your own header file and output that with your normal output.
Brief Overview of MODExt
More info on , the ExtJS integration for MODx Revolution, can be found .MODExt here
Custom Connectors
A Connector is essentially a PHP file whose main purpose is to provide a connection between an AJAX-based request, and a Processor file.Since Processors are usually involved in CRUD (create, read, update, and delete) operations on a database, they should never be accesseddirectly. Instead, by using a Connector as a proxy to connect to a Processor, additional authentication and security checks can be performedbefore allowing access to the Processor.
Unlike Controllers, which are used in the Manager to display an actual page (and belong in your component's /core/ directory), Connectors mustbe HTTP-accessible. Therefore, it's best to place them in your component's /assets/ directory. Now, let's take a look at the structure of aConnector file.
The first thing your Connector must do is include the MODx configuration file, as well as the main MODx Connector file.
<?php$basePath = dirname( dirname( dirname( dirname( dirname( __FILE__ ) ) ) ) );
require_once $basePath . '/config.core.php';require_once MODX_CORE_PATH . 'config/' . MODX_CONFIG_KEY . '.inc.php';require_once MODX_CONNECTORS_PATH . 'index.php';
Next, we simply have to 'handle' the, or pass the request data on to the appropriate Processor file.
$modx->request->handleRequest( array( 'processors_path' => $modx->getOption( 'core_path' ) . 'components/mycomponent/processors/', 'location' => '') );?>
As you can see, handleRequest() accepts an array of options:
processors_path: The base directory where your component's processor files are found.location: The subdirectory of processors_path to find the processor.action: This tells MODx the filename of the Processor file to load. This value will be taken from the HTTP $_REQUEST['action']parameter.
That's all there is to it! Your AJAX requests simply need to call your Connector file (with an 'action' parameter referring to the appropriateProcessor file), and voila -- you can now use AJAX requests in your component!
Conclusion
CMPs allow developers to create custom manager interfaces for their MODx Components without hacking the core. They integrate seamlesslyinto the core MODx installation, and allow for entire custom applications to be built with MODx technologies.
See Also
Actions and Menus
MODx Revolution introduces an entirely new program structure for its core. The manager is also built on what are called and controllers templates, which use AJAX processing to send data to that access .connectors processors
The controllers are simply PHP files that load the correct Smarty template to display, and fetch any pre-render data for the template. Revolutionabstracts these controllers into the DB as modAction's, allowing 3rd party developers to easily create custom manager pages that 'hook' into thecurrent MODx system .without modifying the core
modAction's require a controller and a template to exist, that must be found in the manager/controllers and manager/templates directories. Theyhave a few certain parameters that are worth noting:
Controller - This points to the controller file. If the file is an index.php, you can leave that off and MODx will try and find it through a smartsearch.Load Headers - if checked, this will load the MODx header and footer for the internal page. If you are wanting just a blank page for themanager page, leave this unchecked.Language Topics - Language Topics are simply separations of language areas that allow for quicker i18n access. They can be found inthe core/lexicon/en (or fr,de,etc) directory, and new topics can easily be created simply by using the Lexicon Management section.
For example, 3rd party devs might want to create a Lexicon Topic named 'buttons' for TinyMCE, which would reference the topic incore/components/tinymce/lexicon/en/buttons.inc.php. They can simply either A) use a build script to install the lexicon via a transport package, orB) have users import the lexicon topics using the Import Lexicon utility in Lexicon Management.
You can then load the topic via:
$modx->lexicon->load('tinymce:buttons');
modAction's are also easily hooked to modMenu's, which are abstract representations of the top menubar in the manager. Again, this lets 3rdparty developers easily and quickly develop custom menu implementations for their components - or lets users rearrange the top menu.
These can all be managed via the Actions panel, which is found under the Tools menu.
Any changes to the order of 'core' menu items will be reverted during Revolution upgrades.
Related Pages
Custom Manager Pages
1. a. b. c.
2.
Internationalization
Custom Manager Pages Tutorial
GoalExplanation and Mental Preparation
What we'll need:Create the NamespaceMake the Controller FileCreate the ActionCreate the Menu ObjectMake your CMP Translatable (Optional)
Create a Lexicon DirectoryIdentify your Lexicon keyCreate the Topic FileCreate the Entries (Provide the Translations)
Troubleshooting / Errors
Goal
We want to add a custom page to the MODx Revolution manager that will load (i.e. execute) the contents of an PHP file that has been uploadedto the webserver. Technically, such a page is called a Custom Manager Page, or CMP; please refer to the page on for aCustom Manager Pagesmore detailed description.
In MODx Evolution (versions 1.x and earlier), this process was handled by "Modules", but those have been deprecated in Revolution.
Explanation and Mental Preparation
"What's the big deal?" you might ask. "Why can't I just add an anchor tag somewhere that links to my PHP file and be done with it?"
<a href= >My Custom Manager Page</a>"/path/to/my/file.php"
That should work, right? Well... maybe, but it's not that simple. There are a lot of moving parts that have to get connected to make this seeminglysimple task work. Allowing for internationalization and scalability requires that this process include several layers of abstraction that are notimmediately obvious. It goes far beyond what's possible with the simple anchor tag solution above. But rest assured, the extra steps will ensurethat the solution will be usable in a far greater number of scenarios.
What we'll need:
A PHP file on the webserver which generates the text for our CMP (a.k.a. the controller).A Namespace (i.e. a path) which defines a dedicated folder for our script(s).A clickable Menu object (modMenu) which associates the clickable link to the action.An Action object (modAction) which is an abstract representation of your file.And optionally, a Lexicon entry which would allow you to translate the label on your menu item.
Maybe you're baffled by the complexity here, and to be fair, for simple scenarios, this is a bit more complicated than is strictly required, but manyof you will find yourself at some point getting into more complicated use-cases at which point you'll realize " !!! THAT'S why they did it thisAHAway!" For now, just trust that the smart folks behind MODx put a lot of thought into how this was built, and there's a good reason that it is the wayit is. Onward.
Create the Namespace
You can think of the Namespace as a dedicated directory for your PHP file(s) that pertain to this particular manager page. Keep in mind thatcreating the Namespace inside the MODx manager does not actually create the directory; likewise, removing a Namespace from the manager willnot actually delete files and folders. When you "create" the namespace, you're just telling MODx about it.
In our example, we've chosen to call our namespace (and its folder) mycmp.
System->NamespacesClick Create NewName: mycmp (all lowercase, one word)Path: {core_path}components/mycmp/ (note the use of the system "core_path" placeholder, and remember to include thetrailing slash)
Using your FTP client, create a directory inside named .core/components mycmp
Watch out for typos! Make sure the Namespace path matches the directory name!
1. 2. 3. 4. 5.
1. 2.
a. b. c. d.
3.
Make the Controller File
For our first manager page, we're going to keep it simple. Create a file named which contains the following:index.php
<?php 'This is my first Custom Manager Page';return
?>
Upload the file to your MODx site into the directory (i.e. the Namespace) you've just created: core/components/mycmp/index.php
As a superficial check, you may want to try navigating to the file in a browser window: http://yourdomain.com/core/components/mycmp/index.php
Notice that we did NOT use , or , or raw HTML in our PHP. If you use any of these, you'll find that the text floats to theprint echotop of the page; remember that a Custom Manager Page is really acting as a function, so it should a value.return
Create the Action
The Action object identifies the location of your index.php file within the namespace.
System->ActionsRight-click from the list of namespaces and select "Create Action Here".mycmpController: index (this should match the name of your PHP file WITHOUT the .php extension)Check Load HeadersOptionally, enter in one or more Language topics (e.g. "mycmp:default" to load the default topic for the mycmp Namespace)
Create the Menu Object
System->Actions (in the same window where you created the Action)Right-Click "Components" and choose "Place Action Here"
Lexicon Key: My CMPDescription: My first custom manager pageAction: mynamespace - indexSave
Refresh your browser page.
After you edit the menu item, be sure to refresh the manager page. The menu item will not be visible until you refresh yourbrowser; likewise, any changes you make to an existing menu item will not be visible until you refresh the page.
If you add any GET Parameters to the menu item under System -> Actions, steer clear of any use of the variable or any other aReserved Parameters
You should now be able to click on the "Components" menu and see your menu item, and when you click it, you should see your message!
Make your CMP Translatable (Optional)
If you never intend on internationalizing your site, then you probably have no need to create a Lexicon entry. But if you do want to provide
1. 2. 3. 4. 5.
1. 2. 3. 4.
translations, the Lexicon is MODx's way of doing it. The Lexicon key is a unique identifier, e.g. 'My CMP' which can get translated into otherlanguages.
Create a Lexicon Directory
Go to your Namespace path (usually in your Extra's core/components/mycmp/ directory) and place a "lexicon/" directory in there. From there, addan 'en' directory as well ('en' for 'English' -- or use your language code of choice). You should have something like:
core/components/mycmp/lexicon/en/
Identify your Lexicon key
System->ActionsFind your Menu action in the menu on the right (under Components)Update MenuNote the 'Lexicon Key' field. Set it to 'mycmp'.Set the 'Description' field to 'mycmp.menu_desc'.
We need both a Language Topic and a Lexicon Key in order to define a Lexicon entry. By doing the above, you've now pointed your Action/Menuto use a particular Topic and Key, but you haven't yet defined them in the Lexicon. It's entirely possible to set up the Lexicon entries and thenfirstpoint your Action and Menu objects to reference them , but here we're assuming that you are adding Lexicon entries creating thesecond afterAction and Menu objects.
Create the Topic File
Create a file name in your new 'en' lexicon directory (i.e. ), and place yourdefault.inc.php core/components/mycmp/lexicon/en/default.inc.phpentries in them, in this format:
$_lang['lexicon_entry_key'] = 'Translation Entry';for
Create the Entries (Provide the Translations)
Go ahead and add these entries to :core/components/mycmp/lexicon/en/default.inc.php
$_lang['mycmp'] = 'My CMP';$_lang['mycmp.menu_desc'] = 'My custom manager page.';
Now, clear the site cache to reload the lexicon cache, via Site -> Clear Cache.
Troubleshooting / Errors
Having problems? Here are a couple things that you may have run into.
Did you make sure you created a directory on your webserver with the EXACT path as defined by your Namespace?Are you sure your controller file is using the statement instead of using or ?return print echoYour menu items aren't being translated? Be sure to clear your cache! Site->Clear CacheTranslations aren't appearing in your CMP? Make sure you specified the "lexicon" in the Action object (ie, "mycmp:default")
Reserved Parameters
Reserved GET Parameters Inside the MODx Manager
The following is a list (currently incomplete) of GET parameters used by the MODx manager. You should avoid using any of these parameters in :Custom Manager Pages
a – used to define an actioncontext_key – specifies one of your contexts (e.g. "web" or "mgr")class_key – specifies a class name, e.g. when creating a Weblink or static resourceid -- specifies a page_id
MODExt
What is MODExt?Commonly-Used Components
More MODExt ComponentsExtending a MODExt ClassSee Also
What is MODExt?
MODExt is an extension of the ExtJS 3.0 JavaScript Framework that provides extra, customized-to-MODx functionality. It drives MODxRevolution's manager interface, and it is also available to developers wanting to use it in their CMP development. A developer simply needs touse Ext.extend on the MODx.* class to instantly get the benefit of custom MODExt components.
Commonly-Used Components
There are a few components that are used throughout the MODx Manager, and will likely be used in CMPs
More MODExt Components
Of course, there are more MODExt components at your disposal. For a full list (and source code to examine), browse to the directory of your MODx installation.manager/assets/modext/widgets/core/
Extending a MODExt Class
Extending a MODExt component is actually quite simple. Let's extend the MODx.grid.Grid class to create our own custom grid, complete with acustom toolbar.
First, create a new JavaScript file and place the following code:
MyComponent.grid.MyGrid = function( config ) { /* parent constructor */Class MyComponent.grid.MyGrid.superclass.constructor.call( , config );this};Ext.extend( MyComponent.grid.MyGrid, MODx.grid.Grid, { /* members will go here */Class} );/* Register as an xtype */"mycomponent-grid-mygrid"Ext.reg( , MyComponent.grid.MyGrid );"mycomponent-grid-mygrid"
Here, we've created our own class (MyComponent.grid.MyGrid) which extends MODx.grid.Grid. We have also registered"mycomponent-grid-mygrid" as an Ext xtype, which can be used to display this grid in a FormPanel or other component. It has no additionalfunctionality -- yet!
Now, let's add some configuration options:
MyComponent.grid.MyGrid = function( config ) { config = config || {}; /* Grid configuration options */ Ext.applyIf( config, { id : ,"mycomponent-grid-mygrid" title : _( ),"my_grid" url : MyComponent.config.connectors_url + ,"list.php" baseParams : { action : "getlist" }, paging : ,true autosave : ,true remoteSort : ,true /* Store field list */ fields : [ { name : ,"id" type : " "int }, { name : ,"name" type : "string" }, { name : "menu" } ], /* Grid ColumnModel */ columns : [ { header : _( ),"id" dataIndex : ,"id" sortable : true }, { header : _( ),"name" dataIndex : ,"name" sortable : true } ], /* Top toolbar */ tbar : [ { xtype : ,"button" text : _( ),"create" handler : { xtype : ,"mycomponent-window-create" blankValues : true }, scope : this } ] } ); /* parent constructor */Class MyComponent.grid.MyGrid.superclass.constructor.call( , config );this};
Ext.extend( MyComponent.grid.MyGrid, MODx.grid.Grid, { /* members will go here */Class} );
/* Register as an xtype */"mycomponent-grid-mygrid"Ext.reg( , MyComponent.grid.MyGrid );"mycomponent-grid-mygrid"
Our basic configuration sets the grid up to work with a "list" connector, using the "getlist" action parameter. It also sets up paging, sorting, andenables "autosave" functionality so that whenever a record is changed, it's automatically updated in the database.
We then set up our fields (id, name, and menu), and our ColumnModel which references the fields in our store.
Lastly, we create the top toolbar, consisting of a button. The handler creates a window used for creating a record to add to our database.
See Also
ExtJS 3.0 API Documentation
MODx.combo.ComboBox
MODx.combo.ComboBox
Extends: Ext.form.ComboBox Remote and local data stores; grid renderer.Key Features:
The MODExt ComboBox class contains all of the functionality of a regular Ext ComboBox. It may be populated remotely by an array of JSONobjects from a connector (default), or locally (using a basic Javascript array or an Ext ArrayStore, with the "mode" config option set to "local").
One unique feature of the MODx ComboBox class is the built-in renderer for grids. It allows developers to use a ComboBox as a grid editor, andautomatically takes care of displaying the correct displayValue in the grid cell:
MODx.FormPanel
MODx.FormPanel
Extends: Ext.FormPanel Drag-and-drop functionality; changed ("dirty") field checking functionality; connector functionality.Key Features:
FormPanels are found throughout the MODx Manager. They can contain form fields, Grids, Trees - just about any component available.
MODx.grid.Grid
MODx.grid.Grid
Extends: Ext.grid.EditorGridPanel Connector functionality; easily integrate toolbar items and MODx.Window; built-in context menu functionality.Key Features:
MODExt Grids are used to display tabular data, complete with a ColumnModel, top toolbar (tbar) and bottom toolbar (bbar). It has built-in support
for paging as well. Grids are populated remotely from a connector request returning a JSON object. Displaying a right-click context menu for eachrow can easily be achieved by including a "menu" key for each data row in your processor:
foreach( $items as $item ) { $data[] = array( 'id' => $obj->get( 'id' ), 'name' => $obj->get( 'name' ), 'menu' => array( array( 'text' => $modx->lexicon( 'my_lexicon' ), 'handler' => ' .myHandler'this ) ) );}
The above code would create a context menu for each item with the text being the lexicon key matching "my_lexicon," and the handler being themyHandler function registered to your Grid object.
See Also
MODx.grid.LocalGridMODExt
MODx.grid.LocalGrid
MODx.grid.LocalGrid
Extends: Ext.grid.EditorGridPanel Similar to MODx.grid.Grid.Key Features:
The MODExt LocalGrid class is similar to the MODx.grid.Grid class, however rather than using a connector to populate it with data, it must beloaded through a local store.
See Also
MODx.grid.GridMODExt
MODx.Msg
MODx.Msg
Extends: Ext.Component AJAX connector features.Key Features:
The MODx.Msg class provides the functionality of the Ext.MessageBox class, with the added benefit of using an AJAX callback function (forconfirmation dialogs). Simply provide a URL and optional parameters and a connector request will be sent after the user confirms the prompt.
MODx.tree.Tree
MODx.tree.Tree
Extends: Ext.tree.TreePanel Remotely-loaded toolbars; drag-and-drop to form fields functionality; connector functionality for removing and dragging/sorting.Key Features:
Trees provide a quick and easy way to display multiple levels of objects which have a parent-child relationship, such as users or resources.
MODx.Window
MODx.Window
Extends: Ext.Window Drag-and-drop functionality; connector functionality for saving.Key Features:
MODExt Windows are a convenient way to display record data from a Grid or AJAX request for editing. Windows automatically include aFormPanel which you can add form fields (and other components) to. Submitting/saving a Window actually submits the FormPanel, and initiatesan AJAX request to your connector.
Internationalization
An Overview
Lexicon EntriesLoading and Using Lexicons
Lexicons Via TagLexicons in Code
modLexicon::load()modX::lexicon()
Lexicons with PlaceholdersLexicons for SettingsConclusionSee Also
An Overview
Internationalization, or i18n, is the process of extrapolating text strings on a document to separate languages, so that the document may beviewed by a multitude of different languages without having to duplicate the page for every different language.
MODx Revolution supports i18n at the core level, through what it calls "Lexicons". A lexicon is simply a collection of the following:
Languages (IANA format)TopicsEntries
A Lexicon Topic is a collection of Lexicon Entries. A Lexicon Entry is one single language string, with a key and a value. Revolution separatesEntries into Topics to make for quicker language file loading, lower JS language cache load times, and ease of maintenance.
Furthermore, the modNamespace class is used to further separate Lexicon Topics into separate namespaces, preventing you from accidentallyoverwriting a core lexicon.
Lexicon Entries
A Lexicon Entry (or modLexiconEntry in the MODx model) is simply a single translation of a string into another language. It has a few importantfields we'll note:
name - This is the name, or "key" of the Entry. When using Lexicons, this is how you will reference this key.value - The translation of the key.topic - The topic that this entry belongs to.language - The IANA key of the language this Entry is translated into.
Loading and Using Lexicons
Lexicons must first be loaded if they are to be used in the front-end; however, this is a trivial process.
Lexicons Via Tag
To use a Lexicon Entry in a tag, simply do:
[[%key? &topic=`topicname` &namespace=`namespace_name` &language=`en`]]
The 'language', 'topic', and 'namespace' properties are optional; if the tag has been run earlier on the page with the same 'topic' property value,that topic will have already been loaded. If 'topic' is not specified, it will assume 'default'. If 'namespace' is not specified, it will assume 'core', or theMODx Revolution Core Namespace.
It is preferable not to use the 'language' property for every tag should you be changing languages; this is best done through aSystem or Context Setting for the entire site or context. The best option is different contexts for each language. But again,MODx leaves you with the preference.
Lexicons in Code
Using lexicons in code is fairly simple; first off you'll want to make sure the modLexicon class is loaded by instantiating it as a service:
$modx->getService('lexicon','modLexicon');
Then we'll want to load the Topic using the load() method.
modLexicon::load()
The syntax for the modLexicon::load method is pretty simple:
$modx->lexicon->load('topicname');
The load() function supports Namespace-specific loading. So, say you had a Lexicon Topic named 'default' in a Namespace called 'school'. You'dsimply load it like so:
$modx->lexicon->load('school: ');default
This would load the 'default' Topic in the 'school' Namespace. If the Namespace is not specified, it defaults to 'core', which is the defaultNamespace for the MODx Revolution backend.
The load() function also takes an infinite number of parameters; each parameter loads a separate Topic. Example:
$modx->lexicon->load('chunk','user','school:playground');
This would load 3 Topics: 'chunk', 'user', and the 'playground' Topic from the 'school' Namespace.
Furthermore, the load parameter supports language-specific loading, should you want to override the default language that is being loaded (whichdefaults to the current value of $this->modx->cultureKey, which is set differently depending on the Context loaded, and can be set via Settings),you could load it like so:
$modx->lexicon->load('es:school:playground');
This would load the Spanish version of the 'playground' Topic for the 'school' Namespace. Fun, huh?
modX::lexicon()
Now we can use the lexicon() method on the MODx object to get our Entry with key 'school.basketball':
$modx->lexicon('school.basketball');
Lexicons with Placeholders
Say we wanted to load a Lexicon Entry with some dynamic values we have in our page. For example, say we want a greeting on a website, thatsays, "Hello, John!" if John is the username of the currently logged in user. Our Lexicon Entry "welcome_message" value would look like this:
Hello, [[+name]]!
And then in tag form:
[[!%welcome_message? &name=`John`]]
Or in PHP:
$modx->lexicon('welcome_message',array('name' => 'John'));
You can have an infinite number of placeholders for each Tag.
Note our ! prefix for the Tag; this makes sure the Tag isn't cached, since our string might be changing before the page cachedoes.
Lexicons for Settings
So say you're creating System Settings for your 3rd Party Component (3PC). The syntax for auto-loading them into the Revolution Settings grid issimple. Let's say we have a Namespace for our Component called 'gallery', and a setting called 'gallery.display_thumbs'
Recommended FormatThe recommended format for 3PC developers is to use a prefix which identifies the parent component:
$_lang[' .key-name'] = 'Your translation here.';name-of-component
This helps to prevent name collisions; keep in mind that the array may have thousands of entries, so you want to make$_langsure each entry is unique.
To add a lexicon name and description, we'd simply add the following 2 strings into our 'default' Lexicon Topic for our 'gallery' Namespace:
$_lang['setting_gallery.display_thumbs'] = 'Display Thumbnails';$_lang['setting_gallery.display_thumbs_desc'] = 'When set to , will display thumbnails true this forthe gallery.';
And we're done!
Conclusion
Lexicons provide MODx Revolution users with a plethora of avenues and options to do their i18n work. Lexicons are composed of multiple Entriesfor each Language, and are grouped into Topics. They can be called by PHP method calls, or by MODx Tags.
See Also
modX.lexicon
Creating Lexicons for Your Components
MODx Services
What is a Service?
A service is any object that is loaded via . It can be a custom class provided by the user, or by MODx itself.$modx->getService
Once an object is loaded with getService, it is accessible via $modx->(servicename). So, for example:
$modx->getService('twitter','myTwitter','/path/to/twitter/model/',array( 'api_key' => 3212423,)); $modx->twitter->tweet('Success!');
What are the Default Included Services?
A list of the core-included MODx Services is as follows:
See Also
modX.getService
modMail
What is modMail?
modMail is an abstract class that can be extended to provide mail services for Revolution. It cannot be run by itself, but must be extended with animplementation class (such as PHPMailer).
What is modPHPMailer?
modPHPMailer is a class that extends modMail to provide an implementation for the open source PHPMailer class.
Usage
Let's say we have an email template in the Chunk 'myEmailTemplate'. We want to send it via mail to [email protected], with the Fromaddress being '[email protected]'. We also want it to be an HTML email. Here's how we'd do it:
$message = $modx->getChunk('myEmailTemplate');
$modx->getService('mail', 'mail.modPHPMailer');$modx->mail->set(modMail::MAIL_BODY,$message);$modx->mail->set(modMail::MAIL_FROM,'[email protected]');$modx->mail->set(modMail::MAIL_FROM_NAME,'Johnny Tester');$modx->mail->set(modMail::MAIL_SENDER,'Johnny Tester');$modx->mail->set(modMail::MAIL_SUBJECT,'Check out my email template!');new$modx->mail->address('to','[email protected]');$modx->mail->address('reply-to','[email protected]');$modx->mail->setHTML( );true
(!$modx->mail->send()) {if $modx->log(modX::LOG_LEVEL_ERROR,'An error occurred trying to send the email: '.$err);while}$modx->mail->reset();
Simple, no?
Note that we have to reset() if we want to send mail again; this resets all the fields to blank. Also, the fields above are (just likeoptionalPHPMailer), so that if you didn't want to specify a 'reply-to' (though we recommend it!) you can.
Also, if you want to send the email to multiple addresses, you can simply call address('to') again, like so:
$modx->mail->address('to','[email protected]');$modx->mail->address('to','[email protected]');
And finally, the example code above will send a message to our error.log if the mail isn't sent for some reason (usually a server misconfiguration).
What if I want to use another email class?
Simple - just extend modMail with that class, then load your class via . You'll get all the modMail functionality, but you will have togetServiceprovide the wrapper class (like modPHPMailer) to do so.
See Also
MODx ServicesmodX.getService
Package Management
MODx Revolution introduces what are called , which are compiled zips of almost anything - from snippets, components,Transport Packagesmanager templates, to the core itself.
Revolution also has , which are download locations that allow for downloading packages straight from within the MODx manager itself.Providers
Downloading PackagesInstalling PackagesUpdating PackagesUninstalling PackagesSee Also
Downloading Packages
You have a few options: you can download remotely via the Provider option, by selecting the modxcms.com provider from the menu (or just byclicking 'Download Extras' in the grid toolbar).
To download the packages, simply select the package you wish to download and click the "Download" button.
Or, packages can be downloaded directly from a browser via MODx's Extras section, located at . The package zipshttp://modxcms.com/extras/are loaded simply by uploading them to your core/packages/ directory, and then running the Package Management section of the manager. Fromthere, click on "Add New Package", and select the "Search Locally for Packages" option. MODx will then scan the core package directory, andadd any packages you have.
Downloading Packages requires you to either have cURL or sockets installed on your web server. If you do not have theseinstalled, the list of packages will show blank.
The Official Provider of modxcms.com has a URL of:http://rest.modxcms.com/extras/and comes packaged in with MODx Revolution 2.0.0.
Installing Packages
You can easily install packages by right-clicking on the package and clicking "Install". A console will load showing you the details of the packageinstallation.
If the package should have a License Agreement, you'll need to agree to it before you can proceed. Also, the package might provide a READMEfile for you to purvey before installing.
Finally, the package may or may not have some pre-install options and settings for you to set, such as:
The package should then install on your MODx installation.
Updating Packages
You can easily update any package that has been downloaded from a provider. Simply click the 'Check for Updates' context menu item (afterright-clicking on the package), and MODx will load a window showing any newer versions. Should your package be already up-to-date, amessage will appear.
You can then select the version you would like to install, and MODx will download the package and start the install process.
Now, if you want to revert back, you'll simply uninstall the package, and click the 'Revert' option, which will revert back to the prior package thatwas installed.
Uninstalling Packages
You can click on any package to either remove or uninstall a package. a package removes the zip file entirely from your core/packagesRemoving
directory. Uninstall simply uninstalls it.
Note the three modes when you uninstall a package:
Each is self-explanatory.
See Also
Transport Packages
What is a Transport Package?The Internals of a Transport PackageThe manifest.php fileOkay, what are these Vehicles?
Inside a Vehicle's SourceResolvers and Validators
A ValidatorA Resolver
UsageRelated Pages
What is a Transport Package?
A Transport Package is a collection of objects and files that can be used to "transport" data from one MODx installation to another; or even totransport 3rd-Party Components in a simple, easily-manageable format. In other words, Transport Packages can transport nearly - fromanythingdatabase data, files and even scripts to run during its install.
Transport Packages also allow for versioning, in that they match based on a simple format, complying with PHP version number standards:
packagename-version-release.transport.zip
So, an example Transport Package might be "myextra-1.0-rc1.transport.zip". If you were to upload a "myextra-1.0-rc2.transport.zip", MODx wouldthen interpret this as part of the same "package" but a newer version of it. It would then behave in "upgrade" mode.
Transport packages are stored in .zip files, ending with ".transport.zip". They can be uploaded and installed anywhere there is a MODx Revolutioninstance - regardless of the server configuration.
The Internals of a Transport Package
MODx Revolution automatically "unpacks", or unzips, your transport packages for you. Once done, a subdirectory in your core/packages directorywill appear with the name of the zip file (minus ".transport.zip"). This directory will contain:
A manifest.php fileSubdirectories of each Vehicle (more on those later)
It may also contain a "preserved.php" file, if the package is an upgrade from a prior package, which contains the metadata for the install to berestored. And finally, there might be a 'setup-options.php' file if the package has packaged one inside.
The manifest.php file
The manifest basically stores all the relevant information for the package, including the locations of files and information about them. If you openthe manifest.php file, you'll see that it contains a giant PHP array being returned. Within that are some keys you might be interested in:
manifest-version - This tells us what version the manifest definition is. MODx uses it to determine how to interpret the manifest andmake it easier for future MODx versions to be backwards-compatible.
manifest-attributes - These are any custom attributes that were set on the package when it was being built. The most common are'license', 'readme' and 'setup-options', which MODx interprets during install time.
manifest-vehicles - These are the Vehicles metadata, in array format.
Okay, what are these Vehicles?
Transport Vehicles are the parts of a Transport Package. A package can contain as many Vehicles as it likes. Vehicles also come in differenttypes; the currently available ones are:
xPDOObjectVehicle - For transporting database dataxPDOFileVehicle - For transporting files
In the 'manifest-vehicles' array, you'll see these keys for each vehicle:
vehicle_package - This tells us what type of package is holding these vehicles. Currently the only type is 'transport'.vehicle_class - The class name of the type of Vehicle this is.class - The class name of the DB object being transported, or xPDOFileVehicle if it's a file vehicle.guid - A randomly generated GUID for the vehicle.native_key - If the vehicle is a database object, this will be its primary key by which it is identified.filename - Where the vehicle's source file can be found within the transport package's folder.namespace - Certain packages use the 'namespace' field to group vehicles and other objects to make them uniquely identifiable within aMODx installation.
So now that we've seen what the vehicles represent in the manifest, let's open up a Vehicle by looking a filename and diving in.
Inside a Vehicle's Source
Vehicles can actually have a few different files grouped with them, but we'll first concern ourselves with the main vehicle file, which is specified inthe manifest and often ends with '.vehicle'.
Again, it looks like a big PHP array, with similar keys. It has some extra keys though, which are important. xPDOFileVehicle andxPDOObjectVehicle can have different keys. Let's go over the common ones:
class - Similar to the manifest, the class type of the vehicle.object - An array that contains the object information. For DB objects this will most likely be a JSON array representation of the DB table.For files, it will be a PHP array with the source, target and name of the vehicle.vehicle_class - Similar to the manifest, the class name of the vehicle.vehicle_package - Similar to the manifest, the transport type of the vehicle.guid - Similar to the manifest, a unique identifier for the vehicle.package - Only applicable to xPDOObjectVehicles, this will most likely be 'modx' or blank.signature - The filename signature for this vehicle.native_key - Similar to the manifest. If the vehicle is a database object, this will be its primary key by which it is identified.
The xPDOObjectVehicle, or database vehicles, often have these extra keys:
preserve_keys - If true, the vehicle will try and preserve the primary key of the database record on install.update_object - If true, the vehicle will UPDATE the object if it's found already in the database during install. If false, it will be skipped.unique_key - The column name by which the database object can be uniquely identified - often this is not the primary key, asauto-incrementing fields often do not match across different databases.related_objects - A complex array of any related objects to this vehicle's main database object. Sometimes, it may be necessary topackage in "related" objects to achieve the desired end result. A great example is if the packager wants to put all of his Snippets in aCategory. He would make the vehicle's object be the Category, and then add related objects - the snippets - to it.related_object_attributes - The attributes for the above related objects.namespace - Similar to the manifest; a grouping value for the objects in a transport package.
There are also some optional ones, which may or may not be set:
validate - An array of arrays which contain validators, explained later.resolve - An array of arrays which contain resolvers, explained later.
In xPDOFileVehicles, you will also see a directory with the same filename as the vehicle, minus the ".vehicle". If you open it, there will be the filesfor the vehicle.
1.
2.
Resolvers and Validators
What are resolvers and validators? Well, think of them like pre and post installation scripts. They are, in essence, PHP scripts. (In fact, if you openthem up, they look exactly like PHP scripts.) They are named the same filename as the vehicle, but are postfixed with ".resolver" or ".validator".
A Validator
A validator is executed the Vehicle is installed, upgraded or uninstalled. If they return false, the Vehicle is not installed, and is skipped.before
They are useful for determining whether or not the Vehicle should still be installed, uninstalled or upgraded in the package process. For example -if you want to have dependencies and not have a Vehicle installed unless something else is found, a Validator would be a great place for it.
A Resolver
Resolvers are executed the Vehicle is installed, upgraded or uninstalled. Each will execute in turn regardless of any other resolver results.after
Resolvers are useful for 'cleaning up' after a Vehicle is installed, or setting custom configuration options (such as ones setup in Setup Optionsduring install).
Usage
Transport Packages can be managed in the section of the Revolution manager. They can be added to the RevolutionPackage Managementinstance by either:
Uploading the file manually to core/packages/, and then clicking "Add New Package" and selecting the "Search Locally for Packages"optionDownloading the package from a . This allows updates to be remotely downloaded for a package as well.Transport Provider
Once downloaded, they can be installed by right-clicking a package in the grid, and clicking Install. This will prompt the user to accept a LicenseAgreement should the package come with one, and prompting to read the README should the package contain one. Then it will present a formwith pre-installation options, which may or may not exist depending on the package. The user can then click 'Install' to install the package.
Once installed, the user can uninstall the package at any time. Also, if the package was downloaded from a , then the user canTransport Providercheck for updates for the package.
Related Pages
Package ManagementProvidersTutorial: Creating a 3rd Party Component Build Script
Providers
What is a Transport Provider?UsageRelated Pages
What is a Transport Provider?
Transport Providers in MODx are remote sources that one can download Transport Packages from. Simply by specifying a service URL, you caneasily hook into the Transport Provider and grab the latest Transport Packages easily from it.
MODx supports an unlimited number of Transport Providers, and each one can be from any source.
MODx recommends not downloading Transport Packages from providers you cannot verify or do not trust. We recommend themodxcms.com Official Provider at: http://rest.modxcms.com/extras/
Usage
To setup a Transport Provider, simply go to the Package Management page, and from there click on the 'Providers' panel heading at the bottom.This will open up a grid of Providers, which you can manage easily.
From there, you can click "Add New Provider" to add another, or right-click on any provider to get more options. Providers must be valid JSONfiles, web-accessible, and in the correct Provider format. Note that the Extras section of modxcms.com is a Provider.
The Service URL is the actual, absolute location of the provider file.
Once you have a provider, you can connect to it by going up to the Packages grid, clicking "Add New Provider", and then select the "Select aProvider" option. This will bring up a dropdown of Provider options:
Once you've selected your provider, click Next, and the download tree will be populated with the contents of that Provider's payload (ie, data).This will show you a tree of Package Versions you can download:
Related Pages
Creating a 3rd Party Component Build Script
Directory StructureStarting the Build ScriptPackaging in ObjectsValidators and ResolversLexiconsPackage Attributes: License, Readme and Setup OptionsRelated Pages
Users using Revolution 2.0.0-beta-4 or earlier should note that the defines are different in beta5 and onward. An example:xPDOTransport::UNIQUE_KEYS in beta5+ is XPDO_TRANSPORT_UNIQUE_KEYS in beta4 and earlier. MODx recommendsto just update to beta5/SVN.
A build script. What is that, you might ask? This is the meat of the packaging process; here is where your component is actually put into the nice,neat .zip transport package that you find on modxcms.com or through Revolution's Package Management section.
This tutorial will guide you through how to create one of those scripts. We'll be using a sample component called Quip, which contains amodAction, a few menus, some chunks and a snippet, lexicons, setup options, a license, a readme, and system settings. It's basically a quick,easy run through of all the basics to creating a fundamental build script.
Directory Structure
First off, let's take a quick look at our directory structure. This isn't always how you have to do it - this one is specifically built this way for SVN; butit's definitely recommended, especially with the and structures, since that makes creating theassets/components/quip/ core/components/quip/transport package much easier.
Starting the Build Script
Let's first start with some phpdoc comments at the top, and then start the timer.
<?php/** * Quip build script * * @ quippackage * @subpackage build */$mtime = microtime();$mtime = explode( , $mtime);" "$mtime = $mtime[1] + $mtime[0];$tstart = $mtime;set_time_limit(0); /* makes sure our script doesnt timeout */
Now let's define some basic paths. We can define these up top into a "sources" array to make them easier to reach later in the build script. Notehow the 'source_core' and 'source_assets' directories do post-fix a foreslash onto their paths. This is required.not
$root = dirname(dirname(__FILE__)).'/';$sources= array ( 'root' => $root, 'build' => $root .'_build/', 'resolvers' => $root . '_build/resolvers/', 'data' => $root . '_build/data/', 'source_core' => $root.'core/components/quip', 'lexicon' => $root . 'core/components/quip/lexicon/', 'source_assets' => $root.'assets/components/quip', 'docs' => $root.'core/components/quip/docs/',);unset($root); /* save memory */
Now, we'll need to include some files to get the build libraries we'll need. First, let's include a file we'll create called 'build.config.php' in our builddir.
require_once dirname(__FILE__) . '/build.config.php';
In this file, we'll want to define the location of our MODx Revolution installation so that the build script can know where to get the modX class, aswell as where to put the package when finished. Our file will look somewhat like this:
<?php/** * Define the MODX path constants necessary core installationfor * * @ quippackage * @subpackage build */define('MODX_CORE_PATH', '/absolute/path/to/modx/core/');define('MODX_CONFIG_KEY','config');
You'll want to make sure to change the value of MODX_CORE_PATH to the absolute path of where your MODx Revolution core is installed.MODX_CONFIG_KEY can stay the same, unless you're doing a multi-domain install.
Now, you'll want to include the modX class, and instantiate it. We'll also initialize it into the 'mgr' context, and set the log output to HTML to makeour errors and info messages nice and formatted - unless we're doing this from the cmd line, where we'll want just standard echo messages.
require_once MODX_CORE_PATH . 'model/modx/modx.class.php';
$modx= modX();new$modx->initialize('mgr');$modx->setLogLevel(modX::LOG_LEVEL_INFO);$modx->setLogTarget(XPDO_CLI_MODE ? 'ECHO' : 'HTML');
Okay, it's time for the meat. Let's first off use $modx->loadClass to load the modPackageBuilder class. Then we'll instantiate an instance of it, andcreate a package.
$modx->loadClass('transport.modPackageBuilder','', , );false true$builder = modPackageBuilder($modx);new$builder->createPackage('quip','0.1','alpha7');$builder->registerNamespace('quip', , ,'{core_path}components/quip/');false true
The modPackageBuilder::createPackage function has 3 parameters:, , and . For us,name version release
we'll be doing quip-0.1-alpha7, so let's go with that.
Next, we'll register a to this package. Not all packages need ; but all 3rd Party Components do. Basically, a NamespaceNamespace Namespacesis an organizing tool for MODx so that MODx can know what objects are tied to what package. This is helpful later on should we want to uninstallour package; we'd want it to remove the objects we'd install.
Plus, should we want to add any to this package (which we will), MODx does so by relating it to it's Namespace. Our packageLexicon Entriesbuilder will assign our Lexicon Entries to the Namespace, so we can easily manage our Lexicon Entries; not any others.just
Packaging in Objects
Objects are packaged as in MODx Revolution; basically think of a vehicle as a sort of storage system that transports the data and/or filesVehiclesinto the zip package. Packages can contain many vehicles; vehicles can contain many objectsor files - however, vehicles that contain an objectmust only have one reference object (or parent object, whichever you prefer) that the vehicle is based off of.
So, let's look at some examples for creating a vehicle before digging into our build script. This first example packages in a simple object, withsome parameters:
$snippet = $modx->newObject('modSnippet');$snippet->set('id',1);$snippet->set('name','Test');$vehicle = $builder->createVehicle($snippet,array( xPDOTransport::UNIQUE_KEY => 'name', xPDOTransport::UPDATE_OBJECT => ,true xPDOTransport::PRESERVE_KEYS => ,false));
So, first off, we created a snippet object. Note that you'll have to specify an arbitrary ID for it, even though we wont keep it later. This is required.Then, we used the 'createVehicle' function in modPackageBuilder to create the vehicle object. Let's look at those attributes options more closely:
xPDOTransport::UNIQUE_KEY - Here you'd place the unique key that identifies the object you're creating. This will tell(string/array)MODx to search for the modSnippet with the 'name' equal to the packaged in name (here, 'Test') when updating or removing the object.For most objects, this will be 'name'; others require different settings. Some might even require an array of two or more fields.xPDOTransport::UPDATE_OBJECT - Either true or false, this tells MODx whether or not to update the object if it is found in(boolean)the DB upon install (or update). Sometimes, if the object is already there, you may not want to update it - the update might erase theuser's current settings for that object.xPDOTransport::PRESERVE_KEYS - Either true or false, this tells MODx whether or not to rewrite the primary keys when the(boolean)object is found. This can be useful if you're wanting the PKs to stay the same when you update - some PKs are auto_increment, and ifyou're wanting those to stay the same number, you'd set this to true. Note: If the object already exists, this feature only works ifxPDOTransport::UPDATE_OBJECT is set to true as well. If the object is not found, it will work regardless.
Simple enough? So our example tells it to look for a Snippet named 'Test', and if it finds it, update its contents. If it doesnt find it, create it.However, if it does find it; we told MODx not to update its PK - there's no need to adjust that in this situation.
Now, what about related objects? What if I want to package in my modMenu, along with its Action associated with the modMenu? Here's a bitmore complex scenario:
$action= $modx->newObject('modAction');$action->fromArray(array( 'id' => 1, 'namespace' => 'quip', 'parent' => '0', 'controller' => 'index', 'haslayout' => '1', 'lang_topics' => 'quip: ,file',default 'assets' => '',),'', , );true true$menu= $modx->newObject('modMenu');$menu->fromArray(array( 'text' => 'quip', 'parent' => 'components', 'description' => 'quip_desc', 'icon' => 'images/icons/plugin.gif', 'menuindex' => '0', 'params' => '', 'handler' => '',),'', , );true true$menu->addOne($action);$vehicle= $builder->createVehicle($menu,array ( xPDOTransport::PRESERVE_KEYS => ,true xPDOTransport::UPDATE_OBJECT => ,true xPDOTransport::UNIQUE_KEY => 'text', xPDOTransport::RELATED_OBJECTS => ,true xPDOTransport::RELATED_OBJECT_ATTRIBUTES => array ( 'Action' => array ( xPDOTransport::PRESERVE_KEYS => ,false xPDOTransport::UPDATE_OBJECT => ,true xPDOTransport::UNIQUE_KEY => array ('namespace','controller'), ), ),));
Okay, a bit more meat here. We're introducing 2 new parameters:
xPDOTransport::RELATED_OBJECTS - Either true or false, this will tell MODx we want to search for related objects to this(boolean)object. This must be set for the next parameter to work.xPDOTransport::RELATED_OBJECT_ATTRIBUTES - This defines the types and details of the related objects we want to grab.(array)If you note, the format is simply an associative array of attributes - similar to the parent object's attributes - where the key is the "alias" ofthe related object we want to grab. The aliases can be found in the Schema, located in .core/model/schema/modx.mysql.schema.xml
So our example above tells us on the modAction (found by looking for the modAction with a namespace of 'quip' and a controller of 'index') toinclude the related modAction object that we package in. We packaged them in manually using xPDO's addOne function on the modAction.
Also, if we wanted to package in related objects to the modAction objects, we would just have had to define that in the 'Action' attributes andaddMany (or addOne) on that action. You can go however deep in nesting that you want.
So, back to our script. To recap, so far we have:
<?php/** * Quip build script * * @ quippackage * @subpackage build */$mtime = microtime();$mtime = explode( , $mtime);" "$mtime = $mtime[1] + $mtime[0];$tstart = $mtime;set_time_limit(0);
$root = dirname(dirname(__FILE__)).'/';$sources= array ( 'root' => $root, 'build' => $root .'_build/', 'lexicon' => $root . '_build/lexicon/', 'resolvers' => $root . '_build/resolvers/', 'data' => $root . '_build/data/', 'source_core' => $root.'core/components/quip', 'source_assets' => $root.'assets/components/quip', 'docs' => $root.'core/components/quip/docs/',);unset($root);
/* override with your own defines here (see build.config.sample.php) */require_once dirname(__FILE__) . '/build.config.php';require_once MODX_CORE_PATH . 'model/modx/modx.class.php';
$modx= modX();new$modx->initialize('mgr');$modx->setLogLevel(modX::LOG_LEVEL_INFO);$modx->setLogTarget(XPDO_CLI_MODE ? 'ECHO' : 'HTML');
$modx->loadClass('transport.modPackageBuilder','', , );false true$builder = modPackageBuilder($modx);new$builder->createPackage('quip','0.1','alpha5');$builder->registerNamespace('quip', , ,'{core_path}components/quip/');false true
So, let's first package in our modActions and modMenus for our backend:
/* load action/menu */$menu = include $sources['data'].'transport.menu.php';
$vehicle= $builder->createVehicle($menu,array ( xPDOTransport::PRESERVE_KEYS => ,true xPDOTransport::UPDATE_OBJECT => ,true xPDOTransport::UNIQUE_KEY => 'text', xPDOTransport::RELATED_OBJECTS => ,true xPDOTransport::RELATED_OBJECT_ATTRIBUTES => array ( 'Action' => array ( xPDOTransport::PRESERVE_KEYS => ,false xPDOTransport::UPDATE_OBJECT => ,true xPDOTransport::UNIQUE_KEY => array ('namespace','controller'), ), ),));$builder->putVehicle($vehicle);unset($vehicle,$action); /* to keep memory low */
Wait! Notice how I put the action data in a different file? You don't have to do this - it's completely personal preference - but it does keep our buildscript clean, and isolate our actions/menus to a separate file for easy management.
Let's do the same with our system settings:
/* load system settings */$settings = include $sources['data'].'transport.settings.php';
$attributes= array( xPDOTransport::UNIQUE_KEY => 'key', xPDOTransport::PRESERVE_KEYS => ,true xPDOTransport::UPDATE_OBJECT => ,false);foreach ($settings as $setting) { $vehicle = $builder->createVehicle($setting,$attributes); $builder->putVehicle($vehicle);}unset($settings,$setting,$attributes);
Great! We've got our actions, menus and settings packaged in. Now, using our newfound knowledge about related objects, let's create a categorycalled 'Quip' and put our Snippet and Chunks in that category. We'll go through this a bit slower, so we can easily see how this works:
/* create category */$category= $modx->newObject('modCategory');$category->set('id',1);$category->set('category','Quip');
Okay, great. Step one done: category created. Now about that Snippet:
/* create the snippet */$snippet= $modx->newObject('modSnippet');$snippet->set('id',0);$snippet->set('name', 'Quip');$snippet->set('description', 'A simple commenting component.');$snippet->set('snippet',file_get_contents($sources['source_core'].'/snippet.quip.php');
Great! Note how here we're actually using the file_get_contents() function to grab the contents of the snippet from our dev environment and placeit here. This makes it easy to run the build in future iterations; no need to continually update this call - just update that file.
Now, we had some properties on that snippet...how do we put those in?
$properties = include $sources['data'].'properties.inc.php';$snippet->setProperties($properties);$category->addMany($snippet);
You'll use modSnippet's setProperties function to pass in an array of property arrays. So, let's take a look at that properties.inc.php file:
<?php/** * Default snippet properties * * @ quippackage * @subpackage build */$properties = array( array( 'name' => 'closed', 'desc' => 'If set to , the thread will not accept comments.',true new 'type' => 'combo- ',boolean 'options' => '', 'value' => ,false ), array( 'name' => 'dateFormat', 'desc' => 'The format of the dates displayed a comment.',for 'type' => 'textfield', 'options' => '', 'value' => '%b %d, %Y at %I:%M %p', ), /* ...removed others brevity... */for);
$properties;return
Simple enough. And now on to the chunks:
/* add chunks */$chunks = include $sources['data'].'transport.chunks.php';
(is_array($chunks)) {if $category->addMany($chunks);} { $modx->log(modX::LOG_LEVEL_FATAL,'Adding chunks failed.'); }else
Good. We returned an array of chunks, and used modCategory's addMany() function to add them in. We also added a sanity check just in casewe made a typo or something. Now, let's package all that into a vehicle:
/* create category vehicle */$attr = array( xPDOTransport::UNIQUE_KEY => 'category', xPDOTransport::PRESERVE_KEYS => ,false xPDOTransport::UPDATE_OBJECT => ,true xPDOTransport::RELATED_OBJECTS => ,true xPDOTransport::RELATED_OBJECT_ATTRIBUTES => array ( 'Snippets' => array( xPDOTransport::PRESERVE_KEYS => ,false xPDOTransport::UPDATE_OBJECT => ,true xPDOTransport::UNIQUE_KEY => 'name', ), 'Chunks' => array ( xPDOTransport::PRESERVE_KEYS => ,false xPDOTransport::UPDATE_OBJECT => ,true xPDOTransport::UNIQUE_KEY => 'name', ), ));$vehicle = $builder->createVehicle($category,$attr);
Great! We've got our category vehicle, complete with all the related chunks and snippet. They'll be installed in the right category when our usersinstall our package, too - so it'll look nice and sharp!
Validators and Resolvers
Validators and resolvers are basically scripts that run during the install process. Validators are run pre-install; meaning that they are run before themain package installation happens. If they return false, the installation does not proceed.
Resolvers, on the other hand, execute after the main package has installed. They can either be file or PHP scripts. A file resolver simply copiesover files into a specific target location. A PHP resolver executes a script after install.
With that said, we're going to attach 2 file resolvers, and one PHP resolver, to our script:
$vehicle->resolve('file',array( 'source' => $sources['source_core'], 'target' => ," MODX_CORE_PATH . 'components/';"return));$vehicle->resolve('file',array( 'source' => $sources['source_assets'], 'target' => ," MODX_ASSETS_PATH . 'components/';"return));$vehicle->resolve('php',array( 'source' => $sources['resolvers'] . 'setupoptions.resolver.php',));$builder->putVehicle($vehicle);
Okay, first things first. File resolvers take two options:
source - This is the target directory or script. If it's a file resolver, it must not end with a trailing slash and must be a valid directory. If it's aPHP script resolver, it must be a valid and accessible file.target - Only applicable to file resolvers, this tells MODx where to install the source files. It is an eval()'ed statement, so must be used asin the example. The standard MODx defines are available to you; use those to grab base paths to target.
So in our examples, we simply move all the files in our source core directory to modx/core/components/quip/ (since our directory that we'removing is named "quip"), and all the files in our source assets directory to modx/assets/components/quip/.
You might be asking why we're moving these to two directories. Well, in practice, it's best to keep non-web-accessible files - such as PHP scripts,tpl files, docs, etc - in the core (which can be placed outside the webroot) so that they are kept secure from web visitors. This keeps only the filesthat need to be accessed through the web by your Component in the web-accessible part of your site.
Next, we add a PHP resolver, called 'setupoptions.resolver.php'. We'll get back to this in much more detail, because it actually deals with thesetup options process we'll get to later.
And finally, we pack the vehicle into the package using the putVehicle function.
Lexicons
So now we've got a package with system settings, actions, menus, snippets, chunks, a category, and a few resolvers all set up. Let's talk aboutour lexicons.
We have our lexicon structured nicely in our \core/components/quip/lexicon directory:
As you can see, we have a subdirectory as 'en', the IANA code for English. Then, we have a 'default.inc.php' - this represents the 'default' lexicontopic. Should we want to create separate lexicon topics, we would name them 'topicname.inc.php'.
As of MODx Revolution RC-2, MODx will automatically find the lexicons in your lexicon directory, assuming that you put them in this structure inthe following place: '{namespace_path}lexicon/', where the Namespace path is the path you put for your Namespace earlier. You don't have tobuild in the lexicons directly at all; MODx will parse it for you.
This is because the lexicons are cached first from your files, then any overrides from the DB are merged and cached. This allows people to'override' your lexicons by using Lexicon Management in the Manager, should they choose to, without breaking their upgrade path for yourComponent.
Package Attributes: License, Readme and Setup Options
Each package has what are called 'package attributes', which can be passed to any resolver or validator. You could pass pretty much anythingyou want into the function modPackageBuilder::setPackageAttributes(), in an array format. There are, however, three special keys that we'll dealwith.
license (string) - This represents your license agreement. Should MODx find this not empty during install, it will prompt the user to agreeto it before they can proceed to install the package.readme (string) - This holds the readme. Before installing, if this is not empty, the user will be able to view the readme. This can be usefulto make sure people see any requirements before they install.setup-options (string) - And here is the best part - this can be an HTML form (minus the form tags) that will pass any user-inputtedoptions to the resolvers or validators. This means that you can take in user input before install, and process it during install!
So let's use these in our build script:
/* now pack in the license file, readme and setup options */$builder->setPackageAttributes(array( 'license' => file_get_contents($sources['docs'] . 'license.txt'), 'readme' => file_get_contents($sources['docs'] . 'readme.txt'), 'setup-options' => array( 'source' => $sources['build'] . 'setup.options.php' ),));
Obviously our license and readme values are being passed the contents of our license and readme files. We're doing them via file_get_contents()so that we can still store the actual files in the directory after install, should the user want to view them later.modx/core/components/quip/docs
But 'setup-options' looks a little different. We could just pass a file_get_contents() call that puts in a string, but then our setup options formwouldn't be dynamic! There might be cases where you wouldn't want that, but we do. We want this options form to upgrade well. Note that you
have to pass the file location as the 'source' parameter - remember Resolvers? Looks familiar, eh? Same idea.
Our setup.options.php file looks like this:
<?php/** * Build the setup options form. * * @ quippackage * @subpackage build *//* set some values */default$values = array( 'emailsTo' => '[email protected]', 'emailsFrom' => '[email protected]', 'emailsReplyTo' => '[email protected]',);
($options[xPDOTransport::PACKAGE_ACTION]) {switch xPDOTransport::ACTION_INSTALL:case xPDOTransport::ACTION_UPGRADE:case $setting = $modx->getObject('modSystemSetting',array('key' => 'quip.emailsTo')); ($setting != ) { $values['emailsTo'] = $setting->get('value'); }if null unset($setting);
$setting = $modx->getObject('modSystemSetting',array('key' => 'quip.emailsFrom')); ($setting != ) { $values['emailsFrom'] = $setting->get('value'); }if null unset($setting);
$setting = $modx->getObject('modSystemSetting',array('key' => 'quip.emailsReplyTo')); ($setting != ) { $values['emailsReplyTo'] = $setting->get('value'); }if null unset($setting); ;break xPDOTransport::ACTION_UNINSTALL: ;case break}
$output = '<label = >Emails To:</label>for "quip-emailsTo"<input type= name= id= width= value= />"text" "emailsTo" "quip-emailsTo" "300" "'.$values['emailsTo'].'"<br /><br />
<label = >Emails From:</label>for "quip-emailsFrom"<input type= name= id= width= value="text" "emailsFrom" "quip-emailsFrom" "300"
/>"'.$values['emailsFrom'].'"<br /><br />
<label = >Emails Reply-To:</label>for "quip-emailsReplyTo"<input type= name= id= width= value="text" "emailsReplyTo" "quip-emailsReplyTo" "300"
/>';"'.$values['emailsReplyTo'].'"
$output;return
As you can see, some new constants here. These are available to all setup options forms and resolvers:
xPDOTransport::PACKAGE_ACTION - This tells us what action is being performed on the package; it is one of the following 3 values:xPDOTransport::ACTION_INSTALL - This is set when the package is being executed as an install.xPDOTransport::ACTION_UPGRADE - This is set when the package is being upgraded.xPDOTransport::ACTION_UNINSTALL - This is set when the package is being uninstalled. This doesn't apply to setup-options,obviously, since nothing is being set up. In future Revolution releases, it will allow you to do specific options for uninstall; but notyet.
Basically, we're presenting them with a form before install that looks like this:
So that they can set or update the values of the emailsTo, emailsFrom, and emailsReplyTo system settings before they install the package. Now,the script will first check to see if those settings already exist; and if so, we'll fill them in with those values. This allows for upgrades to gogracefully, persisting the user's custom settings for those values. Pretty cool, huh?
Obviously, there's a lot you could do with this. You could set target directories for photo locations, setup basic email accounts, set login/passinformation for 3rd party web service integrations, and more. We'll leave your imagination to do the work from here on out.
Let's go back to our PHP script resolver that processes this information:
<?php/** * Resolves setup-options settings by setting email options. * * @ quippackage * @subpackage build */$success= ;false
($options[xPDOTransport::PACKAGE_ACTION]) {switch xPDOTransport::ACTION_INSTALL:case xPDOTransport::ACTION_UPGRADE:case /* emailsTo */ $setting = $object->xpdo->getObject('modSystemSetting',array('key' => 'quip.emailsTo')); ($setting != ) {if null $setting->set('value',$options['emailsTo']); $setting->save(); } {else $object->xpdo->log(xPDO::LOG_LEVEL_ERROR,'[Quip] emailsTo setting could not be found, sothe setting could not be changed.'); }
/* emailsFrom */ $setting = $object->xpdo->getObject('modSystemSetting',array('key' => 'quip.emailsFrom')); ($setting != ) {if null $setting->set('value',$options['emailsFrom']); $setting->save(); } {else $object->xpdo->log(xPDO::LOG_LEVEL_ERROR,'[Quip] emailsFrom setting could not be found, sothe setting could not be changed.'); }
/* emailsReplyTo */ $setting = $object->xpdo->getObject('modSystemSetting',array('key' => 'quip.emailsReplyTo')); ($setting != ) {if null $setting->set('value',$options['emailsReplyTo']); $setting->save(); } {else $object->xpdo->log(xPDO::LOG_LEVEL_ERROR,'[Quip] emailsReplyTo setting could not be found,so the setting could not be changed.'); }
$success= ;true ;break xPDOTransport::ACTION_UNINSTALL:case $success= ;true ;break}
$success;return
Note that $modx is not available here; you're actually running these scripts from within the transport object. The $modx object is available as adifferent name, however: $object->xpdo. $object is the object that the resolver is attached to; here, it would be the modCategory.
Our script, then, is setting the values set in the setup-options to the newly installed system settings.
And now that we've got everything packaged and ready to go, let's pack the package into a zip file and give us the time it took to build thepackage:
$builder->pack();
$mtime= microtime();$mtime= explode( , $mtime);" "$mtime= $mtime[1] + $mtime[0];$tend= $mtime;$totalTime= ($tend - $tstart);$totalTime= sprintf( , $totalTime);"%2.4f s"
$modx->log(modX::LOG_LEVEL_INFO, );"\nPackage Built.\nExecution time: {$totalTime}\n"exit();
Great, we're done! You'll only need to run this script now, and viola! A fully zipped transport package file will appear in your core/packagesdirectory.
View the Source
Related Pages
Package ManagementTransport PackagesTransport Providers
Extending modUser
Intended Audience
This article attempts to ride the line of beginners desiring to learn the basics of setting up an extended modUser class and those moreexperienced individuals needing a foundation to begin with. For fully functional applications please refer to Currenty available extended modUser
.classes
Overview
By extending the MODx Revolution authentication layer we can simply and easily build very complex and varied user subsystems rivaling that ofsocial networking, user management systems, and other applications not yet conceptualized. This ability to extend the modUser class is just oneexample of the underlying power of MODx Revolution. By following the steps detailed below you will quickly be on your way to developing yourown user "interfaces" or sub-systems.
Purpose
Extending modUser is for those situations when user authentication interaction needs overridden, extended, enhanced, etc. The focus is purelyuser authentication. Also, please understand this is a a simplified working concept. You can get much more complex.
The Rules
Extending modUser does NOT mean we are adding anything to the _users table in the database. It simply means we are going to bemodxappending our own data to the end of the table by attaching our data sets via relationships and a schema. At no time should an extendedapplication actually attempt to completely replace the modUser Class. Instead we should be using it as a platform to build upon. The onlyindication that the user has been extended will be found by the class_key being changed from "modUser" to the extended class name.
Your extension should be used to access . If the user (object) has not been extended, do not allow your extension to interact withyour extensionthem -- hence: let your extension die.
MODx Revolution already handles users and probably does not need your help. While you may use your extension on *your* data, please do notbegin writing "bloat" which is already in modUser. In other words use the Revolution resources for your extended users, but do not create code toreplace modUser.
Lastly, get familiar with , before you begin to code. Some methods are not one-to-one as you might assume, such as attributes, whichmodUsercan be assigned per context, resource, etc. Typically use the modUser suggestions to access modUser methods.
Steps to extending modUser
1. ) Create the schema and generate a model
The first thing we need to accomplish, is to create an extended user schema which extends modUser. Please note that there is no aggregaterelation upwards from your "main" class which is extending modUser. An example follows:
<?xml version= encoding= ?>"1.0" "UTF-8"<model = baseClass= platform= defaultEngine=package "extendeduser" "xPDOObject" "mysql" "MyISAM"tablePrefix= >"ext_" <!-- inherit the modx user and extend it --> <object class= table= = >"extUser" "users" extends "modUser" <composite alias= local= foreign= cardinality= owner= />"Phones" "id" "user" "many" "local" <composite alias= local= foreign= cardinality= owner= />"Table2" "id" "user" "many" "local" </object>
<!-- track all user phone numbers --> <object table= = >"phone_numbers" extends "xPDOSimpleObject" <field key= dbtype= phptype= = = index= />"user" " "int "integer" null " "false default "0" "index" <field key= dbtype= precision= phptype= = ="""areacode" "varchar" "3" "string" null " "false default/> <field key= dbtype= precision= phptype= = ="" />"number" "varchar" "7" "string" null " "false default <aggregate alias= local= foreign= cardinality= owner= />"extUser" "user" "id" "one" "foreign" </object>
<!-- user extension --> <object table= = >"table2" extends "xPDOSimpleObject" <field key= dbtype= phptype= = = index= />"user" " "int "integer" null " "false default "0" "index" <field key= dbtype= precision= phptype= = /> "myspaceurl" "varchar" "255" "string" null " "false <aggregate alias= local= foreign= cardinality= owner= />"extUser" "user" "id" "one" "foreign" </object></model>
You will need to parse and create the model map associated with this schema. As this process is out of the scope of this topic, please refer to for further information.Using Custom Database Tables in your 3rd Party Components
2.) Edit the extuser.class.php
To access the extended class, we have to inform modUser that the user in question has been extended. The _users table in the databasemodxcontains a field specifically for this purpose: class_key. The default value in this field is modUser. As users are added to your site using yourextension we need to "force" the name of our "main" class in the schema, namely extUser in our example.
Edit the extuser.class.php file created when you generated the model. The specific file is the one found in the top of the model tree (you shouldsee a mysql directory) in this same folder. Edit the file to resemble the following:
<?php/** * @ extendeduserpackage * @subpackage user.mysql */class extUser modUser {extends function __construct(xPDO & $xpdo) { parent :: __construct($xpdo); $ ->set('class_key','extUser');this }}?>
3.) Create (or edit) extension_packages in System Settings
Access the System settings found in the System menu of the manager, and search for: extension_packages.
If the key already exists, add
, extendeduser:{core_path}components/extendeduser/model/
Note the comma at the beginning of the Value.
If the key does not exists
Create a new system setting with name of extension_packagesKey of extension_packagesFieldtype: Textfieldvalue
extendeduser:{core_path}components/extendeduser/model/
Note the absence of a comma at the beginning of the Value.
4.) Final Step Create a class to access and utilize your extended class
<?php/** * File sample.class.php (requires MODx Revolution 2.x) * Created on Aug 18, 2010 * Project shawn_wilkerson * @ extendedUserpackage * @version 1.0 * @category User Extension * @author W. Shawn Wilkerson * @link http://www.shawnWilkerson.com * @copyright Copyright (c) 2010, W. Shawn Wilkerson. All rights reserved. * @license GPL * */
(!class_exists('Sampleclass')) {if class Sampleclass { function __construct(modX & $modx, array $config= array ()) {
/* Import modx as a reference */ $ ->modx= & $modx;this
/* Establish the environment */ $ ->extPath= $modx->getOption('core_path', ,this nullMODX_CORE_PATH).'components/extendeduser/'; $ ->modx->addPackage('extendeduser', $ ->extPath .'model/', 'ut_');this this $ ->_config= array_merge(array (this 'userID' => $ ->modx->user->get('id'),this ), $config);
/* Define the user */ $ ->userObj = $ ->setUser($ ->_config['userID']);this this this $ ->userID = $ ->userObj->get('id');this this }
function __destruct() { unset ($ ->extPath, $ ->userObj, $ ->userID, $ ->_config);this this this this }
/** * Returns object of type Phone. */ function getPhoneObj() {public $ ->userObj->getOne('Phones');this $ ->userObj->Phones;return this }
/** * Returns object utUser instance of modUser Defaults to current user. * @param $userID */ function getUserObj($userID) {public $ ->modx->getObject('modUser', $userID);return this }
/** * Establishes the user. * @param $userIDint */ function setUser($userID){public $ ->getUserObj($userID);return this } }}
5.) Accessing the class
In our example we will be accessing our extended user throughout our site, therefore we load it as a service as shown in the following example:
1.
2.
3. 4. 5.
a.
b. 6.
<?php$x = $modx->getService('extendeduser','Sampleclass',$modx->getOption('core_path', ,nullMODX_CORE_PATH).'components/extendeduser/',$scriptProperties);
(!($x Extendeduser)) {if instanceof $modx->log(modX::LOG_LEVEL_ERROR,'[Extendeduser] Could not load Extendeduser class.'); $modx->event->output( );true}
;return
Noteworthy items
Any pre existing user, will still have modUser as the class_key and therefore will be extended or produce user objects of type extUsernotunless you change itDouble check the modx.mysql.schema.xml file to make sure you are not using classes or alias it is already using, as yours will supersedethe default moduser prohibiting you access to items such as the user attributes (with alias Profile)The extUser will have a table created in the database, but the attached relations willnotThe extended class table(s) be in the same database as the regular _users tablemust modxSymptoms of step 3 (extension_packages path) not being correct:
Any user with the class_key of extUser will return an error upon login: "User cannot be found...". If this is the admin, access yourdatabase directly, return the class_key to modUser, login correctly and then alter the path to a correct representation of the path.The snippets attached to the class will intermittently work or fail altogether
To get counts from your data (i.e. how many phone numbers does this person have) use either (any criteria can be added):
$ ->modx->getCount('extPhones', array('user' => $ ->userID));this this$ ->modx->getCount('extPhones');this
It is completely possible to have multiple extended modUser systems active at the same time. It would even be feasible to extend JasonCoward's rpx extension into a hybrid system utilizing the benefits of both systems. It is also completely possible to have multiple extendedmodUser applications running autonomously. This is simply done by following this process for each of your extensions, changing only the"class_key" field to reflect the extended class belonging to each respective user.
Suggested additional considerations
The model files can be edited with methods and descriptions. Take a look at much of the MODx / xPDO models and you will see this doneextensively.
This process can be automated and captured upon user login. For brevity sake, it is best to refer you to splittingred's github, where he provides areal world application:
The plugins: http://github.com/splittingred/modActiveDirectory/blob/master/core/components/activedirectory/elements/plugins/plugin.activedirectory.php
The events: http://github.com/splittingred/modActiveDirectory/blob/master/core/components/activedirectory/elements/events/onauthentication.php
http://github.com/splittingred/modActiveDirectory/blob/master/core/components/activedirectory/elements/events/onusernotfound.php
Extended modUser Classes currently Available
modActiveDirectory an application which provides interaction with a Microsoft Domain Controller
rpm extension allows people to login via Facebook and other social networking medium
Other Development Resources
This section covers alternative development options and information for MODx developers.
Loading MODx Externally
Loading the MODx Object
Using the MODx object (and all of its respective classes) is quite simple. All you need is this code:
require_once '/absolute/path/to/modx/config.core.php';require_once MODX_CORE_PATH.'config/'.MODX_CONFIG_KEY.'.inc.php';require_once MODX_CORE_PATH.'model/modx/modx.class.php';$modx = modX();new$modx->initialize('web');
This will initialize the MODx object into the 'web' . Now, if you want to access it under a different context (and thereby changing its accessContextpermissions, policies, etc), you'll just need to change 'web' to whatever you want to load.Context
From there, you can use any MODx methods, functions, or classes.
See Also
Developer IntroductionxPDO, the db-layer for Revolution
API Reference
The MODx Revolution API Documentation can be found here:
http://api.modxcms.com
Class Reference
modX
The modX Class
This is the base class of MODx Revolution; it is used for many of the main processing methods of MODx. It extends the class.xPDO
Methods
See Also
modX.getChunk
modX::getChunk
Processes and returns the output from an HTML chunk by name.
Syntax
API Doc: http://api.modxcms.com/modx/modX.html#getChunk
string getChunk (string $chunkName, [array $properties = array ()])
Example
Lets process this chunk and output its value. We have this Chunk, called "WelcomeChunk":
<p>Welcome [[+name]]!</p>
We'll put this in our Snippet:
$output = $modx->getChunk('WelcomeChunk',array( 'name' => 'John',));
$output;return
This code outputs this:
<p>Welcome John!</p>
See Also
Chunks
modX.getLoginUserName
modX::getLoginUserName
Returns the current user name, for the current or specified context.
Syntax
API Doc: http://api.modxcms.com/modx/modX.html#getLoginUserName
string getLoginUserName ([string $context = ''])
Example
Grab the user's username in the current Context.
$username = $modx->getLoginUserName();
See Also
Contexts
modX.getPlaceholder
modX::getPlaceholder
Get a placeholder value by key.
Syntax
API Doc: http://api.modxcms.com/modx/modX.html#getPlaceholder
mixed getPlaceholder (string $key)
Example
$value = $modx->getPlaceholder('MyPlaceholder');
See Also
modX.setPlaceholdermodX.setPlaceholdersmodX.toPlaceholdermodX.toPlaceholders
modX.setPlaceholder
modX::setPlaceholder
Sets a Placeholder value.
Syntax
API Doc: http://api.modxcms.com/modx/modX.html#setPlaceholder
void setPlaceholder (string $key, mixed $value)
Example
$modx->setPlaceholder('name','Barry');
See Also
modX.getPlaceholdermodX.setPlaceholdersmodX.toPlaceholdermodX.toPlaceholders
modX.setPlaceholders
modX::setPlaceholders
Sets a collection of placeholders stored in an array or as object vars.An optional namespace parameter can be prepended to each placeholder key in the collection, to isolate the collection of placeholders.
Note that unlike , this function does not add separators between the namespace and the placeholder key. UsemodX.toPlaceholderstoPlaceholders() when working with multi-dimensional arrays or objects with variables other than scalars so each level gets delimited by aseparator.
Syntax
API Doc: http://api.modxcms.com/modx/modX.html#setPlaceholders
void setPlaceholders (array|object $placeholders, [string $namespace = ''])
Example
Add an array of placeholders, and prefix 'my.' to their key.
$modx->setPlaceholders(array( 'name' => 'John', 'email' => '[email protected]',),'my.');
See Also
modX.toPlaceholdermodX.toPlaceholdersmodX.setPlaceholdermodX.getPlaceholder
modX.addEventListener
modX::addEventListener
Add a plugin to the eventMap within the current execution cycle.
Syntax
API Doc: http://api.modxcms.com/modx/modX.html#addEventListener
boolean addEventListener (string $event, integer $pluginId)
Example
Add a Plugin with ID 2 to the Event 'OnChunkPrerender':
$modx->addEventListener('OnChunkPrerender',12);
See Also
modXPlugins
modX.checkForLocks
modX::checkForLocks
Checks for locking on a page. A page is "locked" if another user is already viewing it. This prevents collisions.
Syntax
API Doc: http://api.modxcms.com/modx/modX.html#checkForLocks
void checkForLocks (integer $id, string $action, string $type)
Example
Check for locks on the edit_chunk action.
if ($modx->checkForLocks($modx->getLoginUserID(),'edit_chunk','edit');
See Also
modX
modX.checkSession
modX::checkSession
Checks to see if the user has a session in the specified context.
Syntax
API Doc: http://api.modxcms.com/modx/modX.html#checkSession
boolean checkSession ([string $sessionContext = 'web'])
Example
Check to see if the user has a session in the 'sports' context.
$modx->checkSession('sports');
See Also
modX
modX.executeProcessor
modX::executeProcessor
Executes a specific processor. The only argument is an array, which can take the following values:
action - The action to take, similar to connector handling.processors_path - If specified, will override the default MODx processors path.location - A prefix to load processor files from, will prepend to the action parameter.
Syntax
API Doc: http://api.modxcms.com/modx/modX.html#executeProcessor
mixed executeProcessor (array $options)
Example
Execute the Context getList processor:
$modx->executeProcessor(array( 'location' => 'context', 'action' => 'getList',));
See Also
modX
modX.getAuthenticatedUser
modX::getAuthenticatedUser
Gets the user authenticated in the specified context.
Syntax
API Doc: http://api.modxcms.com/modx/modX.html#getAuthenticatedUser
unknown getAuthenticatedUser ([string $contextKey = ''])
Example
Get the authenticated user for the 'sports' context:
$user = $modx->getAuthenticatedUser('sports');
See Also
modX
modX.getCacheManager
modX::getCacheManager
Get an extended xPDOCacheManager instance responsible for MODx caching.
Overrides xPDO::getCacheManager.
Syntax
API Doc: http://api.modxcms.com/modx/modX.html#getCacheManager
object getCacheManager()
Example
Get the Cache Manager to set a dummy cache file.
$cacheManager = $modx->getCacheManager();$cacheManager->set('testcachefile','test123');
See Also
modX
modX.getChildIds
modX::getChildIds
Gets all of the child resource ids for a given resource.
Syntax
API Doc: http://api.modxcms.com/modx/modX.html#getChildIds
array getChildIds ([integer $id = ], [integer $depth = 10])null
Example
Get all the children IDs for the Resource 23. Limit to 6 levels deep.
$array_ids = $modx->getChildIds(23,6);
See Also
modXmodX.getParentIds
modX.getConfig
modX::getConfig
Get the configuration for the site.
Syntax
API Doc: http://api.modxcms.com/modx/modX.html#getConfig
array getConfig ()
Example
Get the site config into an array.
$config = $modx->getConfig();
See Also
modX
modX.getContext
modX::getContext
Retrieve a context by name without initializing it.
Within a request, contexts retrieved using this function will cache the context data into the modX::$contexts array to avoid loading the samecontext multiple times.
Syntax
API Doc: http://api.modxcms.com/modx/modX.html#getContext
&$modContext getContext (string $contextKey)
Example
Get the 'sports' Context.
$ctx = $modx->getContext('sports');
See Also
Page: Contexts
Page: modX
Page: modX.getContext
Page: Contexts
Page: modX
Page: modX.getContext
modX.getEventMap
modX::getEventMap
Gets a map of events and registered plugins for the specified context.
Syntax
API Doc: http://api.modxcms.com/modx/modX.html#getEventMap
array getEventMap (string $contextKey)
Example
Get the event map for the current Context.
$map = $modx->getEventMap();
See Also
modX
modX.getLoginUserID
modX::getLoginUserID
Returns the current user ID, for the current or specified context.
Syntax
API Doc: http://api.modxcms.com/modx/modX.html#getLoginUserID
string getLoginUserID ([string $context = ''])
Example
Get the current login user ID for the 'sports' context.
$id = $modx->getLoginUserID('sports');
See Also
modX
modX.getParentIds
modX::getParentIds
Gets all of the parent resource ids for a given resource.
Syntax
API Doc: http://api.modxcms.com/modx/modX.html#getParentIds
array getParentIds ([integer $id = ], [integer $height = 10])null
Example
Get all of the parent IDs for the Resource with ID 23.
$parentIds = $modx->getParentIds(23);
See Also
modXmodX.getChildIds
modX.getParser
modX::getParser
Gets the MODx parser.
Returns an instance of modParser responsible for parsing tags in element content, performing actions, returning content and/or sending otherresponses in the process.
Syntax
API Doc: http://api.modxcms.com/modx/modX.html#getParser
object getParser()
Example
Get the MODx Parser object.
$parser = $modx->getParser();
See Also
modX
modX.getRegisteredClientScripts
modX::getRegisteredClientScripts
Returns all registered JavaScript and HTML blocks.
Syntax
API Doc: http://api.modxcms.com/modx/modX.html#getRegisteredClientScripts
string getRegisteredClientScripts ()
Example
Get all registered scripts into an array.
$scripts = $modx->getRegisteredClientScripts();
See Also
modX
modX.getRegisteredClientStartupScripts
modX::getRegisteredClientStartupScripts
Returns all registered startup CSS, JavaScript, or HTML blocks.
Syntax
API Doc: http://api.modxcms.com/modx/modX.html#getRegisteredClientStartupScripts
string getRegisteredClientStartupScripts ()
Example
Get all registered startup scripts into an array.
$startupScripts = $modx->getRegisteredClientStartupScripts();
See Also
modX
modX.getRequest
modX::getRequest
Attempt to load the request handler class, if not already loaded. Defaults to modRequest.
Syntax
API Doc: http://api.modxcms.com/modx/modX.html#getRequest
boolean getRequest ([$string $class = 'modRequest'], [$path $path = ''])
Example
Load a custom Request handler class called 'myRequest' from '/path/to/myrequest.class.php':
$modx->getRequest('myRequest','/path/to/');
See Also
modX
modX.getResponse
modX::getResponse
Attempt to load the response handler class, if not already loaded. Defaults to modResponse.
Syntax
API Doc: http://api.modxcms.com/modx/modX.html#getResponse
boolean getResponse ([$string $class = 'modResponse'], [$path $path = ''])
Example
Load a custom Response handler class called 'myResponse' from '/path/to/myresponse.class.php':
$modx->getRequest('myResponse','/path/to/');
See Also
modX
modX.getService
modX::getService
Load and return a named service class instance.
Syntax
API Doc: http://api.modxcms.com/modx/modX.html#getService
object getService (string $name, [string $class = ''], [string $path = ''], [array $params = array()])
Examples
Get the modSmarty service.
$modx->getService('smarty','smarty.modSmarty');
Get a custom, user-defined service called 'modTwitter' from a custom path ('/path/to/modtwitter.class.php'), and pass in some custom parameters.
$modx->getService('twitter','modTwitter','/path/to/',array( 'api_key' => 3212423,));$modx->twitter->tweet('Success!');
See Also
modXMODx Services
modX.getSessionState
modX::getSessionState
Returns the state of the SESSION being used by modX.
The possible values for session state are:
modX::SESSION_STATE_UNINITIALIZEDmodX::SESSION_STATE_UNAVAILABLEmodX::SESSION_STATE_EXTERNALmodX::SESSION_STATE_INITIALIZED
Syntax
API Doc: http://api.modxcms.com/modx/modX.html#getSessionState
integer getSessionState ()
Example
$state = $modx->getSessionState();
See Also
modX
modX.getTree
modX::getTree
Get a site tree from a single or multiple modResource instances.
Syntax
API Doc: http://api.modxcms.com/modx/modX.html#getTree
array getTree ([ |array $id = ], [ $depth = 10])int null int
Example
Get a tree for the Resource with ID 12. Only go 5 levels deep.
$treeArray = $modx->getTree(12,5);
See Also
modX
modX.getUser
modX::getUser
Get the current authenticated User and assigns it to the modX instance.
Syntax
API Doc: http://api.modxcms.com/modx/modX.html#getUser
modUser getUser ([string $contextKey = ''])
Example
Get the current auth'ed user and print out its username.
$user = $modx->getUser();echo $user->get('username');
See Also
modX
modX.getVersionData
modX::getVersionData
Gets the modX core version data. The array contains the following keys (examples for version "MODx Revolution 2.0.0-beta-3"):
version - The current version number, eg: 2major_version - The current major version number, eg: 0minor_version - The current minor version number, eg: 0patch_level - The current release level, eg: 'beta-3'code_name - The code name for the product, eg: 'Revolution'full_version - A compiled full version name, eg: '2.0.0-beta-3'full_appname - The entire version name, eg: 'MODx Revolution 2.0.0-beta-3'
Syntax
API Doc: http://api.modxcms.com/modx/modX.html#getVersionData
array getVersionData ()
Example
Print out the current full version:
$vers = $modx->getVersionData();echo $vers['full_version'];
See Also
modX
modX.handleRequest
modX::handleRequest
Initialize, cleanse, and process a request made to a modX site.
Syntax
API Doc: http://api.modxcms.com/modx/modX.html#handleRequest
mixed handleRequest ()
Example
Handle the current request.
$modx->handleRequest();
See Also
modX
modX.hasPermission
modX::hasPermission
Returns true if user has the specified policy permission.
Syntax
API Doc: http://api.modxcms.com/modx/modX.html#hasPermission
boolean hasPermission (string $pm)
Example
Deny the user access if they don't have the permission 'edit_chunk' in their loaded Policies.
if (!$modx->hasPermission('edit_chunk')) die('Access Denied!');
See Also
Policies
modX.initialize
modX::initialize
Initializes the modX engine into a .Context
This includes preparing the session, pre-loading some common classes and objects, the current site and context settings, extension packagesused to override session handling, error handling, or other initialization classes.
Syntax
API Doc: http://api.modxcms.com/modx/modX.html#initialize
void initialize ([string $contextKey = 'web'])
Example
Initialize the 'sports' Context.
$modx->initialize('sports');
See Also
Contexts
modX.invokeEvent
modX::invokeEvent
Invokes a specified Event with an optional array of parameters.
Syntax
API Doc: http://api.modxcms.com/modx/modX.html#invokeEvent
void invokeEvent (string $eventName, [array $params = array ()])
Example
Invoke the OnChunkRender event:
$modx->invokeEvent('OnChunkRender',array( 'id' => $chunk->get('id'),));
See Also
modX
modX.lexicon
modX::lexicon
Grabs a processed Lexicon Entry.
This may also be a modLexicon object as well, if the Lexicon has been loaded. PHP supports having objects and methods withthe same name.
Syntax
API Doc: http://api.modxcms.com/modx/modX.html#lexicon
void lexicon (string $key, [array $params = array()])
Example
Output the translation of the 'welcome_message' Entry, and sets the 'name' Placeholder in it.
echo $modx->lexicon('welcome_message',array('name' => 'John'));
See Also
Internationalization
modX.makeUrl
modX::makeUrl
Generates a URL representing a specified resource.
The scheme indicates in what format the URL is generated.
-1 : (default value) URL is relative to site_url
0 : see http1 : see httpsfull : URL is absolute, prepended with site_url from configabs : URL is absolute, prepended with base_url from confighttp : URL is absolute, forced to http schemehttps : URL is absolute, forced to https scheme
Syntax
API Doc: http://api.modxcms.com/modx/modX.html#makeUrl
string makeUrl (integer $id, [string $context = ''], [string $args = ''], [mixed $scheme = -1])
Examples
Make a URL for the Resource with ID 4.
$url = $modx->makeUrl(4);
Make a URL for the Resource with ID 12, but make sure it's in HTTPS.
$url = $modx->makeUrl(12,'','','https');
modX.parseChunk
modX::parseChunk
Parse a chunk using an associative array of replacement variables.
Syntax
API Doc: http://api.modxcms.com/modx/modX.html#parseChunk
string parseChunk (string $chunkName, array $chunkArr, [string $prefix = '[[+'], [string $suffix =']]'])
Example
$output = $modx->parseChunk('myChunk',array('name' => 'John'));
See Also
modXmodX.getChunk
modX.regClientCSS
modX::regClientCSS
Register CSS to be injected inside the HEAD tag of a resource.
Syntax
API Doc: http://api.modxcms.com/modx/modX.html#regClientCSS
void regClientCSS (string $src)
Example
Register a CSS file to the HEAD tag:
$modx->regClientCSS('assets/css/style.css');
See Also
modXmodX.regClientHTMLBlockmodX.regClientScriptmodX.regClientStartupHTMLBlockmodX.regClientStartupScriptmodX.getRegisteredClientScriptsmodX.getRegisteredClientStartupScripts
modX.regClientHTMLBlock
modX::regClientHTMLBlock
Register HTML to be injected before the closing BODY tag.
Syntax
API Doc: http://api.modxcms.com/modx/modX.html#regClientHTMLBlock
void regClientHTMLBlock (string $html)
Example
Inject a footer into the page.
$modx->regClientHTMLBlock('<div id= >(c) 2009 MODx</div>');"footer"
See Also
modXmodX.regClientCSSmodX.regClientScriptmodX.regClientStartupHTMLBlockmodX.regClientStartupScriptmodX.getRegisteredClientScriptsmodX.getRegisteredClientStartupScripts
modX.regClientScript
modX::regClientScript
Register JavaScript to be injected before the closing BODY tag.
Syntax
API Doc: http://api.modxcms.com/modx/modX.html#regClientScript
void regClientScript (string $src, [ $plaintext = ])boolean false
Example
Add some JS to the end of the page.
$modx->regClientScript('assets/js/footer.js');
See Also
modXmodX.regClientCSSmodX.regClientHTMLBlockmodX.regClientStartupHTMLBlockmodX.regClientStartupScriptmodX.getRegisteredClientScriptsmodX.getRegisteredClientStartupScripts
modX.regClientStartupHTMLBlock
modX::regClientStartupHTMLBlock
Register HTML to be injected before the closing HEAD tag.
Syntax
API Doc: http://api.modxcms.com/modx/modX.html#regClientStartupHTMLBlock
void regClientStartupHTMLBlock (string $html)
Example
Render a faux tag element before the end of the HEAD.
$modx->regClientStartupHTMLBlock('<tag></tag>');
See Also
modXmodX.regClientCSSmodX.regClientHTMLBlockmodX.regClientScriptmodX.regClientStartupScriptmodX.getRegisteredClientScriptsmodX.getRegisteredClientStartupScripts
modX.regClientStartupScript
modX::regClientStartupScript
Register JavaScript to be injected inside the HEAD tag of a resource.
Syntax
API Doc: http://api.modxcms.com/modx/modX.html#regClientStartupScript
void regClientStartupScript (string $src, [ $plaintext = ])boolean false
Example
Register some JS to the start of the page:
$modx->regClientStartupScript('assets/js/onload.js');
See Also
modXmodX.regClientCSSmodX.regClientHTMLBlockmodX.regClientScriptmodX.regClientStartupHTMLBlockmodX.getRegisteredClientScriptsmodX.getRegisteredClientStartupScripts
modX.reloadConfig
modX::reloadConfig
Reload the config settings. Useful in cases where you've loaded some Settings dynamically.
Syntax
API Doc: http://api.modxcms.com/modx/modX.html#reloadConfig
array reloadConfig ()
Example
$modx->reloadConfig();
See Also
modXSettingsSystem Settings
modX.removeAllEventListener
modX::removeAllEventListener
Remove all registered events for the current request.
Syntax
API Doc: http://api.modxcms.com/modx/modX.html#removeAllEventListener
void removeAllEventListener ()
Example
Eliminate any other events from firing:
$modx->removeAllEventListener();
See Also
modX
modX.removeEventListener
modX::removeEventListener
Remove an event from the eventMap so it will not be invoked.
Syntax
API Doc: http://api.modxcms.com/modx/modX.html#removeEventListener
boolean removeEventListener (string $event)
Example
Prevent any Events from firing on 'OnChunkRender':
$modx->removeEventListener('OnChunkRender');
See Also
modX
modX.runSnippet
modX::runSnippet
Process and return the output from a PHP snippet by name.
Syntax
API Doc: http://api.modxcms.com/modx/modX.html#runSnippet
string runSnippet (string $snippetName, [array $params = array ()])
Example
Run the 'Welcome' snippet with some custom parameters:
$output = $modx->runSnippet('Welcome',array( 'name' => 'John'));echo $output; // prints 'Welcome John!'
See Also
modXSnippets
modX.sendError
modX::sendError
Send the user to a type-specific core error page and halt PHP execution.
The parameter 'type' can be any field, which will load the template file in core/error. MODx comes prepackaged with 2 default error pages; theseare 'unavailable' (the default), and 'fatal'.
Syntax
API Doc: http://api.modxcms.com/modx/modX.html#sendError
void sendError ([string $type = ''], [array $options = array()])
Examples
Send an Unavailable 503 error page.
$modx->sendError('unavailable');
Send a Fatal 500 error page.
$modx->sendError('fatal');
See Also
modX
modX.sendErrorPage
modX::sendErrorPage
Send the user to a MODx virtual error page.
Syntax
API Doc: http://api.modxcms.com/modx/modX.html#sendErrorPage
void sendErrorPage ([array $options = ])null
Example
Send the user to the default Error page for the site.
$modx->sendErrorPage();
See Also
modXmodX.sendUnauthorizedPagemodX.sendForwardmodX.sendRedirect
modX.sendForward
modX::sendForward
Forwards the request to another resource without changing the URL. If the ID provided does not exist, sends to a 404 Error page.
Syntax
API Doc: http://api.modxcms.com/modx/modX.html#sendForward
void sendForward (integer $id, [string $options = ])null
Example
Send the user to Resource ID 234 without actually changing the URL.
$modx->sendForward(234);
See Also
modXmodX.sendRedirectmodX.sendErrorPage
modX.sendRedirect
modX::sendRedirect
Sends a redirect to the specified URL using the specified method.
Valid $type values include:
REDIRECT_REFRESH - Uses the header refresh methodREDIRECT_META - Sends a a META HTTP-EQUIV="Refresh" tag to the outputREDIRECT_HEADER - Uses the header location method
REDIRECT_HEADER is the default.
Syntax
API Doc: http://api.modxcms.com/modx/modX.html#sendRedirect
void sendRedirect (string $url, [integer $count_attempts = 0], [string $type = ''])
Examples
Send a redirection request to the Resource with ID 54.
$url = $modx->makeUrl(54);$modx->sendRedirect($url);
Send a redirection request to modxcms.com. Do so via the META HTTP-EQUIV refresh tag.
$modx->sendRedirect('http://modxcms.com',0,REDIRECT_META);
See Also
modXmodX.sendForwardmodX.sendErrorPage
modX.sendUnauthorizedPage
modX::sendUnauthorizedPage
Send the user to the MODx unauthorized page.
Syntax
API Doc: http://api.modxcms.com/modx/modX.html#sendUnauthorizedPage
void sendUnauthorizedPage ([array $options = ])null
Example
Send the user to the unauth page.
$modx->sendUnauthorizedPage();
See Also
modXmodX.sendErrorPagemodX.sendForwardmodX.sendRedirect
modX.setDebug
modX::setDebug
Sets the debugging features of the modX instance.
Syntax
API Doc: http://api.modxcms.com/modx/modX.html#setDebug
boolean| setDebug ([ | $debug = ], [ $stopOnNotice = ])int boolean int true boolean false
Example
Turn debug mode on, and tell the process to stop if Notices occur:
$modx->setDebug( );true
See Also
Page: modX.setDebug
Page: modX.setDebug
modX.switchContext
modX::switchContext
Switches the primary Context for the modX instance.
Be aware that switching contexts does not allow custom session handling classes to be loaded. The gateway defines the session handling that isapplied to a single request. To create a context with a custom session handler you must create a unique context gateway that initializes thatcontext directly.
Syntax
API Doc: http://api.modxcms.com/modx/modX.html#switchContext
boolean switchContext (string $contextKey)
Example
Switch to the 'sports' Context.
$modx->switchContext('sports');
See Also
Contexts
modX.toPlaceholder
modX::toPlaceholder
Recursively validates and sets placeholders appropriate to the value type passed.
Syntax
API Doc: http://api.modxcms.com/modx/modX.html#toPlaceholder
void toPlaceholder (string $key, mixed $value, [string $prefix = ''], [string $separator = '.'])
Example
Set a placeholder and prefix its key with 'my.'
$modx->toPlaceholder('name','John','my');
See Also
modX.toPlaceholdersmodX.setPlaceholdermodX.setPlaceholdersmodX.getPlaceholder
modX.toPlaceholders
modX::toPlaceholders
Sets placeholders from values stored in arrays and objects.
Each recursive level adds to the prefix, building an access path using an optional separator.
Syntax
API Doc: http://api.modxcms.com/modx/modX.html#toPlaceholders
void toPlaceholders (array|object $subject, [string $prefix = ''], [string $separator = '.'])
Example
Set an array of placeholders and prefix with 'my.'
$modx->toPlaceholders(array( 'name' => 'John', 'email' => '[email protected]',),'my');
See Also
modX.toPlaceholdermodX.setPlaceholdermodX.setPlaceholdersmodX.getPlaceholder
modX.unsetPlaceholder
modX::unsetPlaceholder
Unsets a placeholder value by key.
Syntax
API Doc: http://api.modxcms.com/modx/modX.html#unsetPlaceholder
void unsetPlaceholder (string $key)
Example
$modx->unsetPlaceholder('myPlaceholder');
See Also
modX.unsetPlaceholdersmodX.toPlaceholdermodX.toPlaceholdersmodX.setPlaceholdermodX.setPlaceholdersmodX.getPlaceholder
modX.unsetPlaceholders
modX::unsetPlaceholders
Unset multiple placeholders, either by prefix or an array of keys.
Syntax
API Doc: http://api.modxcms.com/modx/modX.html#unsetPlaceholders
void unsetPlaceholders (string|array $keys)
Example
Unset the 'my.name' and 'my.email' Placeholders.
$modx->unsetPlaceholders(array('my.name','my.email'));
Unset all Placeholders that are prefixed with 'my.'
$modx->unsetPlaceholders('my.');
See Also
modX.unsetPlaceholdermodX.setPlaceholdermodX.setPlaceholdersmodX.toPlaceholdermodX.toPlaceholders
modChunk
The modChunk Class
This is the Chunk base class for MODx Revolution.
Methods
See Also
Page: (at)CHUNK
Page: modChunk.getContent
Page: modChunk.setContent
Page: modChunk
Page: (at)CHUNK
Page: modChunk
Page: modChunk.getContent
Page: modChunk.setContent
modChunk.getContent
modChunk::getContent
Get the source content of this chunk.
Syntax
API Doc: http://api.modxcms.com/modx/modChunk.html#getContent
void getContent ([ $options = array()])
Example
$chunk = $modx->getObject('modChunk',array('name' => 'MyChunk')); ($chunk) {if
$content = $chunk->getContent();}
See Also
Page: (at)CHUNK
Page: modChunk.getContent
Page: modChunk.setContent
Page: modChunk
Page: (at)CHUNK
Page: modChunk
Page: modChunk.getContent
Page: modChunk.setContent
modChunk.setContent
modChunk::setContent
Sets the content of this Chunk.
Syntax
API Doc: http://api.modxcms.com/modx/modChunk.html#
void setContent ( $content, [ $options = array()])
Example
$chunk->setContent('<h2>Hello!</h2>');
See Also
Page: (at)CHUNK
Page: modChunk.getContent
Page: modChunk.setContent
Page: modChunk
Page: (at)CHUNK
Page: modChunk
Page: modChunk.getContent
Page: modChunk.setContent
modUser
The modUser Class
This is the base User class for MODx Revolution.
Methods
See Also
Page: Users
Page: modUser
Page: Users
Page: modUser
modUser.isAuthenticated
modUser::isAuthenticated
Determines if this user is authenticated in a specific context.
Separate session contexts can allow users to login/out of specific sub-sites individually (or in collections).
Syntax
API Doc: http://api.modxcms.com/modx/modUser.html#isAuthenticated
boolean isAuthenticated ([string $sessionContext = 'web'])
Example
See if the User is logged into the 'web' context. If not, deny access and send to Unauthorized Page.
if (!$modx->isAuthenticated('web')) { $modx->sendUnauthorizedPage();}
See Also
Page: Users
Page: modUser
Page: Users
Page: modUser
modUser.addSessionContext
modUser::addSessionContext
Adds a new context to the user session context array.
Syntax
API Doc: http://api.modxcms.com/modx/modUser.html#addSessionContext
void addSessionContext (string $context)
Example
Add a 'sports' Context session to the user.
$modx->addSessionContext('sports');
See Also
Page: Users
Page: modUser
Page: Users
Page: modUser
modUser.changePassword
modUser::changePassword
Change the User password.
Syntax
API Doc: http://api.modxcms.com/modx/modUser.html#changePassword
boolean changePassword (string $newPassword, string $oldPassword)
Example
Change the password from 'boo123' to 'b33r4me'
$modx->changePassword('b33r4me', 'boo123');
See Also
Page: Users
Page: modUser
Page: Users
Page: modUser
modUser.endSession
modUser::endSession
Ends a user session completely, including all contexts.
Syntax
API Doc: http://api.modxcms.com/modx/modUser.html#endSession
void endSession ()
Example
End the user's session.
$user->endSession();
See Also
modUser
modUser.getSessionContexts
modUser::getSessionContexts
Returns an array of user session context keys.
Syntax
API Doc: http://api.modxcms.com/modx/modUser.html#getSessionContexts
array getSessionContexts ()
Example
Get all user seesion contexts for this user that is logged into the web and mgr contexts:
$keys = $user->getSessionContexts();print_r($keys); // prints Array ( 'web', 'mgr' );
See Also
modUser
modUser.getSettings
modUser::getSettings
Gets all user settings in array format.
Syntax
API Doc: http://api.modxcms.com/modx/modUser.html#getSettings
array getSettings ()
Example
Get all the User Settings for this User.
$settings = $user->getSettings();
See Also
modUserSettings
modUser.hasSessionContext
modUser::hasSessionContext
Checks if the user has a specific session context, or in other words, is "logged into" a certain context.
Syntax
API Doc: http://api.modxcms.com/modx/modUser.html#hasSessionContext
boolean hasSessionContext (mixed $context)
Example
See if the User has a Session for the 'sports' Context:
if ($user->hasSessionContext('sports')) { // code heredo}
See Also
modUserContexts
modUser.isMember
modUser::isMember
States whether a user is a member of a group or groups. You may specify either a string name of the group, or an array of names.
Syntax
API Doc: http://api.modxcms.com/modx/modUser.html#isMember
boolean isMember (mixed $groups)
Example
See if the User is a member of the 'Staff' group:
$user->isMember('Staff');
See if the User is a member of either the 'Staff' or 'Investors' groups.
$user->isMember(array('Staff','Investors'));
See Also
modUser
modUser.loadAttributes
modUser::loadAttributes
Loads the principal attributes that define a modUser security profile.
Syntax
API Doc: http://api.modxcms.com/modx/modUser.html#loadAttributes
void loadAttributes ( $target, [ $context = ''], [ $reload = ])false
Example
Load attributes for the 'sports' context and the modResource target.
$user->loadAttributes('modResource','sports', );true
See Also
modUser
modUser.removeSessionContext
modUser::removeSessionContext
Removes a user session context.
Syntax
API Doc: http://api.modxcms.com/modx/modUser.html#removeSessionContext
void removeSessionContext (string|array $context)
Example
Remove the session for the User in the 'sports' Context.
$user->removeSessionContext('sports');
See Also
modUserContexts
modUser.removeSessionContextVars
modUser::removeSessionContextVars
Removes the session vars associated with a specific context.
Syntax
API Doc: http://api.modxcms.com/modx/modUser.html#removeSessionContextVars
void removeSessionContextVars (string $context)
Example
Remove all session vars for the User in the 'sports' Context.
$user->removeSessionContextVars('sports');
See Also
modUserContexts
modUser.removeSessionCookie
modUser::removeSessionCookie
Removes a session cookie for a user.
Syntax
API Doc: http://api.modxcms.com/modx/modUser.html#removeSessionCookie
void removeSessionCookie (string $context)
Example
Remove the Session Cookie for the User in the 'sports' Context.
$user->removeSessionCookie('sports');
See Also
Page: Users
Page: modUser
Page: Users
Page: modUser
Case Studies and Tutorials
Case Studies and Tutorials
This page contains a list of case studies and tutorials for various MODx Revolution scenarios.
Using Custom Database Tables in your 3rd Party Components
The ScenarioOur ModelBuilding the SchemaUsing our New ModelSee Also
So you're developing your custom component for MODx Revolution, and you've run into a dilemma. You've got some data that uses a table inyour MODx database, but you want a way to use xPDO's object model to access it. This tutorial will walk you through the process of creating yourown custom schema, adding it as an xPDO model package, and querying it.
The Scenario
So let's say we want to create a custom component called "StoreFinder" that takes a zip code from a textfield and then looks up all the storelocations with that zip code and returns them in a table. Currently we'll have one table for this: (note the prefix "modx_" - this is specific to your DBconnection, done in Revolution setup.)
modx_sfinder_stores
Our component will grab all the stores with the specified zip code. Our store table will have the following fields:
idnameaddresscitystatezipcountryphonefaxactive
So now that we've got an idea of what's in our tables, let's make the schema file that defines the model. This "schema" file is an XMLrepresentation of our database table(s). It is then parsed by xPDO into PHP-format "maps", which are array representations of the schema and itsrelationships.
Our Model
First off, let's take a quick look at our directory structure. This isn't always how you have to do it - this one is specifically built this way for SVN; butit's definitely recommended, especially with the structures, since that makes creating the transport package (shouldcore/components/storefinder/we want to distribute this via Package Management) much easier.
Now, on to the model file. In our _build directory, let's create a "schema" subfolder. Then, from there, we'll create a file called"storefinder.mysql.schema.xml". Note that "mysql" is in there - yes, eventually xPDO and Revolution will support other database platforms. But fornow, we're going to do this in MySQL.
In our XML file, let's start out with the first few lines:
<?xml version= encoding= ?>"1.0" "UTF-8"<model package= baseClass= platform= defaultEngine="storefinder" "xPDOObject" "mysql" "MyISAM"phpdoc-package= phpdoc-subpackage= >"storefinder" "model"
First we'll tell the browser and parser that this is XML code with a standard XML header. Next, we're going to create a "model" tag, and put someattributes into it. They are:
package - The name of the xPDO package (note this is different than a "transport package", a Revolution term). This is how xPDOseparates different models and manages them.baseClass - This is the base class from which all your class definitions will extend. Unless you're planning on creating a customxPDOObject extension, it's best to leave it at the default.platform - The database platform you're using. At this time, xPDO only supports mysql.defaultEngine - The default engine of the database tables, usually either MyISAM or InnoDB. MODx recommends using MyISAM.phpdoc-package & phpdoc-subpackage - These are custom attributes we're going to use in our map and class files. They're notstandard xPDO attributes, but show that you can put whatever you want as attributes.
Great! Now we've got our model definition. Let's add a table tag as the next line.
<object class= table= = >"sfStore" "sfinder_stores" extends "xPDOSimpleObject"
"Object" is our representation of a table, which will generate into an xPDOObject class when we're through. There are some attributes to notehere:
class - This is the name of the class we want to be generated from the table. Here, we'll use "sfStore". Note that instead of just "Store",we prefixed it with "sf" to prevent collisions with any other packages we might install that might also have Store tables.
table - This should point to the actual database table name, minus the prefix.extends - This is the class that it extends. Note that you can make subclasses and extended classes straight from the XML. Extendedclasses will inherit their parent class's fields.
You'll see here that this table extends "xPDOSimpleObject", rather than xPDOObject. This means that the table comes already with an "id" field,that is an auto-increment primary key.
Now that we've got a table definition for our stores table, let's add some field definitions to it:
<field key= dbtype= precision= phptype= = ="name" "varchar" "100" "string" null " "false default "" index="index" /><field key= dbtype= precision= phptype= = ="" />"address" "varchar" "255" "string" null " "false default<field key= dbtype= precision= phptype= = ="" />"city" "varchar" "255" "string" null " "false default<field key= dbtype= precision= phptype= = ="" />"state" "varchar" "255" "string" null " "false default<field key= dbtype= precision= phptype= = = index="zip" "varchar" "10" "string" null " "false default "0"
/>"index"<field key= dbtype= precision= phptype= = ="" />"country" "varchar" "20" "string" null " "false default<field key= dbtype= precision= phptype= = ="" />"phone" "varchar" "20" "string" null " "false default<field key= dbtype= precision= phptype= = ="" />"fax" "varchar" "20" "string" null " "false default<field key= dbtype= precision= attributes= phptype= = "active" " "int "1" "unsigned" "integer" null " "false
= />default "0"
As you can see here, each column in our table has a field definition tag. From there, we haveattribute properties for each field. Most of these are optional, depending on the database type of the column. Some of those attribute propertiesare:
key - The key name of the column.dbtype - The DB type - such as varchar, int, text, tinyint, etc.precision - The precision of the field. Usually this is the max number of characters.attributes - Only applies to some DB types; in integers you can set to "unsigned" to make sure that the value is always positive.phptype - The corresponding PHP type of the DB field type.null - If the field can be NULL or not.default - The default starting value of the field should none be set.index - An optional field, when set, will add a type of index to the field. Some of the values are "pk", "index", and "fk".
And we'll finish by closing the object and model tags:
</object></model>
So now we have a completed XML schema for our model! You can view the full version. Let's move on to the schema build script.here
Building the Schema
Go ahead and create a 'build.config.php' file in your _build directory. It should contain this:
<?phpdefine('MODX_BASE_PATH', dirname(dirname(dirname(dirname(dirname(__FILE__))))) . '/MODxRevolution/');define('MODX_CORE_PATH', MODX_BASE_PATH . 'core/');define('MODX_MANAGER_PATH', MODX_BASE_PATH . 'manager/');define('MODX_CONNECTORS_PATH', MODX_BASE_PATH . 'connectors/');define('MODX_ASSETS_PATH', MODX_BASE_PATH . 'assets/');
...where obviously MODX_BASE_PATH will need to point to where you installed MODx Revolution. If you moved the manager or core outside ofthat base path, you'll need to adjust those defines as well. From here, create a 'build.schema.php' file in your _build directory. At the top, put this:
<?php/** * Build Schema script * * @ storefinderpackage * @subpackage build */$mtime = microtime();$mtime = explode( , $mtime);" "$mtime = $mtime[1] + $mtime[0];$tstart = $mtime;set_time_limit(0);
require_once dirname(__FILE__) . '/build.config.php';include_once MODX_CORE_PATH . 'model/modx/modx.class.php';$modx= modX();new$modx->initialize('mgr');$modx->loadClass('transport.modPackageBuilder','', , );false true$modx->setLogLevel(modX::LOG_LEVEL_INFO);$modx->setLogTarget(XPDO_CLI_MODE ? 'ECHO' : 'HTML');
$root = dirname(dirname(__FILE__)).'/';$sources = array( 'root' => $root, 'core' => $root.'core/components/storefinder/', 'model' => $root.'core/components/storefinder/model/', 'assets' => $root.'assets/components/storefinder/', 'schema' => $root.'_build/schema/',);
This will do a few things. First off, it starts up a nice execution time script for us, so we can see how long it takes to build the schema. Secondly, Itincludes our build.config.php file to tell the schema script where to find MODx Revolution. Thirdly, it loads the necessary classes, initializes themodX object, and loads the modPackageBuilder class. And finally, it sets some log levels and some base paths for our build script.
Note that you'll want to make sure the $sources array has the correct paths defined; otherwise your script wont run. Let's add a couple more linesto our schema build script:
$manager= $modx->getManager();$generator= $manager->getGenerator();
These lines will load xPDOManager and xPDOGenerator, two classes we'll need to build our schema map files.
And finally, we want to actually parse this into a file:
$generator->parseSchema($sources['schema'].'storefinder.mysql.schema.xml', $sources['model']);
$mtime= microtime();$mtime= explode( , $mtime);" "$mtime= $mtime[1] + $mtime[0];$tend= $mtime;$totalTime= ($tend - $tstart);$totalTime= sprintf( , $totalTime);"%2.4f s"
echo ;"\nExecution time: {$totalTime}\n"
exit ();
This block of code executes the schema parsing method, and then outputs the total time the script took to execute. Run it, and viola! Ourstorefinder/core/model/storefinder/ directory is now filled with all of our map and class files!
Using our New Model
You may be asking, "Okay, that's great. Now how do I these?" Well, xPDO makes it incredibly simple. Let's first create our snippet file in ourusecore/components/storefinder/ directory, and call it 'snippet.storefinder.php' -- we're going to tie into a file on the file system because it's easier toedit it using a text editor, and we want a file on the file system for our build package.
Before we proceed, let's enable testing of this snippet directly from MODx. Since we're developing this in a separate directory from our MODxinstall, let's create a snippet called 'StoreFinder' in our MODx Revolution instance, and put this inside of it (you'll need to change the first line tothe correct path):
$base_path = dirname(dirname($modx->getOption('core_path'))).'/MODxComponents/tutorials/storefinder/trunk/core/components/storefinder/';/* change above line to your path */
$o = '';$f = $base_path.'snippet.storefinder.php';
(file_exists($f)) {if $o = include $f;} {else $modx->setLogTarget('ECHO'); $modx->log(modX::LOG_LEVEL_ERROR,'StoreFinder not found at: '.$f);}
$o;return
This little helper code allows us to do our development in our own code editor of choice until we're ready to package and distribute ourComponent. Then we'll simply delete this 'StoreFinder' snippet from our MODx Revolution instance, and install our package. You can find moreabout building packages by going . If you don't want to build a transport package (we recommend doing so, it makes upgrades FAR easier!),hereyou can simply just copy the files to their proper directories in the manager.
Okay, back to our snippet. Open up 'snippet.storefinder.php' in your editor, and add this code:
<?php/** * @ storefinderpackage */$base_path = !empty($base_path) ? $base_path :$modx->getOption('core_path').'components/storefinder/';
You'll see here that we're setting a $base_path variable if and only if it's not already set. Why? Well, this allows us to do development outside ourtarget directory (like we're doing now). If no base_path is set, then we simply point it to where the component will be installed:core/components/storefinder/
Now on to the brunt of the code. You've got your snippet working, you're in an easy development environment, and now you're ready to get thatmodel working. First off, add these lines:
$modx->addPackage('storefinder',$base_path.'model/');
This will add the package to xPDO, and allow you to use all of xPDO's functions with your model. Let's test it out:
$stores = $modx->getCollection('sfStore');echo 'Total: '.count($stores);
Note the first time you run this, it might throw an error. This is because xPDO is dynamically creating your database table from your schema. Afterrunning, it should show "Total: 0".
Let's add a few records into the database for testing. Above the getCollection call, add:
$store = $modx->newObject('sfStore');$store->fromArray(array( 'name' => 'Store 1', 'address' => '12 Grimmauld Place', 'city' => 'London', 'country' => 'England', 'zip' => '12345', 'phone' => '555-2134-543',));$store->save();
$store = $modx->newObject('sfStore');$store->fromArray(array( 'name' => 'Store 2', 'address' => '4 Privet Drive', 'city' => 'London', 'country' => 'England', 'zip' => '53491', 'phone' => '555-2011-978',));$store->save();
Run this (unless you want duplicate data). That should populate your table with some data, and then output 'Total: 2', assuming youonly oncedidn't remove the getCollection lines. After you've run it, go ahead and erase those lines.
Okay, now we've got our model running smoothly! For those of you who are already familiar with component development, the second part of thistutorial will be dealing with finishing our Component's scenario. You can stop reading if you want.
Part 2 Coming Soon...
See Also
Generating the xPDO Model Codehttp://svn.modxcms.com/docs/display/revolution/PHP+Coding+in+MODx+Revolution%2C+Pt.+I
Creating a Blog in MODx Revolution
Creating a Blog in MODx RevolutionGetting the Needed Extras
Needed ExtrasOptional Extras
Creating your Blog Post TemplateHeader and FooterThe Post InfoThe Post ContentAdding Comments to Posts
Setting up TaggingCreating the Sections
Setting up the blogPost ChunkSetting up Your Blog HomeAdding Posts
Page Structure Within the SectionsAdding a New Blog Post
Setting up Your ArchivesCreating the Archives ResourceSetting up the Archivist Widget
Advanced OptionsAdding a Moderator GroupAdding a "Latest Posts" widgetAdding a "Latest Comments" widgetAdding a "Most Used Tags" widget
Conclusion
Creating a Blog in MODx Revolution
This tutorial is here to help you setup a flexible, powerful blogging solution in MODx Revolution. Since MODx Revolution is not blogging software,
but rather a full-blown Content Application Platform, it doesn't come pre-packaged with a cookie-cutter blogging solution. You'll need to setup yourblog how you want it.
Fortunately, the tools to do so are already there for your taking. This tutorial will walk you through how to set them up. It's recommended thatyou're familiar with Revolution's before we start.Tag Syntax
One thing before we start, though - this tutorial is extensive, and will show you how to set up a powerful blog with posting, archiving, tagging,commenting and more. If you don't need any specific part, just skip that part. MODx is modular, and your blog can function in any scope you like.And, again, this is only one way to do it - there are tons of ways to setup a blog in MODx Revolution.
This tutorial was based on the blog setup at . If you'd like a demo before reading, try there.splittingred.com
Getting the Needed Extras
First off, you'll want to go ahead and download and install some Extras that we'll be using in our Blog. The following is a list of mostcommonly-used Extras:
Needed Extras
getResources - For listing posts, pages and other Resources.getPage - For pagination of listings.Quip - For anything and everything in commenting.tagLister - For managing tags and doing tag-based navigation.Archivist - For managing your Archives section.
Optional Extras
Breadcrumbs - For displaying a breadcrumb navigation trail.Gallery - For managing photo Galleries.SimpleSearch - For adding a simple search box to your site.getFeed - If you want to grab other feeds in your site, such as a Twitter feed.Login - If you want to restrict commenting to logged in users only, you'll need this.
Creating your Blog Post Template
First off, you'll want to have a Template that's geared just for Blog Posts. Why? Well, if you want comments and special formatting or pagedisplays for your blog, you'll probably not want to have to do that for each Blog Post. So, the best route is to setup your own blog post template.This tutorial already assumes you have a base Template for your normal pages on the site - we'll reference that later on as 'BaseTemplate'.
We'll create one called 'BlogPostTemplate'. Our content looks something like this:
[[$pageHeader]]<div id= class= >"content" "blog-post" <h2 class= ><a href= >[[*pagetitle]]</a></h2>"title" "[[~[[*id]]]]" <p class= >"post-info" Posted on [[*publishedon:strtotime:date=`%b %d, %Y`]] | Tags: [[*tags:notempty=`[[!tolinks? &items=`[[*tags]]` &key=`tag` &target=`1`]]`]] | <a href= class= >"[[~[[*id]]]]#comments" "comments" Comments ([[!QuipCount? &thread=`blog-post-[[*id]]`]]) </a> </p>
<div class= >"entry" <p>[[*introtext]]</p> <hr /> [[*content]] </div>
<div class= >"postmeta" [[*tags:notempty=`<span class= >Tags: [[!tolinks? &items=`[[*tags]]` &key=`tag` &target=`1`]]</span>"tags" `]] <br class= />"clear" </div> <hr /> <div class= id= >[[!Quip?"post-comments" "comments" &thread=`blog-post-[[*id]]` &replyResourceId=`123` &closeAfter=`30` ]] <br /><br /> [[!QuipReply? &thread=`blog-post-[[*id]]` ¬ifyEmails=`[email protected]` &moderate=`1` &moderatorGroup=`Moderators` &closeAfter=`30` ]] </div></div>[[$pageFooter]]
So let's examine the Template, shall we? As we go, remember this - you can move any of these pieces around, change their parameters, andadjust their placing. This is solely a base structure - if you want your tags at the bottom, for instance, move them there! MODx doesn't restrict youfrom doing that.
Header and Footer
First off, you'll notice that I have two chunks: "pageHeader" and "pageFooter". These chunks contain my common HTML tags that I would put inthe footer and header across my site, so I can use them in different templates. Useful if I don't want to have to update any header/footer changesin each of my Templates - I can just do it in one chunk. After that, I put the pagetitle of my Resource, and make it a link that takes you to the samepage.
The Post Info
Next we get into the "info" of the post - basically the author and tags for the post. Let's look in detail:
<p class= >"post-info"Posted on [[*publishedon:strtotime:date=`%b %d, %Y`]] | Tags: [[*tags:notempty=`[[!tolinks? &items=`[[*tags]]` &key=`tag` &target=`1`]]`]] | <a href= class= >"[[~[[*id]]]]#comments" "comments" Comments ([[!QuipCount? &thread=`blog-post-[[*id]]`]])</a></p>
The first part takes the publishedon Resource field, and formats it into a nice, pretty date.
Secondly, we then display a Tag listing for this Blog Post. You can see how we reference a "tags" Template Variable - we haven't created this justyet, so dont worry - and then pass it as a property to the 'tolinks' snippet. The tolinks snippet comes with , and translates delimited tagstagListerinto links. This means our tags become clickable! We've specified a 'target' Resource of 1, or our home page. If your blog was in another pagebesides home, you'd change the ID number there.
And finally, we load a quick count of the number of comments, along with a clickable anchor tag link to load them. Note how our 'thread' propertyin the QuipCount snippet call (and later on in the Quip call) uses 'blog-post-[*id]'. This means that MODx will automatically create a new thread foreach new Blog Post we create. Neat!
The Post Content
Okay, back to our Template. We're in the content section now - note how we start with [[*introtext]]. This is a useful MODx Resource field - think ofit like a beginning excerpt for a blog post, that will show up on our main page when we're listing the latest blog posts.
Adding Comments to Posts
Okay, now we're in the comments part of BlogPostTemplate. As you can see here, we're using for our commenting system. You could feelQuipfree to use another system, such as Disqus, here if you choose. For this tutorial, we'll go with Quip. Our code is as follows:
<div class= id= >[[!Quip?"post-comments" "comments" &thread=`blog-post-[[*id]]` &replyResourceId=`19` &closeAfter=`30` ]] <br /><br /> [[!QuipReply? &thread=`blog-post-[[*id]]` &moderate=`1` &moderatorGroup=`Moderators` &closeAfter=`30` ]]</div>
Okay, cool. Note we have two Snippet calls here - one for displaying the comments for this thread ( ), and another for displaying the replyQuipform ( ).QuipReply
In our Quip snippet call, we've specified a thread ID in the manner we've described above, and then set some settings. Our comments are goingto be threaded (the default), so we need to specify a Resource ID where our Reply to Thread post is going to be (this is detailed in the Quip
. We recommend reading there for how to set it up.) with the 'replyResourceId' property.Documentation
Next, we want to specify - in both the Quip and Quip Reply calls - a 'closeAfter' property. This tells Quip to automatically close commenting onthese threads after 30 days of the thread creation (when we load it).
In our QuipReply call, we want to tell Quip to moderate all posts, and the moderators for our post can be found in the Moderators User Group(we'll explain how to set this up later in the tutorial).
There's a whole bunch of other Quip settings we could change, but we'll leave you to further customization, which you can find out how to do inthe .Quip docs
Setting up Tagging
Now that we've got our Template all setup, we need to setup the 'tags' Template Variable that we'll be using for our Tagging.
Go ahead and create a TV called 'tags', and give it a description of "Comma delimited tags for the current Resource." Next, make sure it hasaccess to the 'BlogPostTemplate' Template we created earlier.
That's it! Now you'll be able to add tags to any blog post we create, simply when editing your Resource by specifying a comma-separated list oftags.
Creating the Sections
If you want your blog to have 'Sections' (also called Categories), you'll first need to create those Resources.
For this tutorial's purpose, we'll create 2 sections: "Personal" and "Technology". Go ahead and create 2 Resources in the root of your site, andmake them 'containers'. You'll want to have their alias be 'personal' and 'technology', so your blog post URLs turn up nicely.
We'll say from here on out that our two Section Resources have IDs of 34 and 35, for reference.
Make sure you don't use the BlogPostTemplate on these, and use instead your own Base Template. These pages will end up being a way tobrowse all posts within a certain Section. In the content of these Resources, go ahead and put the following:
[[!getResourcesTag? &element=`getResources` &elementClass=`modSnippet` &tpl=`blogPost` &hideContainers=`1` &pageVarKey=`page` &parents=`[[*id]]` &includeTVs=`1` &includeContent=`1`]][[!+page.nav:notempty=`<div class= > "paging"<ul class= > "pageList" [[!+page.nav]] </ul> </div>`]]
Okay, let's explain this. getResourcesTag a wrapper snippet for and that automatically filters results by a 'tags' TV. SogetResources getPagebasically, we want to grab all published Resources within this section (and we can also filter by tag should we pass a '?tag=TagName' parameter
into the URL.
Below the getResourcesTag call, we put our pagination links, since by default getResourcesTag only shows 10 posts per page.
Setting up the blogPost Chunk
In that call, we also have a property called 'tpl' which we set to 'blogPost'. This is our Chunk that shows each result of our blog post listings. Itshould contain this:
<div class= >"post" <h2 class= ><a href= >[[+pagetitle]]</a></h2>"title" "[[~[[+id]]]]" <p class= >Posted by [[+createdby:userinfo=`fullname`]]"post-info" [[+tv.tags:notempty=` | <span class= >Tags: "tags"[[!tolinks? &items=`[[+tv.tags]]` &key=`tag` &target=`1`]]</span>`]]</p> <div class= >"entry" <p>[[+introtext: =`[[+content:ellipsis=`400`]]`]]</p>default </div> <p class= >"postmeta" <span class= >"links"<a href= class= >Read more</a>"[[~[[+id]]]]" "readmore"| <a href= class= >"[[~[[+id]]]]#comments" "comments" Comments ([[!QuipCount? &thread=`blog-post-[[+id]]`]]) </a>| <span class= >[[+publishedon:strtotime:date=`%b %d, %Y`]]</span>"date" </span> </p></div>
Cool - let's dive in. We start out by making a clickable link to the post with the pagetitle as the title. Then, we set our 'posted by' part and tag listing(similar to how we did it earlier in BlogPostTemplate).
Next, we show some of the excerpt of the content - which we store in the 'introtext' field on the content. We're also going to say that if introtext isempty, go ahead and instead just grab the first 400 characters of the content field, and add an ellipsis (...) to it if it's more than 400.
After that, we have a nice little 'read more' link which links to the post, and then our comments and publishedon date. That's it!
Setting up Your Blog Home
In our home page for our blog, which we've got in Resource ID 1 - our site start - we've got this:
[[!getResourcesTag? &elementClass=`modSnippet` &element=`getResources` &tpl=`blogPost` &parents=`34,35` &limit=`5` &includeContent=`1` &includeTVs=`1` &showHidden=`0` &hideContainers=`1` &cache=`0` &pageVarKey=`page`]][[!+page.nav:notempty=`<div class= > "paging"<ul class= > "pageList" [[!+page.nav]] </ul> </div>`]]
This allows us to show all posts from the two sections we've made, in Resources 34 and 35. It also allows us to filter by tag (since all our 'tolinks'and 'tagLister' calls have a target of 1 (this Resource's ID). In other words, by putting our getResourcesTag call here, we have automatic tagging!
You could easily make this another page than your site_start (or ID 1) - just make sure to change the 'target' properties in your tagLister andtolinks Snippet calls to reflect that.
Adding Posts
Okay, now we're ready to actually add blog posts, now that our structure is all setup.
Page Structure Within the Sections
Before we start, though, it's important to note that how you structure your posts within the section is totally up to you. You can add year and monthcontainer Resources to put these posts in, or just post them directly within the section. It's totally up to you.
If you choose to have date/year or sub-containers, make sure they have Hide from Menus checked, so that they wont show upin your getResources calls.
Remember, though, that whatever structure you build under the sections, that's not going to determine your navigation - will handle that.ArchivistWhat it will determine, however, is the URL of your blog posts. So have fun.
Adding a New Blog Post
Okay - go ahead and create a new Resource, and set it's Template to 'BlogPostTemplate'. Then you can start writing your post. You can specifyin the 'introtext' field the excerpt for the blog post, and then write the full body in the content field.
And finally, when you're done, make sure to specify the tags for your post in your newly created 'tags' TV!
Setting up Your Archives
Great - you have your first blog post! And, you've got it so you can browse it in Sections as well. Now, you're going to want to set up some way ofbrowsing old blog posts. This is where 'Archvist' comes into play.
Creating the Archives Resource
Go ahead and place a Resource in your root called 'Archives', and give it an alias of 'archives'. Then inside the content, place this:
[[!getPage? &element=`getArchives` &elementClass=`modSnippet` &tpl=`blogPost` &hideContainers=`1` &pageVarKey=`page` &parents=`34,35` &includeTVs=`1` &toPlaceholder=`archives` &limit=`10` &cache=`0`]]
<h3>[[+arc_month_name]] [[+arc_year]] Archives</h3>
[[+archives]]
[[!+page.nav:notempty=`<div class= > "paging"<ul class= > "pageList" [[!+page.nav]] </ul> </div>`]]
Look familiar? It's very similar to getResourcesTag, described above in our Section page. This time, getPage is wrapping the snippet,getArchivesand saying that we want to grab posts in Resources 34 and 35 (our Section pages). We'll set the result to a placeholder called 'archives' which wereference later.
Then, below that, we add a few placeholders that show the current browsing month and year. And finally, we have our pagination. Cool! We'redone with that. Our Resource, for reference purposes, we'll say has an ID of .30
Setting up the Archivist Widget
Okay, so now you've got a Resource to browse archives in, but you need some way of generating the months that lists posts. That's actuallypretty simple - somewhere on your site (say, in your footer, put this nice little bit:
<h3>Archives</h3><ul>[[!Archivist? &target=`30` &parents=`34,35`]]</ul>
So what the Snippet does is generate a month-by-month list of posts (you can add all kinds of other options, but see Archivist it's documentationfor that). We are saying we want its links to go to our Archives Resource (30), and to only grab posts in the Resources 34 and 35 (our SectionResources).
That's it! Archivist will actually automatically handle the rest - including all your URL generation for archives - archives/2010/05/ will show all theposts within May 2010, where archives/2009/ will show all posts in 2009. Pretty sweet, huh?
Advanced Options
Adding a Moderator Group
So earlier, in our QuipReply call, we specified a moderatorGroup of 'Moderators'. Let's go ahead and create that User Group now.
Go to Security -> Access Controls, and create a new User Group called 'Moderators'. Add any users you want in the group (including yourself!)and give them whatever role you want.
Then, go to the Context Access tab. Add an ACL (a row, basically) that gives this user group access in the 'mgr' context, with a minimum role ofMember (9999), and the Access Policy of 'QuipModeratorPolicy'.
What this does is allow anyone in the 'Moderators' usergroup to moderate posts in your threads, and also notifies them via email when new postsare made. They can then either login to the manager to moderate comments, or click on links directly in the emails to approve or reject thecomments. Your ACL should look something like this:
Save your User Group, and that's it! You might have to flush sessions (Security -> Flush Sessions) and re-login to reload your permissions, butQuip will handle the rest.
Adding a "Latest Posts" widget
You're probably going to want a "Latest Posts" somewhere on the site, and no fear - adding it is quite easy.
First off, you'll want to place this call wherever you want the list to appear:
[[!getResources? &parents=`34,35` &hideContainers=`1` &tpl=`latestPostsTpl` &limit=`5` &sortby=`publishedon`]]
So we're telling to display a top 5 list of Resources in your Section Resources (34,35), and sort by their publishedon date.getResources
Then, create the `latestPostsTpl` chunk, which you've specified with the 'tpl' call in the getResources snippet call. Put this as the chunk's content:
<li> <a href= >[[+pagetitle]]</a>"[[~[[+id]]]]" [[+publishedon:notempty=`<br /> - [[+publishedon:strtotime:date=`%b %d, %Y`]]`]]</li>
And boom! Latest blog posts displaying on your site:
Adding a "Latest Comments" widget
What about a widget that shows a few of the latest comments across your posts? Simple - Quip packages a nice little snippet called that can handle this easily.QuipLatestComments
Place the call wherever you want the comment list to show:
[[!QuipLatestComments? &tpl=`latestCommentTpl`]]
Now create a chunk called 'latestCommentTpl':
<li class= >"[[+cls]][[+alt]]" <a href= >[[+body:ellipsis=`[[+bodyLimit]]`]]</a>"[[+url]]" <br /><span class= >by [[+name]]</span>"author" <br /><span class= >[[+createdon:ago]]</span>"ago"</li>
Before we proceed, there's a few things to note - QuipLatestComments will automatically truncate the comment and add an ellipsis past the&bodyLimit property passed into it, which defaults to 30 characters. Secondly, note the 'ago' we used here. This filter is built intoOutput FilterMODx Revolution, and translates a timestamp into a nice, pretty 'two hours, 34 minutes' (or two other time metrics, such as min/sec, year/mo,mo/week) format.
Note also that it will default to showing the 5 latest. The result:
You can see the for more configuration options.documentation for the snippet
Adding a "Most Used Tags" widget
This part is ridiculously easy; does this for you. Just place this wherever you want:tagLister
[[!tagLister? &tv=`tags` &target=`1`]]
And tagLister will check the TV 'tags', and create links that go to the target (here, Resource ID 1) with the top 10 tags being used. There's a tonmore , but we'll leave you with this.configuration options
Conclusion
So we've got a full blog setup! It should look something like this in our tree now:
Again, there's far more customization and things you could add to your blog. This tutorial is meant as a starting point, but feel free to customizeand add things to your liking - the great part about MODx is that you can very easily customize, tweak and scale any solution: including a blog!
Remember, this tutorial was based off of , if you'd like to see a full-scale demo of it in action.splittingred.com
PHP Coding in MODx Revolution, Pt. I
The Simple HowThe ModelSee Also
So, a lot of people have been asking about the new codebase. Is it coder-friendly? Will it be a big deviation from 0.9.6/Evolution? Does it supportOOP projects? Is it faster? Will it be easy to learn?
In these tutorials, we plan to answer those questions with a resounding, "yes."
The codebase in Revolution has switched to , an object relational bridge modeling tool built by Jason Coward. In layman's terms, thisxPDOmeans that all the database tables are now represented by PHP objects (which you'd expect with any ORM). Chunks are represented by'modChunk' objects, snippets by 'modSnippet' objects and so on.
The Simple How
So, how does one actually get an object in the new modx? Well, used to, you had to do and remember a myriad of different functions:
// The old way of doing things in MODx 1.x and earlier$doc = $modx->getDocument(23);$doc = $modx->getDocument(45,'pagetitle,introtext');$chunk = $modx->getChunk('chunkName');
// or even more convoluted$res = $modx->db->select('id,username',$table_prefix.'.modx_manager_users');$users = array();
($modx->db->getRecordCount($res))if{ ($row = $modx->db->getRow($res)) {while array_push($users,$row); }}
$users;return
Not anymore. Things are much simpler, and there's really only a few functions you'll need. Lets look at some examples:
// getting a chunk with ID 43$chunk = $modx->getObject('modChunk',43);
// getting a chunk with name 'TestChunk'$chunk = $modx->getObject('modChunk',array( 'name' => 'TestChunk'));
// getting a collection of chunk objects, then outputting their names$chunks = $modx->getCollection('modChunk');foreach ($chunks as $chunk) { echo $chunk->get('name'). ;"<br />\n"}
// getting a resource (i.e. a page) that is published, with a alias of 'test'$document = $modx->getObject('modResource',array( 'published' => 1, 'alias' => 'test',));
The Model
So, you're probably asking, Where is the list of table names to object names map? It can be found in"core/model/schema/modx.mysql.schema.xml". (You'll note the 'mysql' - yes, this means that MODx will in the near future support otherdatabases) From there you can view an XML representation of all the MODx DB tables.
For example, modChunk:
<object class= table= = >"modChunk" "site_htmlsnippets" extends "modElement" <field key= dbtype= precision= phptype= = ="name" "varchar" "50" "string" null " "false default "" index="unique" /> <field key= dbtype= precision= phptype= = ="description" "varchar" "255" "string" null " "false default
/>"Chunk" <field key= dbtype= precision= phptype= = = />"editor_type" " "int "11" "integer" null " "false default "0" <field key= dbtype= precision= phptype= = = />"category" " "int "11" "integer" null " "false default "0" <field key= dbtype= precision= phptype= = ="cache_type" "tinyint" "1" "integer" null " "false default "0"/> <field key= dbtype= phptype= />"snippet" "mediumtext" "string" <field key= dbtype= precision= attributes= phptype= ="locked" "tinyint" "1" "unsigned" " "boolean null "
= />"false default "0" <aggregate alias= class= key= local= foreign="Category" "modCategory" "id" "category" "id"cardinality= owner= />"one" "foreign"</object>
You can also define your own schemas for your own components and add them as packages - more on that in a future article. Lets go into theschema:
<object class= table= = >"modChunk" "site_htmlsnippets" extends "modElement"
The property tells you what the name of the class will be. The property shows the actual MySQL table, and shows what objectclass table extendsit extends. modElement is a base class for all Elements in MODx - snippets, modules, chunks, templates, etc.
<field key= dbtype= precision= phptype= = ="name" "varchar" "50" "string" null " "false default "" index="unique" />
This tag represents a column in the database. Most of these attributes are pretty straightforward.
<aggregate alias= class= key= local= foreign= cardinality="modCategory" "modCategory" "id" "category" "id" owner= />"one" "foreign"
Okay, this is where we get into DB relationships. An relationship is a relationship where, in laymans terms, if you were to delete thisAggregatechunk, it wouldn't delete the Category that it's related to. If it were a relationship, it would. There is "dependence" in the CompositeComposite
relationship that is related to the other object. For an example, let's get all the modContextSettings for a modContext:
$context = $modx->getObject('modContext','web');$settings = $context->getMany('ContextSettings');foreach ($settings as $setting) { echo 'Setting name: '.$setting->get('key').' <br />'; echo 'Setting value: '.$setting->get('value').' <br />';}
Pretty easy, huh? We'll get into creating and removing objects, as well as more complex queries, such as inner joins, limits, sorting and others, inthe .next article
See Also
xPDO: Defining a SchemaxPDO: Related Objects
PHP Coding in MODx Revolution, Pt. II
Creating ObjectsRemoving an ObjectMore Complex QueriesSee Also
In this article, we'll talk about creating and removing objects (and their respective rows in the database), as well as more complex queries.
Creating Objects
Creating objects is pretty simple. It's important to note, however, that a row is never actually added to the database until the object's save()command is run. So, on to the code:
// let's create a Template$template = $modx->newObject('modTemplate');
// now, lets save some data into the fields$template->set('templatename','TestTemplate');$template->set('description','A test template.');
// we could have also done it like :this$data = array( 'templatename' => 'TestTemplate', 'description' => 'A test template.',);$template->fromArray($data);
// okay, now we're ready. let's save. ($template->save() === ) {if false
die('An error occurred saving!');while}
It's that simple.
Removing an Object
Okay, so assuming we have the same previous object and now want to remove it (you could also grab another object, of course), the code issimply:
$template->remove();
Yes. That's it. Done. It will also remove any composite relationships - with modTemplates, these are the modTemplateVarTemplate objects, whichmap Templates to TVs. Those will cascade and be removed.
More Complex Queries
Okay, so obviously you are going to need to do some more complex queries than we've dealt with. That's where the xPDOQuery object comes in.This allows you to build abstract query objects that emulate more advanced SQL commands. So, lets try to grab the third 10 resources (so 21-30),ordered by menuindex, that are either 1) published and searchable, or 2) created by the user with username 'george123'.
$c = $modx->newQuery('modResource');$c->leftJoin('modUser','PublishedBy');$c->where(array( 'modResource.published' => 1, 'modResource.searchable' => 1,));$c->orCondition(array( 'PublishedBy.username' => 'george123',), ,1);null$c->sortby('menuindex','ASC');$c->limit(10,20);
$resources = $modx->getCollection('modResource',$c);
A couple of things to note. One, note that innerJoin first passes the class name, then the alias. And in orCondition, the 3rd parameter is the groupnumber, which effectively groups the conditions into proper parenthesis (the first 2 in the first parenthetical group, the 3rd in another).
xPDOQuery supports the the methods: join, rightJoin, leftJoin, innerJoin, andCondition, orCondition, sortby, groupby, limit, bindGraph,bindGraphNode, and select.
Obviously, you can go pretty wild here with complex queries. The nice thing about xPDO in MODx is that there's really a ton of different ways todo most things - you could also have used $modx->getCollectionGraph for this as well.
In the , we'll talk about how this is used in the context of MODx processors with JSON.next article
See Also
xPDO: Creating ObjectsxPDOObject::removexPDOQuery
PHP Coding in MODx Revolution, Pt. III
In MODx, form processing is handled by 'Processors', which are isolated files located in the MODx core directory. They are accessed through'Connectors', which handle AJAX requests from the User Interface (UI), which require a REQUEST variable named 'action' that specifies whichprocessor to send to. Processors are sent the sanitized REQUEST data, and then when finished respond with a JSON message back to thebrowser.
This allows for quick, easy requests that reduce the load on the server and the browser. You can also do multiple, asynchronous requests toprocessors in this method.
We'll look in-depth at the processor for creating a Chunk, and show you how MODx processors work.
First off, let's assume that we're sending the following data into the POST array to the connector, which has the REQUEST "action" variable set to'create', loading the proper create.php variable. In the JS, the connector is MODx.config.connectors_url+'element/chunk.php, which resolves to (inour default setup):
/modx/connectors/element/chunk.php
From there the connector will verify the request, and then send it to the proper processor, at:
/modx/core/model/modx/processors/element/chunk/create.php
And now on to the processor:
<?php/** * @ modxpackage * @subpackage processors.element.chunk */$modx->lexicon->load('chunk');
First off, we include the root index.php file for the processors, which does some slight variable checking and includes licensing. Then, we load theproper lexicon foci. In MODx Revolution, i18n language files are separated into smaller files by their 'foci', which is a term we've coined for 'focusarea'. Here, we want all language strings with foci 'chunk'. This saves processing power by only loading relevant i18n strings.
if (!$modx->hasPermission('new_chunk')) $modx->error->failure($modx->lexicon('permission_denied'));
This checks to make sure the user has the correct permissions to run this processor. If not, then it sends a failure response back to the browservia $modx->error->failure(). The response is a string message translated via the lexicon.
// valuesdefaultif ($_POST['name'] == '') $_POST['name'] = $modx->lexicon('chunk_untitled');
// get rid of invalid chars$_POST['name'] = str_replace('>','',$_POST['name']);$_POST['name'] = str_replace('<','',$_POST['name']);
// the name already exists chunk, send back an errorif for this$name_exists = $modx->getObject('modChunk',array('name' => $_POST['name']));
($name_exists != ) $modx->error->failure($modx->lexicon('chunk_err_exists_name'));if null return
Note now how we're sanitizing variables, and checking to make sure there already isn't a Chunk with this name.
// category$category = $modx->getObject('modCategory',array('id' => $_POST['category']));
($category == ) {if null $category = $modx->newObject('modCategory'); (empty($_POST['category'])) {if $category->set('id',0); } {else $category->set('category',$_POST['category']); $category->save(); }}
Okay, here, we allow dynamic Category creation. If the category specified exists, it will later assign it to that category. If not, then it creates thecategory in the database and prepares it for later association to the Chunk.
// invoke OnBeforeChunkFormSave event$modx->invokeEvent('OnBeforeChunkFormSave',array( 'mode' => modSystemEvent::MODE_NEW, 'id' => $_POST['id'],));
Events are pretty much the same invoke-wise in Revolution as they were in 096 - however they are more optimized in their loading.
$chunk = $modx->newObject('modChunk', $_POST);$chunk->set('locked',isset($_POST['locked']));$chunk->set('snippet',$_POST['chunk']);$chunk->set('category',$category->get('id'));
($chunk->save() === ) {if false $modx->error->failure($modx->lexicon('chunk_err_save'));return}
Important: note the 2nd parameter of the newObject() method. This is basically the same as $obj->fromArray() - it allows you to specify an array ofkey-value pairs to assign to the new object.
// invoke OnChunkFormSave event$modx->invokeEvent('OnChunkFormSave',array( 'mode' => modSystemEvent::MODE_NEW, 'id' => $chunk->get('id'),));
Again, more event invoking.
// log manager action$modx->logManagerAction('chunk_create','modChunk',$chunk->get('id'));
Now, how manager actions work in Revolution is a little different. This stores a lexicon string key ('chunk_create'), the class key of the objectbeing modified, and the actual ID of the object. This allows for more detailed manager action reporting.
$cacheManager= $modx->getCacheManager();$cacheManager->clearCache();
Let's simply and easily clear the cache. Pretty easy, huh?
return $modx->error->success('',$chunk->get(array('id', 'name', 'description', 'locked','category')));
Now, send a success response back to the browser. The parameters of $modx->error->success() are as follows:
1: $message - A string message to send back. Used to report details about a success (or failure).2: $object - An xPDOObject or array of data fields to convert into JSON and send back to the browser.
So basically, here, we're sending back the Chunk information - minus the content, which could be big and unnecessary and complicated to send.This will allow the UI to handle the creation properly.
Next, we'll talk about how to create your own schemas and add them dynamically into the MODx framework, without having to modify the core.
Loading Pages in the Front-End via AJAX and jQuery Tabs
The ProblemCreating the ResourcesDoing the Front-End LoadingWait, I want the Page Titles as the tab headers!
Using getResourcesUsing WayfinderUsing a getField Snippet
Conclusion
The Problem
We want in our site to use to load our Resources via AJAX. How do we do that in MODx? This tutorial will show you just how easy itjQuery's tabsis to accomplish this in MODx Revolution.
Creating the Resources
In the Resources you want to load via the tabs, you'll need to just create all your Resources with the Template being (or a minimal templateblankwith only the things you want inside the tabs). This will make sure that we're not loading anything besides the wanted material - you wouldn't wantto load your whole page header and footer into each tab!
Doing the Front-End Loading
Now we'll use jQuery's fun tabs() command to create the front-end loading system. The code would look something like this (pulled from jqueryUI's docs):
<script type= >"text/javascript"$(function() { $( ).tabs(); });"#tabs"</script>
<div id= >"tabs" <ul> <li><a href= >Resource with ID 92</a></li>"[[~92]]" <li><a href= >Resource with ID 546</a></li>"[[~546]]" <li><a href= >Resource with ID 123</a></li>"[[~123]]" </ul></div>
Great! So this loads the pages via Ajax.
Wait, I want the Page Titles as the tab headers!
There are a few ways you can do this; one, you can use , , or use a getField snippet.getResources Wayfinder
Using getResources
For getResources, make sure you use the 'tpl' property, which you can create as a Chunk named 'myRowTpl' (or whatever you want), looks likethis:
<li id= ><a href= title= >[[+pagetitle]]</a></li>"[[+id]" "[[~[[+id]]]]" "[[+longtitle]]"
and in our tabs page:
<script type= >"text/javascript"$(function() { $( ).tabs(); });"#tabs"</script>
<div id= >"tabs" <ul>[[getResources? &parents=`123` &depth=`1` &tpl=`myRowTpl` &includeContent=`1` &includeTVs=`1`]] </ul></div>
Using Wayfinder
For Wayfinder, make sure your rowTpl template, which you can create as a Chunk named 'myRowTpl' (or whatever you want), looks like this:
<li[[+wf.id]][[+wf.classes]]><a href= title= >[[+wf.linktext]]</a></li>"[[+wf.link]]" "[[+wf.title]]"
and in our tabs page:
<script type= >"text/javascript"$(function() { $( ).tabs(); });"#tabs"</script>
<div id= >"tabs" <ul>[[Wayfinder? &startId=`123` &level=`1` &rowTpl=`myRowTpl`]] </ul></div>
Using a getField Snippet
Or, you can use a Snippet such as this one to grab the pagetitle:
<?php/** * Grabs a field a specified Resourcefor *//* setup some properties */default$id = $modx->getOption('id',$scriptProperties, );false$field = $modx->getOption('field',$scriptProperties,'pagetitle');
($id) { /* grab the resource object */if $resource = $modx->getObject('modResource',$id); ($resource == ) '';if null return} { /* no id specified, use current doc */else if $resource =& $modx->resource;}
/* the field value */return $resource->get($field);return
?>
Call this Snippet getField like so in our tabs page:
<script type= >"text/javascript"$(function() { $( ).tabs(); });"#tabs"</script>
<div id= >"tabs" <ul> <li><a href= >[[getField? &id=`92` &field=`pagetitle`]]</a></li>"[[~92]]" <li><a href= >[[getField? &id=`546` &field=`pagetitle`]]</a></li>"[[~546]]" <li><a href= >[[getField? &id=`123` &field=`pagetitle`]]</a></li>"[[~123]]" </ul></div>
However, the getField solution is not as fast or elegant as the Wayfinder solution, since it has to make a query every tab.
Conclusion
Note that all you're doing is pointing the href tags to the actual document IDs, just like a normal link. The trick is you're making your Template forthe Documents be blank (or minimal) so that it only loads the parsed content itself.
This will successfully load your MODx Resources into jQuery tabs.
Managing Resources and Elements via SVN
The Problem
When working in collaboration, teams of developers and designers often collaborate via Subversion (SVN) to make development easier betweenmultiple people. MODx, however, stores its data in the database. This has many benefits generally, but DB-stored code cannot beversion-controlled via SVN.
However, the solution in MODx Revolution is quite simple.
The Solution
For Resources, it's simple. Just use , and point the content to a file in your SVN checkout.Static Resources
For Elements, all you need is a simple "include" . The code:snippet
if (!file_exists($file)) '';return$o = include $file;
$o;return
You can then call it like so in your Static Resources:
[[include? &file=`/path/to/my/svn/checkout/snippet.php`]]
And you're done. You can also use tags within the 'file' parameter, such as this:
[[include? &file=`[[++assets_path]]/js/myscript.js`]]
Conclusion
This allows you to easily manage content via SVN. It can be achieved with and as well; just plop the include snippet wherever youTemplates TVsneed filesystem-based files.
xPDO XML Schema File vs. Table Structure Examples
Before you StartA simple table: session
The xPDO XML:The MySQL CREATE TABLE statement:Things to Note
system_settingsXMLMySQL
user_group_rolesXML
This is a work in progress
The XML files used by xPDO to define database tables are meant to be created in a certain format. One of the easiest ways to get familiar withthis format is to look at existing XML schemas and compare them to the actual database tables which they represent.
Although xPDO is built to be database agnostic, and in the future it will support other databases, for the point of familiar comparison, MySQL isused in these comparisons.
Before you Start
Note that the schema file contains definitions of many tables, and we're using it as a referencecore/model/schema/modx.mysql.schema.xmlfor all the tables mentioned on this page.
In this schema file, each table is defined as an , and all objects are wrapped in the following model tag:object
<model = baseClass= platform= defaultEngine= phpdoc- =package "modx" "xPDOObject" "mysql" "MyISAM" package phpdoc-subpackage="">"modx"
<!-- all table objects are defined in here --></model>
Also note that each object defines a attribute, but this attribute does NOT include any table prefix you may have defined for your site.table
Also note that xPDO defines foreign relationships in both directions; if a column is a foreign key, the XML definition will reflect that in the definitionof that object, but if an id from a table is used as a foreign key in another table, this dependency will noted in tables; this isn't the case withbothevery ORM out there.
A simple table: session
The table is a good table for a simple example because it contains no foreign keys; in other words, it has no composite or aggregatesessionrelations to worry about.
The xPDO XML:
1.
2. 3.
<object class= table= = >"modSession" "session" extends "xPDOObject" <field key= dbtype= precision= phptype= = index= "id" "varchar" "40" "string" null " "false "pk"
="" />default <field key= dbtype= precision= phptype= = attributes="access" " "int "20" "timestamp" null " "true
/>"unsigned" <field key= dbtype= phptype= />"data" "text" "string" </object>
The MySQL CREATE TABLE statement:
CREATE TABLE `modx_session` ( `id` varchar(40) NOT NULL '',default `access` (20) unsigned NULL,int default `data` text, PRIMARY KEY (`id`)) ENGINE=MyISAM DEFAULT CHARSET=latin1
Things to Note
The XML precision matches up exactly to MySQL precision defined after each datatype: it reflects how many characters are visible in acolumn. E.g. int(2) could store any 2 digit number.The in the XML corresponds to the definition in MySQL.null="false" NOT NULLattributes="unsigned" in the XML corresponds directly to the definition in MySQLUNSIGNED
system_settings
This is a fairly simple table too, but it includes 2 foreign keys.
XML
<object class= table= = >"modSystemSetting" "system_settings" extends "xPDOObject" <field key= dbtype= precision= phptype= = ="key" "varchar" "50" "string" null " "false default ""
pk" />index=" <field key= dbtype= phptype= = ="" />"value" "text" "string" null " "false default <field key= dbtype= precision= phptype= = ="xtype" "varchar" "75" "string" null " "false default
/>"textfield" <field key= dbtype= precision= phptype= = ="namespace" "varchar" "40" "string" null " "false default
/>"core" <field key= dbtype= precision= phptype= = ="" />"area" "varchar" "255" "string" null " "false default <field key= dbtype= phptype= = attributes="editedon" "timestamp" "timestamp" null " "false "ON
/>UPDATE CURRENT_TIMESTAMP" <aggregate alias= class= local= foreign="ContextSetting" "modContextSetting" "key" "key"cardinality= owner= />"one" "local" <aggregate alias= class= local= foreign="Namespace" "modNamespace" "namespace" "name"cardinality= owner= />"one" "foreign" </object>
MySQL
CREATE TABLE `modx_system_settings` ( `key` varchar(50) NOT NULL '',default `value` text NOT NULL, `xtype` varchar(75) NOT NULL 'textfield',default `namespace` varchar(40) NOT NULL 'core',default `area` varchar(255) NOT NULL '',default `editedon` timestamp NOT NULL '0000-00-00 00:00:00' on update CURRENT_TIMESTAMP,default PRIMARY KEY (`key`)) ENGINE=MyISAM DEFAULT CHARSET=latin1
user_group_roles
This table includes a single foreign key... actually, its id is as a foreign key by another table. This is a good reminder that in xPDO, theusedforeign key relationships are defined in both directions.
XML
CREATE TABLE `modx_user_group_roles` ( `id` (10) unsigned NOT NULL auto_increment,int `name` varchar(255) NOT NULL, `description` mediumtext, `authority` (10) unsigned NOT NULL '9999',int default PRIMARY KEY (`id`), UNIQUE KEY `name` (`name`), KEY `authority` (`authority`)) ENGINE=MyISAM AUTO_INCREMENT=3 DEFAULT CHARSET=latin1
Adding Custom Fields to Manager Forms
Adding a Custom Field
Adding custom fields to manager forms - such as the Create Chunk, Update Resource, etc - in MODx Revolution is fairly straightforward. You justuse the On*FormRender Plugin events.
We want to add a field called 'Home' that puts an address field into the manager interface, and then stores it into the longtitle value (this is not the
best place to store it, but let's go along with it for tutorial purposes ).
To do so, we'd create a and associate it to the and events. Our code would look like this:Plugin OnDocFormRender OnDocFormSave
<?php/** * Register a form field to forms */
($modx->event->name) {switch 'OnDocFormPrerender':case /* you want to add custom scripts, css, etc, register them here */if ;break 'OnDocFormRender':case $v = ''; (isset($scriptProperties['resource'])) {if /* on the update screen, so set the value */ $v = $scriptProperties['resource']->get('longtitle'); } {else /* on the create screen, so set the */default $profile = $modx->user->getOne('Profile'); $v = $profile->get('address'); } /* now the HTML */do $fields = '<div class= >"x-form-item x-tab-item" <label class= style= >Home</label>"x-form-item-label" "width:150px;" <div class= >"x-form-element" <input type= name= value= class= />"text" "home" "'.$v.'" "x-form-text x-form-field" </div></div>'; $modx->event->output($fields);
;break 'OnDocFormSave':case /* processing logic here. */do $resource =& $scriptProperties['resource']; $resource->set('longtitle',$_POST['home']); $resource->save(); ;break}
;return
Note the CSS classes and styling in the form HTML. Those are unnecessary; but will make the form "match" the styling of the rest of the fields.
MODx Community Information
This section is currently under construction.
Getting a MODx Account
This section is under construction.
Simply signup here: http://modxcms.com/community/register/
Filing Bug Reports
This section is under construction.
Bug reports can be filed in JIRA at http://bugs.modx.com
Becoming a Core Contributor
The ResourcesBut how do I get a sign-on?I've submitted my CLA, now what? I want to commit!My patch was rejected! What?!See Also
So, you've seen MODx Revolution, and are itching to get in on the development. But, to your dismay, you're confused on how to start thatprocess. This article will help you get to your level of commitment in development - be it an active coder or simply a tester who submits patches.
The Resources
MODx has moved to an -driven development environment, comprised of Jira, Fisheye/Crucible, and Confluence. All resources use anAtlassianSSO-driven authentication interface, so no need for multiple logins. For now, you only need 2 - one for the Atlassian apps, and the other for theMODx forums. (And we're working on getting the forums to SSO!)
JIRA - MODx's new bugtracker. It combines detailed tracking with Fisheye SVN integration. All new bugs will be submitted via JIRA.Fisheye/Crucible - This is a detailed SVN reporting and reviewing interface that allows for peer reviews of committed code.Confluence - The new wiki for MODx.
But how do I get a sign-on?
Simple. Just for a modxcms.com SSO (single-sign on) login and you're there! You'll instantly have access to and , and beregister Jira Fisheyeable to submit bugs and review recent commits. MODx team administrators will then grant you access according to team needs, and you'll be ableto quickly start submitting Jira patches and more!
If you want to contribute to , the official documentation wiki, work on bugs, or actually commit some code, the first step is to fill out andConfluencesend in a right after creating a JIRA account. A CLA protects your contributions, but also gives MODxContributor License Agreement (CLA)and it's user base clear permission to use those contributions any way that is compliant with the MODx license (GPL), and it's based on—morelike copied directly from—the same one used by Apache and the Dojo Foundation.
I've submitted my CLA, now what? I want to commit!
We applaud your eagerness. Honestly, no joke, we do. MODx needs eager developers. However, there is a level of relationship that we like todevelop with people before they become core devs. You've got access to Jira now, and probably have noticed some bugs or have some featurerequests. So use it! Submit those bugs and ideas to Jira, and include in Jira patches for them. Please, no political jokes hidden in the comments.Really.
You can submit a patch by using (SVN). For the current time, MODx is on SVN for development, although this will soon be moving to Subversion. Simply and start working on your patch. Then, you can use SVN's "Create Patch" to create the patch and submitGit checkout MODx Revolution
it to JIRA.
After an unspecified, arbitrary amount of time (usually decided by a game of Risk in which one of us usually ends up regretting taking overAustralia after they skipped Africa, and ends the game by throwing the board at the others while yelling Orwellian quotations) the core dev teamwill then approach you with SVN commit privileges. From there you'll be able to submit commits just like the rest of the team, and have thosecommits reviewed by fellow team members in Crucible. Don't worry if your first Crucible review is scathing - we usually do that to the newbies. Atleast we don't do the glue-on-the-keyboard initiation anymore (darn lawyers).
My patch was rejected! What?!
Every so once in a while, a patch submitted doesn't make it into the core. It's not because we don't like you. In all honesty, Jason loves darts, andbefore each patch approval he staples them to the wall and we choose the one he hits while blindfolded. (Okay, so that's not true. He's notblindfolded.)
Sometimes a patch you submit wont make it in. That may be for a myriad of reasons:
The patch wasn't a part of the Core Design PhilosophyWe decided to move the feature request to a component rather than the coreSomeone else provided a more elegant patch
The patch caused too many other issues to ariseThe patch was submitted in , which although we had a hoot reading, was pretty useless in the endlolcode
So don't take offense. We really appreciate contributions to MODx, and we seriously consider everything that this wonderfulany and allcommunity gives to it. MODx has thrived because of this community. However, some things just wont match with the MODx vision and designphilosophy; so be patient with us, and know we really like people who submit patches. A ton. Did we mention we really like patch submitters?
See Also
Development Environments
Recommended Development Tools and Environments for MODx Revolution
In developing MODx Revolution, the MODx Team has found the following environments invaluable:
Netbeans
Netbeans 6.8Netbeans Subversion and JIRA plugins
Eclipse
Eclipse 3.2.+ (recommend latest 3.5.1)Web Standard Tools Project (WST) 2.0.1 ( )http://download.eclipse.org/webtools/updates/Subclipse 1.6.5 ( )http://subclipse.tigris.org/update_1.6.xPHPEclipse 1.2.3 ( )http://update.phpeclipse.net/update/nightlySpket IDE 1.6.18 ( )http://spket.com/update/
Installation
Simply install the latest Eclipse ClassicStart up eclipse / select a workspaceUse the Install Software option under the help menuRight click and copy each of the links above (doing them in order doesn't hurt)Click the "Add" buttonName the "repo" WST, Subclipse, PHPEclipse, or Spket, as it relates to the URLPaste the URLClick OKRepeat for each of the links above as necessaryIndividual notes:
WST - select the latest Web Tools Platform (takes quite a while)Subclipse - simply install the Subclipse optionPHPEclipse - install everything offeredSpket - Install everything offered
Other IDEs
For Mac:
TextMate - IDECoda - IDEVersions - SVN clientsvnX - SVN client
For PC:
UltraEdit - IDEE - IDETortoiseSVN - SVN clientKate - IDE for Linux / KDE
Development Server Environments
We also MacPorts, XAMPP and MAMP, and the following tools/libraries in the development of MODx Revolution:
PHPUnit - this drives the PHP 5.1+ unit testing framework for xPDO, and we'll be adding a test harness to MODx soonSimpleTest - this drives the PHP 4/5.0.x unit testing framework for xPDO, and we'll be adding a test harness to MODx soonPHPDocumentor - all of the classes in MODx Revolution are documented in PHPDoc format, and we'll be developing tutorials and otherextended documentation for inclusion in the PHPDocs in DocBook XML formatPhing - will be used to allow automation of nightly builds, various distribution builds, unit testing, and many other development tasks
MODx PHP Coding Standards
GeneralParenthesisClassesVariablesFunction Arguments and Class VariablesArraysConstantsFile StructurePrefixing
General
Beginning brackets do NOT linebreak. They start one space after the end parenthesis, as according to traditional Unix policy.Do not do any real logic in object constructors. Create class methods to do so.null, true and false should always be lowercase.Avoid embedded assignments (ex: $d = ($a = $b + $c) is bad).Never use extract().Avoid using global variables if at all possible.Document EVERYTHING.
Parenthesis
Do not put parenthesis next to keywords. Put a space between.Do put parenthesis next to function names.Do not use parenthesis in return statements when it's not necessary. Example:
if ($test) {}
($test == $other) {while}array_push($one,$two);
$test;return
Do use parenthesis when using include, require, include_once, and require_once.not
Classes
All ''core'' classnames, unless stated otherwise for special conditions, will be prefixed with the "mod" prefix: ie, modChunk, modTemplate,etc.All method names will be camelCase and will start with a lowercase letter.All private methods and variables must be prefixed with the underscore _ character.
class modFactor { $publicVar;public $_privateVar;private function _privateFunc() { }private function publicFunc() { }public}
Variables
Note these are not function arguments.
Use all lowercase letters.Separate words with the underscore.
Function Arguments and Class Variables
The first letter is lowercase, rest are camelCase. Example:
class modFactor { function testFunc($testVar, array &$anotherTest = array()) {public $ ->_privateVar = $testVar;this $local_variable =& $anotherTest; }}
Arrays
Array index names use the underscore _, not the dash as their separator. This prevents errors with magic_quotes.Array index names are always lowercase. Spaces are represented by an underscore.Array index names are always encapsulated with single quotes.Example:
$_lang['chunk_create_text'] = 'Test';
Constants
Constants must be in all UPPERCASE letters.Use only if absolutely necessary.
File Structure
Always name PHP class files in name.class.php format.
Prefixing
Lexicon strings for Components need to be prefixed:
$_lang['mycomponent.welcome_message'] = 'Welcome!';
Always prefix class names; eg: 'finBank', 'finTransaction', etc.Always prefix names; eg: 'finStatement', 'finDeposit'Chunk
Using GitHub
This section is under construction.
See .Git Installation