ModX Revolution Docs 20101007
ModX Revolution Docs 20101007
1. Home . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
1.1 Getting Started . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
1.1.1 Server Requirements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
1.1.1.1 MySQL 5.0.51 Issues . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
1.1.2 Installation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
1.1.2.1 Basic Installation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
1.1.2.1.1 MODx Revolution on Debian . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
1.1.2.1.2 Lighttpd Guide . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
1.1.2.1.3 Problems with WAMPServer 2.0i . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
1.1.2.2 Advanced Installation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
1.1.2.3 Git Installation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
1.1.2.4 Troubleshooting Installation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
1.1.2.5 Successful Installation, Now What Do I Do? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
1.1.2.6 Using MODx Revolution from SVN . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
1.1.3 An Overview of MODx . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
1.1.3.1 Glossary of Revolution Terms . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
1.1.3.1.1 Explanation of Directory Structure . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
1.1.3.2 Roadmap . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
1.2 Making Sites with MODx . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
1.2.1 Structuring Your Site . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
1.2.1.1 Resources . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
1.2.1.1.1 Content Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38
1.2.1.1.2 Named Anchor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39
1.2.1.1.3 Static Resource . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39
1.2.1.1.4 Symlink . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40
1.2.1.1.5 Weblink . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41
1.2.1.2 Templates . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
1.2.1.3 Chunks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43
1.2.1.4 Using Snippets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45
1.2.2 Tag Syntax . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46
1.2.3 Customizing Content . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47
1.2.3.1 Template Variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47
1.2.3.1.1 Creating a Template Variable . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48
1.2.3.1.2 Bindings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50
1.2.3.1.3 Template Variable Output Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56
1.2.3.1.4 Adding a Custom TV Input Type . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58
1.2.3.1.5 Adding a Custom TV Output Type . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59
1.2.3.2 Properties and Property Sets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62
1.2.3.3 Input and Output Filters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65
1.3 Administering Your Site . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67
1.3.1 Settings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68
1.3.1.1 System Settings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69
1.3.1.1.1 allow_duplicate_alias . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69
1.3.1.1.2 allow_multiple_emails . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69
1.3.1.1.3 allow_tags_in_post . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69
1.3.1.1.4 auto_check_pkg_updates . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70
1.3.1.1.5 auto_check_pkg_updates_cache_expire . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70
1.3.1.1.6 auto_menuindex . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70
1.3.1.1.7 blocked_minutes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70
1.3.1.1.8 cache_action_map . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70
1.3.1.1.9 cache_context_settings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70
1.3.1.1.10 cache_db . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71
1.3.1.1.11 cache_db_expires . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71
1.3.1.1.12 cache_default . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71
1.3.1.1.13 cache_disabled . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71
1.3.1.1.14 cache_handler . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71
1.3.1.1.15 cache_json . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72
1.3.1.1.16 cache_json_expires . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72
1.3.1.1.17 cache_lexicon_topics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72
1.3.1.1.18 cache_noncore_lexicon_topics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72
1.3.1.1.19 cache_resource . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72
1.3.1.1.20 cache_resource_expires . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72
1.3.1.1.21 cache_scripts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73
1.3.1.1.22 compress_css . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73
1.3.1.1.23 compress_js . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73
1.3.1.1.24 concat_js . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73
1.3.1.1.25 container_suffix . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73
1.3.1.1.26 cultureKey . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74
1.3.1.1.27 custom_resource_classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74
1.3.1.1.28 default_template . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74
1.3.1.1.29 editor_css_path . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74
1.3.1.1.30 editor_css_selectors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74
1.3.1.1.31 emailsender . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75
1.3.1.1.32 emailsubject . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75
1.3.1.1.33 error_page . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75
1.3.1.1.34 failed_login_attempts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75
1.3.1.1.35 fe_editor_lang . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75
1.3.1.1.36 feed_modx_news . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75
1.3.1.1.37 feed_modx_news_enabled . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76
1.3.1.1.38 feed_modx_security . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76
1.3.1.1.39 feed_modx_security_enabled . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76
1.3.1.1.40 filemanager_path . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76
1.3.1.1.41 friendly_alias_urls . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76
1.3.1.1.42 friendly_url_prefix . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77
1.3.1.1.43 friendly_url_suffix . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77
1.3.1.1.44 friendly_urls . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77
1.3.1.1.45 mail_charset . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77
1.3.1.1.46 mail_encoding . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77
1.3.1.1.47 mail_smtp_auth . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78
1.3.1.1.48 mail_smtp_helo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78
1.3.1.1.49 mail_smtp_hosts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78
1.3.1.1.50 mail_smtp_keepalive . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78
1.3.1.1.51 mail_smtp_pass . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78
1.3.1.1.52 mail_smtp_port . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78
1.3.1.1.53 mail_smtp_prefix . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78
1.3.1.1.54 mail_smtp_single_to . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79
1.3.1.1.55 mail_smtp_timeout . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79
1.3.1.1.56 mail_smtp_user . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79
1.3.1.1.57 mail_use_smtp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79
1.3.1.1.58 manager_date_format . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79
1.3.1.1.59 manager_direction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79
1.3.1.1.60 manager_lang_attribute . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 80
1.3.1.1.61 manager_language . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 80
1.3.1.1.62 manager_theme . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 80
1.3.1.1.63 manager_time_format . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 80
1.3.1.1.64 modx_charset . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 80
1.3.1.1.65 new_file_permissions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 80
1.3.1.1.66 new_folder_permissions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 80
1.3.1.1.67 password_generated_length . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81
1.3.1.1.68 password_min_length . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81
1.3.1.1.69 phpthumb_cache_maxage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81
1.3.1.1.70 phpthumb_cache_maxfiles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81
1.3.1.1.71 phpthumb_cache_maxsize . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81
1.3.1.1.72 phpthumb_cache_source_enabled . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81
1.3.1.1.73 phpthumb_far . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82
1.3.1.1.74 phpthumb_zoomcrop . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82
1.3.1.1.75 proxy_auth_type . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82
1.3.1.1.76 proxy_host . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82
1.3.1.1.77 proxy_password . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82
1.3.1.1.78 proxy_port . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82
1.3.1.1.79 proxy_username . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82
1.3.1.1.80 publish_default . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83
1.3.1.1.81 rb_base_dir . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83
1.3.1.1.82 rb_base_url . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83
1.3.1.1.83 request_controller . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83
1.3.1.1.84 request_param_alias . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83
1.3.1.1.85 request_param_id . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84
1.3.1.1.86 search_default . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84
1.3.1.1.87 server_offset_time . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84
1.3.1.1.88 server_protocol . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84
1.3.1.1.89 session_cookie_domain . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84
1.3.1.1.90 session_cookie_lifetime . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84
1.3.1.1.91 session_cookie_path . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85
1.3.1.1.92 session_cookie_secure . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85
1.3.1.1.93 session_handler_class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85
1.3.1.1.94 session_name . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85
1.3.1.1.95 settings_version . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85
1.3.1.1.96 signupemail_message . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 86
1.3.1.1.97 site_name . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 86
1.3.1.1.98 site_start . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 86
1.3.1.1.99 site_status . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87
1.3.1.1.100 site_unavailable_message . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87
1.3.1.1.101 site_unavailable_page . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87
1.3.1.1.102 strip_image_paths . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87
1.3.1.1.103 tree_root_id . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88
1.3.1.1.104 udperms_allowroot . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88
1.3.1.1.105 unauthorized_page . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88
1.3.1.1.106 upload_maxsize . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88
1.3.1.1.107 use_alias_path . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88
1.3.1.1.108 use_browser . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89
1.3.1.1.109 use_editor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89
1.3.1.1.110 use_multibyte . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89
1.3.1.1.111 welcome_screen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89
1.3.1.1.112 which_editor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89
1.3.1.1.113 which_element_editor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90
1.3.2 Using Friendly URLs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90
1.3.3 Contexts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91
1.3.3.1 Creating a Subdomain from a Folder using Virtual Hosts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 92
1.3.3.2 Using One Gateway Plugin to Manage Multiple Domains . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 93
1.3.4 Customizing the Manager . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 94
1.3.4.1 Form Customization Pages . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 95
1.3.4.1.1 FC-Resource . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 95
1.3.4.1.2 FC-Template . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 96
1.3.4.1.3 FC-Chunk . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97
1.3.4.1.4 FC-Snippet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97
1.3.4.1.5 FC-Plugin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97
1.3.4.2 Form Customization Rules . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97
1.3.4.2.1 Field Default . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 99
1.3.4.2.2 Field Label . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 100
1.3.4.2.3 Field Visible . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101
1.3.4.2.4 Move TV to Tab . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102
1.3.4.2.5 New Tab . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 103
1.3.4.2.6 Tab Title . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 105
1.3.4.2.7 Tab Visible . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 106
1.3.4.2.8 TV Default . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 106
1.3.4.2.9 TV Title . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 107
1.3.4.2.10 TV Visible . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 108
1.3.5 Security . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 109
1.3.5.1 Users . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 110
1.3.5.2 User Groups . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 112
1.3.5.3 Resource Groups . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 112
1.3.5.4 Roles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 113
1.3.5.5 Policies . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 115
1.3.5.5.1 Permissions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 117
1.3.5.5.2 ACLs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 120
1.3.5.6 Security Tutorials . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 121
1.3.5.6.1 Giving a User Manager Access . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 121
1.3.5.6.2 Making Member-Only Pages . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 122
1.3.6 Installing a Package . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 124
1.3.6.1 Troubleshooting Package Management . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 126
1.3.7 Upgrading MODx . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 126
1.3.7.1 Upgrading from MODx Evolution . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 127
1.3.7.1.1 Functional Changes from Evolution . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 128
1.3.7.2 Upgrading to Revolution 2.0.0-rc-2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 129
1.3.8 Moving Your Site to a New Server . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 130
1.4 Developing in MODx . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 132
1.4.1 Code Standards . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 132
1.4.2 Overview of MODx Development . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 143
1.4.2.1 Developer Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 143
1.4.2.1.1 Getting Started Developing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 145
1.4.2.2 Extras Directories . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 146
1.4.2.3 Setting up a Development Environment . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 146
1.4.3 Basic Development . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 148
1.4.3.1 Snippets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 148
1.4.3.1.1 Templating Your Snippets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 151
1.4.3.1.2 Adding CSS and JS to Your Pages Through Snippets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 154
1.4.3.2 Plugins . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 155
1.4.3.2.1 System Events . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 157
1.4.3.3 xPDO . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 193
1.4.4 Advanced Development . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 193
1.4.4.1 Namespaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 193
1.4.4.2 Caching . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 194
1.4.4.3 Custom Manager Pages . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 195
1.4.4.3.1 Actions and Menus . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 200
1.4.4.3.2 Custom Manager Pages Tutorial . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 201
1.4.4.3.3 MODExt . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 203
1.4.4.4 Internationalization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 208
1.4.4.4.1 Creating Lexicons for Your Components . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 211
1.4.4.5 MODx Services . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 211
1.4.4.5.1 modMail . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 211
1.4.4.6 Package Management . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 212
1.4.4.6.1 Transport Packages . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 214
1.4.4.6.2 Providers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 216
1.4.4.6.3 Creating a 3rd Party Component Build Script . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 218
1.4.4.7 Extending modUser . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 231
1.4.5 Other Development Resources . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 235
1.4.5.1 Loading MODx Externally . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 235
1.4.5.2 API Reference . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 236
1.4.5.3 Class Reference . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 236
1.4.5.3.1 modX . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 236
1.4.5.3.2 modChunk . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 261
1.4.5.3.3 modUser . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 262
1.5 Case Studies and Tutorials . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 268
1.5.1 Using Custom Database Tables in your 3rd Party Components . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 268
1.5.2 Creating a Blog in MODx Revolution . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 273
1.5.3 PHP Coding in MODx Revolution, Pt. I . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 283
1.5.4 PHP Coding in MODx Revolution, Pt. II . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 285
1.5.5 PHP Coding in MODx Revolution, Pt. III . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 286
1.5.6 Loading Pages in the Front-End via AJAX and jQuery Tabs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 288
1.5.7 Managing Resources and Elements via SVN . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 290
1.5.8 xPDO XML Schema File vs. Table Structure Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 291
1.5.9 Adding Custom Fields to Manager Forms . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 293
1.6 MODx Community Information . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 294
1.6.1 Getting a MODx Account . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 294
1.6.2 Filing Bug Reports . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 294
1.6.3 Becoming a Core Contributor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 295
1.6.3.1 Development Environments . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 296
1.6.3.2 MODx PHP Coding Standards . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 297
1.6.4 Using GitHub . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 298
Home
MODx Revolution
Been 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.
Want to contribute to Revolution development or testing? See our Becoming a Core Contributor page.
Getting Started
Developing in MODx
You can download the documentation in PDF format, as of July 21st, 2010.
Getting Started
Welcome to MODx Revolution. This section provides installation tutorials, beginning concepts around MODx, and general information about
MODx to get you started.
Server Requirements
PHP Compatibility
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 here
and there.
MySQL 5.0.51 Issues
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 support
installations with MySQL 5.0.51 installed. Please upgrade your MySQL installation.
http://bugs.mysql.com/bug.php?id=32202
http://bugs.php.net/bug.php?id=47655
http://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:
The quickest way to get your Revolution site up and running is to grab a copy directly from the MODx Downloads page. There you will find
downloads 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 may
have changed since then, including bugfixes and the addition of new features. Note the release date for each package. Git will always have the
latest up-to-date snapshot of Revolution.
You have probably noticed that there are a few different types of packages to choose from. Some are labeled as "Advanced," others are just a
plain 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 Installation
guide 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
Installation document for this distribution.
From Git
MODx Revolution 2.0.0 is managed on GitHub. Please read the Git Installation document to learn how to use MODx Revolution from Git.
Installing MODx
MODx comes with multiple distributions for download. Installation steps will differ in each distribution, so please select the distribution's installation
guide below:
Basic Installation
Beginning Setup
Install Options
Database Options
Collations and Charsets
Creating an Administrator User
Pre-Installation Checks
Post-Installation Summary
Additional Info
WAMPServer 2.0i
MAMP on MacOSX
Debian
Vista and XAMPP
Installing Packages
See Also
Beginning Setup
After you've downloaded MODx Revolution, start the install process by loading your web browser and running the setup script by navigating to the
setup/ folder.
You might want to check the Server Requirements page first. If you're still having issues installing, please read the
Troubleshooting Installation page.
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.
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 your
MySQL 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 to
make 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 do
have errors, check to make sure your database username and password are correct. Also, if your user does not have access to create a
database, you might need to do that manually.
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, make sure the collation matches the charset.
Click the 'Create or test selection of your database.' after you've finished.
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 administrator
username.
MODx recommends not using 'admin', as this is a common administrator username and is often the first username hackers
check.
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 the
directions that it suggests to make sure your environment meets the Server Requirements and has the correct directories writable.
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:
1. Make sure the directories "/[root]", "/core/packages","/core/cache", and "/core/export" are writable. (root will be the
actual directory you are installing to.)
2. Make sure your php.ini setting sets memory_limit to 128M, and max_execution_time to 120
3. Create a blank file "/core/config/config.inc.php" and make it writable. DO NOT COPY config.inc.tpl! Just make it a
blank file!
4. Post a message in the Revolution forum regarding your issue. State your server setup and installation info, and we'll try
and 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 have
occurred.
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 setup
on 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
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 the
drivers compiled with MAMP are faulty with regards to PDO and will cause Apache kernel errors. Select the 'xCache' caching drivers to remedy
this.
Debian
Debian uses outdated MySQL drivers for its PHP build that will need to be updated; please see the MODx Revolution on Debian article for more
information.
There have been reported problems with installing Revolution on 64-bit Vista with XAMPP. We cannot guarantee a working solution on that OS
and setup at this time.
Some users have reported that applying a fix found here: http://www.apachefriends.org/f/viewtopic.php?f=16&t=32617 will fix
Apache crashing errors with PDO support in XAMPP.
Installing Packages
For information on installing 3rd-party packages, see the How to Install Packages article.
See Also
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
do the following:
1. Update the server MySQL drivers. You can do this manually on debian, or use apt-get.
2. Update the client MySQL drivers. The easiest way to do this is by updating PHP, like so:
apt-get update
apt-get upgrade php5
vi /etc/php5/apache2/php.ini
date.timezone = Europe/Amsterdam
/etc/init.d/apache2 reload
Lighttpd Guide
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.
lighttpd does not use the same system, or even same idea as Apache does for URL rewriting. All URL rewriting is done in the
lighttpd.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"
After this is done, you will have working friendly URLs again in lighttpd.
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/
Usually it is best to make sure in any server configuration that your MySQL server and client build versions are the same. WAMPServer allows
you 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
does not support 5.0.51a, and therefore will not install with this configuration.
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 still
not optimal, this will allow Revolution to run smoothly without MySQL hiccups.
Advanced Installation
Installation Pre-Steps
Renaming or Moving the Core
Changing the Configuration Key
Advanced Options
Database Options
Collations and Charsets
Creating an Administrator User
Context Configuration
Pre-Installation Checks
Post-Installation Summary
See 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/ directory
You have SSH access or can easily move/make writable directories on your server.
You might want to check the Server Requirements page first. If after reading this, you're still having issues installing, please
read the Troubleshooting Installation page.
Installation Pre-Steps
After you've downloaded MODx Revolution's advanced distribution, upload and extract it to your server. You should be left with two directories -
core/ 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 the
config key, browse to setup/ in your browser and skip to the Advanced Options section of this document.
Should you choose to rename or move the core, MODx recommends doing so before installing. Simply rename or move the core, and setup/ - at
the 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 proceed
normally 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 an
absolute 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 so
before proceeding.
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 if
you 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 own
unique 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:
Advanced Options
You will now be presented with some options for install, similar to the Basic Installation screen, but with two extra options at the bottom. 'New
Installation' will be your only radio option available to check, which is what you want. Below that, you can choose to adjust the permissions for
creating new files or folders in your MODx installation. The defaults should work fine, but if on a more restrictive server, you can change the
folder/file perms to 0775/0664, respectively.
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 your
MySQL 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 to
make 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 do
have errors, check to make sure your database username and password are correct. Also, if your user does not have access to create a
database, you might need to do that manually.
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, make sure the collation matches the charset.
Click the 'Create or test selection of your database.' after you've finished.
MODx recommends not using 'admin', as this is a common administrator username and is often the first username hackers
check.
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 main
context), 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 URLs
in the textfields provided. Note: If you do change the directories, the directories above any of those paths must be writable to allow MODx to write
the manager/ and/or connectors/ directories to them.
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 the
directions that it suggests to make sure your environment meets the Server Requirements and has the correct directories writable.
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:
1. Make sure the directories "/[root]", "/core/config", "/core/packages","/core/cache", and "/core/export" are writable. (root
will be the actual directory you are installing to.)
2. Make sure your php.ini setting sets memory_limit to 128M, and max_execution_time to 120
3. Ensure that MODx can create the manager and connectors directories; this is done by making the parents of those
directories writable (since you can change where they are installed)
4. Post a message in the Revolution forum regarding your issue. State your server setup and installation info, and we'll try
and 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 have
occurred.
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 setup
on 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 Process
Git Location
Stable Branches
Development Branches
Run the Build
Run Setup
Upgrading Your Local Git Repository After Commits
Sending Pull Requests
Switching Branches
Additional Information
Using 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 not
install directly from Git. Because of the nature of the new packaging and installation system, you must first create the core installation package
using a PHP build script before running the setup.
Git Location
Git clone the revolution repository on GitHub at: http://github.com/modxcms/revolution/ using this syntax:
Or, if you'd like to contribute back, fork it in your GitHub repository and clone that repository with:
Forking it with your GitHub account will allow you to contribute back to MODx by sending pull requests by clicking the "Pull Request" button on
your GitHub page. (You'll need to submit a CLA before we can accept your code, though.)
If you're not familiar with Git, please read the excellent tutorial from GitHub and view the GitHub help pages.
From there, make sure you are working on the 2.0 branch, if you're wanting the latest bugfix release. There are three current branches in the
modxcms/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 here
from time to time.
To create a local tracking branch from one in the origin remote; after cloning, just type:
There will be other branches in the future, centered around new features. New branches will be feature-specific and merged into
the 2.1 branch when they are complete and stable.
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 to
point at a valid database with proper credentials. NOTE that this database does not have to contain anything; the build script just needs to be able
to 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 is
not 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 take
an 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 the
Leopard 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 is
accessible 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 directory
core/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. http://localhost/modxrevo/setup/ if installed in a subdirectory of the web
root named modxrevo/).
Make sure you check both the "Core package has been manually unpacked" and "Files are already in-place" options when
installing 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 for
installs from the core package with files not already in-place, where the installer will place the files in the specified locations (assuming the
locations 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 get
a blank page when you click "install", try increasing the memory_limit configuration to 32M or more (16M may work, but why not
give php a little space, eh?).
And Git will update your install. (Substitute '2.0' for '2.1' or 'master' if you're running from another branch.)
[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 run
setup/ again.
If this message does not show up, you're done after you fetch and rebase.
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 MODx
and one of the Integration Managers will review your patch. Make sure you are sending pull requests to the development branches, ie 2.0 for
patches or 2.1 for new features. Pull requests to master will be ignored.
Switching Branches
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, since
different branches might have different databases.
Switching backwards is not always recommended; ie, switching from 2.1 to 2.0, since database changes don't always
necessarily 'backport'. While no major issues should occur, be careful when doing this.
Additional Information
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 Problems
PDO Error Messages
Common 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 page
Warning: PDO::__construct() [pdo.--construct]: [2002] Argument invalid (trying to connect via unix://) OR "Checking
database:Could not connect to the mysql server."
The login page keeps redirecting me back to the login screen with no error
Things sometimes don't load, the page flakes out, etc (eAccelerator)
Resource / Elements / File tree not appearing
I 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 here for your distribution.
You are using at least PHP 5.1.1+, but not 5.1.6 or 5.2.0
You are using MySQL later than 4.1.20, but not any iteration of MySQL 5.0.51 (including 5.0.51a).
If you are getting PDO-related error messages during install, before proceeding to specific error messages as below, please confirm that your
PDO 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 = new PDO($dsn, $user, $password);
} catch (PDOException $e) {
echo 'Connection failed: ' . $e->getMessage();
}
?>
Here are some common problems that might occur during installation and their solutions:
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.
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.
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 "Checking
database: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.sock
mysqli.default_socket=/path/to/my/mysql.sock
pdo_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_name
session_cookie_path
session_cookie_domain
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 your
php.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 0
php_flag eaccelerator.optimizer 0
php_flag eaccelerator.debug 0
Additional, page "flake outs" may stem from items stored within your own browser's cache, which may result with the resource / elements / file
tree not appearing due to old versions of javascript and other files being utilized on the client side. This can be verified by accessing the manager
with a browser not previously utilized in doing so.
The simple fix: clear your browser's cache, and log back into the manager.
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
If you're still having problems, post your error and your server environment information in our forums here, and we'll try and address your issue as
soon as possible.
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 first
thing to do is create a page to make these errors go away.
This being MODx, there are several ways to create a new resource (document). The main Site menu has a "New Document" menu item, the Tree
View 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 menu
bar 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 on
the 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 the
document a title, Home, a Menu Title, First Document, and some content. Make sure to check the Published check box. Click on the floating Save
button 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 view
your 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 the
Template 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 working
on.
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. MODx
templates are basically just HTML pages, with the content parts replaced with MODx tags. So to begin with, let's just create a really simple
template.
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
<title>My First Revolutionary Page</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<style type="text/css" media="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, and
the 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 home
page without that long list of errors!
Installation Process
SVN Locations
Run the Build
Run Setup
Upgrading After Commits
Using 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 not
install directly from SVN. Because of the nature of the new packaging and installation system, you must first create the core installation package
using a PHP build script before running the setup.
SVN Locations
Checkout or export the latest Revolution code from SVN at the URL:
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 valid
database with proper credentials. NOTE that this database does not have to contain anything; the build script just needs to be able to 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 is
not 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 take
an 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 the
Leopard 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 is
accessible 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 directory
core/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. http://localhost/modxrevo/setup/ if installed in a subdirectory of the web
root named modxrevo/).
Make sure you check both the "Core package has been manually unpacked" and "Files are already in-place" options when
installing 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 for
installs from the core package with files not already in-place, where the installer will place the files in the specified locations (assuming the
locations 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 get
a blank page when you click "install", try increasing the memory_limit configuration to 32M or more (16M may work, but why not
give 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 to
go, so expect the unexpected as we work through changes, fixes and refactorings.
See Development Environments for recommended tools and setups for contributing directly to MODx core code.
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 rebuild
the core transport and run setup/ again.
If neither of these messages show up, you simply need to svn update, and you're done.
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:
MODx offers you a system that lets you publish your offline content onto the web in any form, shape or presence you want. It also
offers 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 URL
compatible, so getting mysite.com/my/own/custom/url.html is incredibly simple, and as easy as structuring your site that way.
Ever wanted complete freedom with your HTML and CSS? Tired of hacking existing systems to get your design to
work the way you comp'ed it? MODx does not generate one single line of HTML - it leaves the front-end design up
to you.
You can use MODx as your Content Management System (CMS) of choice, as MODx offers completely flexible
templating and no-holds-barred content delivery. Put your CSS and images where you want them. And once you're
done designing, either hand off the development duties to your developer, or point-and-click install Extras straight
from within the manager. Simple.
You've looked at different CMSes, but have found developing in them to be either a mishmash of too many
unconnected code pieces, or simply not powerful or elegant enough. You've looked at PHP frameworks, and have
found they have the power, but don't do Content Management nor have a good enough UI for your clients. You
want 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 a
PDO-powered Object Relational Model (ORM) called xPDO. Add in a rich, Sencha-powered UI for your clients,
that's fully customizable. Custom properties and sets. Internationalization support. Package distribution built-in so
you can pack up your code, and distribute it across any Revolution install. Add custom manager pages to run entire
applications 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 be
a '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 Snippets, or any other Element type (Snippet,
Chunk, 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 a
part of the loading process - such as stripping out bad words in content, adding dictionary links to words, managing redirects for old pages, etc.
Add-on
Asset
Back-end
Category
Chunk Tags
Component
Content Element
Content Type
Context
Context Setting
Core Workspace
Document
Document Identifier
Extension
File Resolver
Language Tags
Lexicon
Lexicon Topic (formerly Foci)
Link Tags
Manager
Namespace
Placeholder Tags
Resource Field
Property
Property Set
Resource
Resource Identifier
Resource Tags
Resolver (for xPDOVehicles)
Setting Tags
Snippet Tags
Static Resource
Symlink
System Setting
Template Variables
Transport Package
Transport Provider (formerly Provisioner)
Transport Vehicles
User Setting
Weblink
Validator (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 the
MODx instance.
Asset
Any type of file resource that is usually located in the /assets directory, as defined by the constant MODX_ASSETS_PATH; can include
Third-party Components, libraries, image files, css files, JavaScript files, class files, etc.
Back-end
An optional classifying name that can be attached to any Element or PropertySet (and other objects in later versions of Revolution) that separates
it from other similar objects.
Chunk Tags
Component
Also called "Third-party Component", or 3PC, a Component usually provides extra functionality to MODx, usually in the form of an Add-on, Core
Extension, or Template.
Content Element
Also called just "Element", a single Template, Template Variable, Chunk, Snippet, Plugin, Category, or Property Set visible in the Manager
Elements 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 as
subdomains, 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 setup
application is recorded into the MODx database as the Default MODx Workspace. In future MODx Revolution releases, there will be an ability to
manage multiple Workspaces from a single database, directly from the manager application. This will make it easy to isolate upgrades to the
MODx Core; by quickly adding a new Core Workspace and selecting a menu option, you'll be able to apply an entire new MODx Core installation
to production sites after testing on a staging site, or quickly revert to a previous Core Workspace you know works. This will be especially important
for multi-site configurations running on shared MODx Core installations.
Document
Document 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
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 the
manager application and can be used by Add-On and Core Extension developers to provide localization facilities for their own components. This
replaces the legacy MODx language files and allows customization of the entries directly from the manager application.
A set of Lexicon Entries focused on a particular subject. Revolution only loads Lexicon Entries as it needs them, by their Topic, to reduce load
times.
Link Tags
Tags in the form [[~ResourceId]] that reference the URL of a particular Resource.
Manager
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') in
a 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 Document
Create/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 that
they 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 a
Document.
Resource Identifier
Also called a Document ID, Resource ID, or Document Identifier; the number in parenthesis in the MODx Resource Tree in the Manager that
uniquely identifies the Resource specified.
Resource Tags
Tags in the form [[*ResourceField/TV]], which can be used to refer to Resource Fields, or Template Variables.
Post-processor: a script or predefined action that is evaluated after a Vehicle is installed or uninstalled. Resolvers always occur after the vehicle's
object 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
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.
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 unique
settings to that user.
Weblink
A type of Resource that references a specific URL or MODx Resource, redirecting the visitor to that URL or Resource.
Pre-processor: a script or predefined action that executes prior to the vehicle being installed or uninstalled. If the validator returns true, the
install/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 a
certain 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, along
with various attributes that control how the object is installed/uninstalled in a Core Workspace (see xPDOVehicle Validators and xPDOVehicle
Resolvers).
The root directory of MODx is split into several subdirectories, each with its own set of responsibilities and tasks. Some of these directories can be
renamed 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 Files
assets/
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 up
the 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 base
connector file (connectors/index.php) which instantiates the main MODx object, handle any custom Context switching, and sanitize the GET or
POST 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, and
then 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 the
manager 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 generated
on-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 been
overridden from their default System Settings.
core/cache/rss/
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 a
context.cache.php file. This file is like the config.cache.php file, except that it only caches settings that have been overridden from their default
System 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 at
cache/web/resources/12.cache.php. This new caching mechanism means that loading times will decrease, and the limit on the number of
cacheable resources will disappear.
core/components/
When you install a package using the Package Manager, a core/components/<component_name>/ directory will be created to hold any files
necessary 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 the
proper 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/
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 are
stored 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 required language strings are loaded, saving memory and loading time.
Second, all lexicons are stored in the MODx database, and later cached on-demand. This makes it possible to manage lexicons directly from the
Manager, inside the Lexicon Management area.
$modx->lexicon->load( 'lang:namespace:topic' );
# lang - the 2-digit IANA code. This is optional, and defaults to 'en'.
1. namespace - Each lexicon has its own Namespace. The built-in namespace for MODx is "core". Package creators will also be able to
create a custom namespace, and Manager users can also create their own namespaces as well.
2. 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 there
should 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 is
the 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 the
processors 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 its
database management. xPDO uses the idea of 'packages' for different connections to different models. So, if I wanted to create my custom
tables, 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 the
MODx core. This is shown in the Creating a 3rd Party Component tutorial.
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 of
classes. These are the classes that are either xPDOObjects - which are PHP classes that represent tables in the DB (ie, modsnippet.class.php is
a 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 table
they 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 accessed
through 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 or
parsed when MODx is running. For the most part, you can ignore this directory, as it is mainly used for development work. The tutorials on
creating 3rd party components teach more about schemas.
core/model/smarty/
This contains the Smarty libraries. It's simply an extraction of the Smarty files you can get from http://smarty.php.net. Nothing in this folder is
customized for MODx - that happens elsewhere.
Smarty is an intelligent, object-oriented templating engine that uses dynamic, modifiable placeholders. Most pages seen in the Manager and
during 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 at
manager/controllers/resource/staticresource/update.php. After setting the characteristics of the resource in the $resource array, this code renders
the page:
$modx->smarty->assign('resource',$resource);
return $modx->smarty->fetch('resource/staticresource/update.tpl');
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 Package Management section of Revolution, such as TinyMCE, Ditto, etc.
The 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 outputting
data 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 System Settings in MODx. Their database equivalents are found in the
_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 use_captcha to '0' to disable
CAPTCHA. 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. In
Revolution, 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 maintenance
tasks.
manager/assets/
This directory contains the ExtJS libraries, as well as the custom ModExt implementation. ModExt extends the original ExtJS library, to make
development 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. Whenever
you load a page in the Manager, you are in effect telling MODx to load a particular Controller, which simply loads a Smarty template and outputs
any 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 are
looking 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 back
afterwards!
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
Fresh Installation or an Upgrade.
_build/
This directory is only present in version of MODx Revolution downloaded from the subversion server (as well as the "SDK" distribution). It
contains 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. After
completion, you should notice a "core" directory inside your core/packages/ directory, which will contain all of the necessary Vehicles for
installing 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 other
media in here.
assets/components/
When you install a package using the Package Manager, an assets/components/<component_name>/ directory will be created to hold any
necessary component files, such as JavaScript or images.
Roadmap
Revolution 2.0
After PL release, the core team will concentrate on creating distributions for different 2.0.0-pl implementations. Migration of the Revolution
codebase to Git will also occur at this time.
Revolution 2.0.1
Revolution 2.1
Revolution 2.2
Revolution 2.3
Revolution 3.0
Add native Versioning
Add Content Element support
Add Workflow support
Redo manager into an actual 'mgr' context where pages are modx resources
See Also
This section contains information about MODx objects that are used in the structure of your website.
Resources
What is a Resource?
Managing Resources
Resource Fields
General Resource Fields
Settings Resource Fields
Using Resource Fields
Accessing Resource Fields in a Snippet
Linking to a Resource
URL Parameters for Resource Tags
URL 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, or
many 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, Weblinks, Symlinks, and 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 resulting
URL. 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 make
to 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 to
edit. 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 be
edited via the tabs on the top of the page.
Resource Fields
Resources come packaged with a list of predetermined fields by default. They are:
Name Description
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.
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.
Name Description
isfolder Labeled as 'Container', this specifies whether or not the Resource renders with a / in Friendly URLs instead of its suffix.
Resource fields can be accessed from anywhere by using the Template Variable syntax, ie:
Grabbing the Resource Fields in a Snippet is quite easy; MODx provides you with the Resource object in any Snippet, via the $modx->resource
reference. For example, this example Snippet will return the current page's pagetitle reversed:
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.
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:
store/items.html?tag=Snacks&sort=Taste
[[~123? &scheme=`https`]]
Would render the URL using 'https' instead of the normal http.
Name Description
See Also
Content Types
Content types are specific filesystem types for your resources. They are associated with file extensions and tell the MODx Parser what type of
extension to render the page with.
For example, a Resources with an alias of 'test' and Content Type "CSS" that has a file extension of ".css" will render as:
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 Resources. This will automatically associate that Resources with the selected Content Type.
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 window
will 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 the
type.
MIME Type - Here you can set the MIME Type for the extension, which will tell the browser what type of file the Resources is. A list of
available MIME Types can be found here or here.
File 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
<a name="prohibited"></a>
To generate a link to the current Resource, while using a named anchor of "prohibited":
To generate a link to a Resource with ID 12, while using a named anchor of "prohibited":
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 System
Settings, or use Snippets to dynamically find the path.
They behave exactly like a Document (standard Resource). Content within a Static Resource is parsed and displayed in the exact same way as a
Document.
See Also
Symlink
What is a Symlink?
Symlinks are similar to Weblinks in that they redirect to another Resources or URL; however, symlinks will persist the current URL.
See Also
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 ever
changes you may want to the symlink, and you can place it anywhere within the site, but you can not add additional content to it.
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 location
A document may actually and logically belong in more than one place in a site
You quickly and simply want to provide a short and simple url to a document located many levels deep in a site
You want to deal with various spellings of a document: foursquare, 4square, forsquare and have them all point to the same content (each
of 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 of
the 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 as
the 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 Us
is 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. In
that 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 that
Resource 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 your
top-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 by
the 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
Templates typically contain the HTML markup tags that determine the layout and appearance of your site. When a document is requested, MODx
loads the document and its template, and MODx finds all the special placeholders in the template and replaces them with the corresponding
values from the document before sending the finished page off to the user's browser.
Think of a Template like a house. Your Resource's content, then, is a person. A person can live in many different houses, but only one house.
A Resources can only be using one Template - however, it can switch Templates at any time, just as a person can move from house to house at
any 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 a
page - 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 your
HTML 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="description" content="[[*description]]"/>
</head>
<body>
<h1>[[*longtitle]]</h1>
<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 must create
a template using the manager.
Remember that simply creating a template doesn't mean that it is automatically put to use: you have to edit each Resources and specify which
Template it uses. This is different from some content management systems where each template has one or many pages. Each MODx page has
a 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 Template Variables, Chunks, Snippets, and others.
As you noticed from our Template sample code above, the fields of a Resource can be referenced using the [[*fieldName]] syntax. A list of
available Resource Fields can be found here. For example, if we wanted to show the current Resource's pagetitle in our <title> tag, we would
simply 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 output filters applied to them. For example, say we wanted to display the "introtext"
field 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>
If Templates are like a house, think of Template Variables (TVs) like rooms in that house. You can have an infinite number of TVs in a Template;
just 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 Resources
in 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 Template Variables must be explicitly assigned to the Template to be used. Once assigned to the Template, a
TV'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 Syntax
Template Variables
Chunks
Create
Usage
Processing Chunk via the API
Modifying a Chunk Via the API
See 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 management
systems. Common examples of Chunks might be your contact information or a copyright notice. Although Chunks cannot contain any logic
directly, they can however contain calls to Snippets, which are executable bits of PHP code which produce dynamic output.
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]]
You can also pass properties to a Chunk. Say you had a chunk named 'intro' with the contents:
You could even take it one step further, by adding a Template Variable that allows the user to specify their name per Resource:
Chunks are also frequently used to format the output of Snippets. A Chunk can be processed from a Snippet using the process() function; for
example, given the following Chunk named 'rowTpl':
the following Snippet code retrieves it and processes it with an array of properties for all published Resources, and returns formatted results as a
table, setting the class to "alt" if for even rows:
$output .= $modx->getChunk('rowTpl',$properties);
$i++;
}
return '<table><tbody>'.$output.'</tbody></table>';
<?php
/* create a new chunk, give it some content and save it to the database */
$chunk = $modx->newObject('modChunk');
$chunk->set('name','NewChunkName');
$chunk->setContent('<p>This is my new chunk!</p>');
$chunk->save();
/* get an existing chunk, modify the content and save changes to the database */
$chunk = $modx->getObject('modChunk', array('name' => 'MyExistingChunk'));
if ($chunk) {
$chunk->setContent('<p>This is my existing chunks new content!</p>');
$chunk->save();
}
See Also
modChunk
Using Snippets
Using a Snippet
Snippet Properties
Installing Snippets
See Also
Snippets are MODx's answer to inline PHP code. They provide customizable dynamic content, such as menus, blog or news listings, search and
other 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 wherever
you 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 Properties, which can be passed in the Snippet call, like so:
You can also aggregate these Properties into a Property Set, which is a dynamic collection of properties that can be attached to any Snippet (or
Element 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`]]
Installing Snippets
You can also download and install Snippets via Package Management. See the tutorial on installing a Package for more information.
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]].
Adopting this simplified format allows the new parser to be fully-recursive, following a source-order mechanism that does not depend on regular
expressions.
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 outer
tag 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. ¶m=`?=&is ok now, wow!?&=`), MODx Content Tags offer a powerful new set of
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 called
uncached: [[!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 the
content:
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 a
Snippet; no longer. You can simply pass a property for the Chunk:
[[$Hello?name=`George`]]
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
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 via
an Excel spreadsheet: each property is a column on that spreadsheet, and each page is a row. By default there are a specified number of
properties or columns for each page (e.g. page title, content, alias). Template Variables allow for you to extend the attributes based on your
content, 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 Resources is displayed on the web, TV tags are replaced with the actual value entered by the user. TVs are Template-specific, meaning
they can only be used in Templates that they are assigned to.
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 few
clicks 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 to
show 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:
[[*intromsg?name=`George` &messageCount=`123`]]
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
General Settings
Rendering Options (Input Type)
Properties
Template and Resource Group Access
See Also
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 you
choose. 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 we
have chosen a list, or dropdown, we can specify the Input Option Values in the following format:
option1==value1||option2==value2||option3==value3
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 Render
selected) some form fields will show:
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're
doing 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 Set
named '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, or
Template, or whereever we are using it like so:
[[*viewingSS@CarsSectionTVPS]]
Viewing: Cars
We can assign TVs to Templates, as well. This allows those Resources assigned to those Templates to edit the TVs for each Resource.
Also, TVs can be restricted to certain Resource Groups, selectable in the grid labeled "Access Permissions".
See Also
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 the
following sources:
These Data Sources can be tied (or "bound") to a Template Variable for formatting and displaying in document. In addition, the bound data in the
TVs can be almost effortlessly formatted via the Display Controls within the TV system for some truly stunning results. The format for using the
types 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 returned
is 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 the
Manager, 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 build
complex forms for data input on the web very quickly.
Types
@FILE
@RESOURCE
@CHUNK
@SELECT
@EVAL
@DIRECTORY
@INHERIT
See Also
Template Variables
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 the
chunk.
This binding is very similar to the @RESOURCE binding with the exception that it will bind the TV to a Chunk.
Usage
@CHUNK MycontactForm
See Also
Template Variables
Bindings
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 do
something 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: it
returns ALL contents of a directory, including all files and all directories - with the sole exception of directories prefixed with a period.
Usage
When you create a Template Variable, place the following text into the Input Option Values box:
@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 not an absolute file path. If
you want to list files above your site's root, you must use the ".." syntax, e.g. @DIRECTORY /../dir_above_root This binding
will 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 some
stuff in it like:
Additional Info
Can you filter which files are selected? E.g. using *.jpg? The following DOES NOT 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 be
executed. What if a user had the ability to upload (and thus execute) a JavaScript file? Also, always be wary of letting users see your directory
structure.
See Also
Template Variables
Bindings
EVAL Binding
The @EVAL Binding executes the specified PHP code. It should be used with careful security precautions.
Syntax
@EVAL php_code_here
Usage
Security
The eval() statement raises an eyebrow with anyone concerned with security: eval statements are notorious for being exploited, so it's
recommended 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 mind
wander, allow me to paint one disasterous circumstance: some web user of your MODx application logs in and has access to a field that gets
executed by an EVAL binding. This nefarious user could eval some nasty unlink() or rmdir() statements and destroy your web server files, or read
sensitive files anywhere on the web-server that PHP has access to. Be careful with these!
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 clever
than me...
See Also
Template Variables
Bindings
FILE Binding
Syntax
@FILE file_path
Binds the variable to a file, where file_path is the path and name of the file. The return value is a string containing the content of the file. The file
path is the absolute path from the root of the server or your particular installation.
The @FILE 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
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 updated
with 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. How
can 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 the
headline_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 separate
each item and display them one at a time. Our fields will look like this:
See Also
Template Variables
Bindings
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 Variables
Bindings
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 the
Resource.
Usage
@RESOURCE 12
See Also
Template Variables
Bindings
SELECT Binding
The @SELECT binding calls a database query based on the provided value and returns the result.
Syntax
To write one of these, you need to be familiar with MySQL syntax. It is recommended that you first write a functional MySQL statement that
executes without error in the MySQL command line (see errors, below for some pit-falls). Once you've verified that your query works, then you
can create a @SELECT binding with it.
All you need to do is after you've got a working MySQL query is:
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 template
must have the correct template variable(s) associated with it. If you've created the Template Variable and associated it with a Template,
and the page you're working on is using that Template, then you'll have a place to enter in some text for that variable when you edit the
page. Paste the "@SELECT ..." stuff in there. It sounds more complicated than it is, but this section is written verbosely for the sake of
clear 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 Variable
that's already in use, be careful, because your pages might require a specific type of output, e.g. the output type that a @SELECT
binding returns.
Alternatives
Before we get any more complicated, consider doing this a different way. A Snippet might do the job more easily than a binding.
If your query needs to work with template variables and you need specialized formatting for the output, the @SELECT binding is probably not the
way to go. Pretty much everything that's done with the bindings is also possible with Snippets; the bindings just provide a shortcut. When you start
over-using the shortcut, you may run into headaches.
What if you need to write a query that accesses the template variables associated with a particular page? Those variables aren't directly stored in
the site_content 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 the
pages in a particular folder have a template variable for opening_date... that field doesn't exist in the "site_content" table. Hold onto your butts,
because 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 the
data stored in the "site_content" table and makes use of the custom fields known as "Template Variables". This is open to some debate, but
unfortunately, MODx's database schema doesn't follow the strict best practices for foreign keys... it's not always clear which table is being
referenced 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 of
patience 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 the
variable 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, this
table represents instances of the particular class. In other words, one row in the site_tmplvars table might have multiple rows in this table
(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 to
site_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, the
site_tmplvar_contentvalues.value field is a text field. MySQL won't automatically recognize arbitrary text as a date value, so you'll have to make
use of MySQL's str_to_date() function. You may think that the site_tmplvars.display_params is a savior here, but it's not... you end up smashing
your nose directly into the nasty truth that the formats used by PHP's strftime() (stored in site_tmplvars.display_params) are not the same as what
MySQL 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 end
up 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 `Formatted
Opening Date`,
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. The
implementation isn't perfect. Pay close attention to the following:
Your query MUST appear on one line. Newline characters cause the @SELECT binding to choke.
Delete all MySQL comments /* this style */ and -- this style
Make sure you have entered the table names correctly! Many sites use table-prefixes, so it is imperative that you test your queries before
trying to use them in a @SELECT Binding. If your query has an error, MODx will log the error to the error log.
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 the
Output Renders, but you might find the available options limiting to you. You can write your own Snippet that formats the value of a Template
Variable.
Security
Does this binding let you execute UPDATE, INSERT, or DELETE queries (or, gasp, DROP TABLE statements)? Even if it doesn't directly support
this, 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 user
could 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 the
CMS's out there give full access to the database (including DROP and DELETE statements) with the database handle used by the application.
That's dangerous, and the @SELECT binding may expose some of those same vulnerabilities.
See Also
Template Variables
Bindings
Output Types (also called Renders) on Template Variables allow you to format the value of a TV to any different kind of output - such as a URL,
image, 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 their
Resource. 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 the
Output 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 create your own, if you know a little PHP. The list of
pre-packaged ones are:
See Also
This type allows you to output any TV input as a Date, formatted in the way you want.
Output Properties
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
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 select
list, 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
Name Description
See Also
This type allows you to wrap an HTML tag around the input.
Output Properties
Tag Name The tag to create, such as div, img, span, etc.
See Also
MODx Revolution allows you to create your own custom TV input types (similar to the textbox, radio, textarea, richtext, etc types already
available) for your Template Variables.
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 selecting
combobox.
$this->xpdo->lexicon->load('tv_widget');
// any other PHP i want here
return $this->xpdo->smarty->fetch('element/tv/renders/input/test.tpl');
And the input template, test.tpl, for the default mgr theme would have (note that it is using Smarty syntax):
<select id="tv{$tv->id}" name="tv{$tv->id}" class="combobox"></select>
<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>
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
TV Output Types allow you to output Template Variables in different markup and formats. Some examples include outputting a TV value as an
image, URL, HTML tag, date, etc.
MODx Revolution lets you create custom output types fairly easily.
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 optional
name, with some other fields for attributes. You'll need 3 files:
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
return $modx->smarty->fetch('element/tv/renders/properties/button.tpl');
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="tv-wprops-form{$tv}"></div>
{literal}
<script type="text/javascript">
// <![CDATA[
var params = {
{/literal}{foreach from=$params key=k item=v name='p'}
'{$k}': '{$v}'{if NOT $smarty.foreach.p.last},{/if}
{/foreach}{literal}
};
var oc = {'change':{fn:function(){Ext.getCmp('modx-panel-tv').markDirty();},scope:this}};
MODx.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.
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= $this->parseInput($value, '||', 'array');
/* allow multiple buttons separated by ||, or checkbox/multiple input tvs */
foreach ($buttons as $button) {
if (!is_array($button)) {
$button= explode('==', $button);
}
/* the TV value must have a value of either: text or text==name */
$text = $button[0];
if (!empty($text)) {
$attributes = '';
$attr = array(
'class' => $params['class'],
'id' => ($params['id'] ? $params['id'] : ''),
'alt' => htmlspecialchars($params['alttext']),
'style' => $params['style']
);
/* if a name is specified, use it! */
if (!empty($button[1])) $attr['name'] = $button[1];
return $o;
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'll
have 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:
See Also
Properties are simply values that can be configured for any Element via Tag Syntax. An example of a Property is the token 'debug' in this Snippet
call:
[[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 can
access them through the $scriptProperties array, or straight in their key values, as they are extract()'ed.
Property Sets are user-defined collections of properties for an Element. They can be attached to one, or more, Elements via that Element's editing
page. 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 the
property '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:
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 load
Elements 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 those
of you wanting more flexibility with knowing what properties are passed in.
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 in
the 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 like
this:
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 to
this 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.
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". That
will 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 add
options to that property from the grid. Once you save the property, it will be added to the property set.
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 before
importing!
$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 the
default value in the property set.
Using getOption
You can also get a snippet property with $modx->getOption() like this:
Conclusion
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 your
templates.
Input Filters
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`]]
[[element:modifier:anothermodifier=`value`:andanothermodifier:yetanother=`value2`]]
cat Appends the option's value (if not empty) to the input value [[+numbooks:cat=` books`]]
htmlent Similar to PHP's htmlentities. Uses the current value the system setting [[+email:htmlent]]
"modx_charset"
wordwrap Similar to PHP's wordwrap. Takes optional value to set wordwrap position. [[+bodytext:wordwrap=`80`]]
wordwrap Similar to PHP's wordwrap, with word cutting enabled. Takes optional value to [[+bodytext:wordwrap=`80`]]
set wordwrap position.
ellipsis Adds an ellipsis to and truncates a string if it's longer than a certain number of [[+description:ellipsis=`50`]]
characters. Defaults to 100.
tag Displays the raw element without the :tag. Useful for documentation. [[+showThis:tag]]
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]]
fuzzydate Returns a pretty date format with yesterday and today being filtered. Takes in a [[+createdon:fuzzydate]]
date.
ago Returns a pretty date format in seconds, minutes, weeks or months ago. Takes [[+createdon:ago]]
in a date.
userinfo Returns the requested user data. The element must be a modUser ID. The value [[+userId:userinfo=`username`]]
field is the column to grab.
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 Snippet can be accomplished simply by utilizing the
userinfo filter. Select the appropriate column from the table and link to it, like so:
Note that the user ID and username is already available by default in MODx, so you dont need to use the "userinfo" filter:
Also, Snippets can be used as custom modifiers and filters. Simply put the Snippet name instead of the modifier. Example with a snippet named
'makeDownloadLink':
[[+file:makeDownloadLink=`notitle`]]
And then the return value of that call would be whatever the snippet returns.
See Also
Settings
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. Context Settings can be overridden by User
Settings, which are specific to each user. So, the order of relevance is:
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 it
to 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 Namespaces, as well. This allows you to easily group your settings for each of your different Components.
$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 you
had 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 that
Context 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 set
it 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.
So, for example, if I were in a Snippet and wanted some default properties at the top, I could use getOption. Snippets automatically pass in an
array 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 check
for 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 default property, if you called the Snippet via the tag call:
[[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 was
specified 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 one
getOption 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 user
settings)
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
allow_duplicate_alias
allow_duplicate_alias
This option should be used with use_alias_path set to 'Yes' in order to avoid problems when referencing a resource.
allow_multiple_emails
allow_multiple_emails
allow_tags_in_post
allow_tags_in_post
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 cause
problems in a MODx install if you change it without an explicit purpose.
auto_check_pkg_updates
auto_check_pkg_updates
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
The number of minutes that Package Management will cache the results when checking for package updates.
auto_menuindex
auto_menuindex
blocked_minutes
blocked_minutes
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
When enabled, actions (or controller maps) will be cached to greatly reduce manager page load times.
cache_context_settings
cache_context_settings
cache_db
cache_db
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
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
cache_disabled
cache_disabled
This feature is experimental. MODx recommends not turning off caching site-wide, as it can significantly slow down your site.
cache_handler
cache_handler
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 have
memcached installed.
cache_json
cache_json
cache_json_expires
cache_json_expires
Expiration time for JSON format cached data. A value of zero means the cache never expires.
cache_lexicon_topics
cache_lexicon_topics
When enabled, all Lexicon Topics will be cached so as to greatly reduce load times for Internationalization functionality. MODx strongly
recommends 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
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
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
Expiration time for the Partial Resource Cache setting. A value of 0 means the cache will never expire.
cache_scripts
cache_scripts
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
When this is enabled, MODx will use a compressed version of its custom CSS stylesheets. 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 it at 'No'.
compress_js
compress_js
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
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
Type: String
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
www.mysite.com/test/
www.mysite.com/test.html
cultureKey
cultureKey
Name: Language
Type: String
Default: en
custom_resource_classes
custom_resource_classes
A comma-separated list of custom Resource classes. Specify with lowercase_lexicon_key:className (Ex: wiki_resource:WikiResource). All
custom 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 class
WikiResource that extends modResource.
default_template
default_template
The default Template to use for new Resources. You can still select a different Template in the Resource editing page; this setting just pre-selects
one of the Templates.
editor_css_path
editor_css_path
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, for
example: /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
emailsender
emailsender
The e-mail address used when sending users their usernames and passwords.
emailsubject
email_subject
error_page
error_page
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
The number of times a user can attempt to login before being blocked.
fe_editor_lang
fe_editor_lang
The language for the editor to use when used as a front-end editor.
feed_modx_news
feed_modx_news
Set the URL for the RSS feed for the MODx News panel in the manager.
feed_modx_news_enabled
feed_modx_news_enabled
If 'No', MODx will hide the News feed in the welcome section of the manager.
feed_modx_security
feed_modx_security
Set the URL for the RSS feed for the MODx Security panel in the manager.
feed_modx_security_enabled
feed_modx_security_enabled
If 'No', MODx will hide the Security feed in the welcome section of the manager.
filemanager_path
filemanager_path
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
Turns on friendly URLs, which generate SEO-friendly URL mappings for your Resources. The URL map is determined by the Resource's place in
the 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. If
the 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's
base URL to get this to work.
friendly_url_prefix
friendly_url_prefix
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 friendly
URL /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
This setting is deprecated and is no longer in use. See Content Types for how suffixes are now handled.
Here you can specify the suffix for Friendly URLs. Specifying '.html' will append .html to all your friendly URLs.
friendly_urls
friendly_urls
This allows you to use search engine friendly URLs with MODx. Please note, this only works for MODx installations running on Apache, and you'll
need to write a .htaccess file for this to work. See the .htaccess file included in the distribution for more info.
mail_charset
mail_charset
mail_encoding
mail_encoding
mail_smtp_auth
mail_smtp_auth
mail_smtp_helo
mail_smtp_helo
mail_smtp_hosts
mail_smtp_hosts
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
Prevents the SMTP connection from being closed after each mail sending. Not recommended.
mail_smtp_pass
mail_smtp_pass
mail_smtp_port
mail_smtp_port
mail_smtp_prefix
mail_smtp_prefix
mail_smtp_single_to
mail_smtp_single_to
Provides the ability to have the TO field process individual emails, instead of sending to entire TO addresses.
mail_smtp_timeout
mail_smtp_timeout
Sets the SMTP server timeout in seconds. This function will not work in win32 servers.
mail_smtp_user
mail_smtp_user
mail_use_smtp
mail_use_smtp
manager_date_format
manager_date_format
The format string, in PHP date() format, for the dates represented in the manager.
manager_direction
manager_text_direction
manager_lang_attribute
manager_lang_attribute
The language code that best fits with your chosen manager language. This will ensure that the browser can present content in the best format for
you.
manager_language
manager_language
The language for the MODx Manager interface. This will override any cultureKey setting for the mgr context.
manager_theme
manager_theme
manager_time_format
manager_time_format
The format string, in PHP date() format, for the time settings represented in the manager.
modx_charset
modx_charset
The character encoding used. Please note that MODx primarily works with UTF-8.
new_file_permissions
new_file_permissions
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. This
may 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
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
password_min_length
password_min_length
phpthumb_cache_maxage
phpthumb_cache_maxage
Delete cached thumbnails that have not been accessed in more than X days.
phpthumb_cache_maxfiles
phpthumb_cache_maxfiles
phpthumb_cache_maxsize
phpthumb_cache_maxsize
Delete least-recently-accessed thumbnails when cache grows bigger than X megabytes in size.
phpthumb_cache_source_enabled
phpthumb_cache_source_enabled
Whether or not to cache source files as they are loaded. Recommended to leave at No.
phpthumb_far
phpthumb_far
The default far setting for phpThumb when used in MODx. Defaults to C to force aspect ratio toward the center.
phpthumb_zoomcrop
phpthumb_zoomcrop
The default zc setting for phpThumb when used in MODx. Defaults to 0 to prevent zoom cropping.
proxy_auth_type
proxy_auth_type
proxy_host
proxy_host
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
proxy_port
proxy_port
proxy_username
publish_default
publish_default
rb_base_dir
rb_base_dir
The physical path to the resource directory. This setting is usually automatically generated. If you're using IIS, however, MODx may not be able to
work 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).
rb_base_url
rb_base_url
Enter the virtual path to resource directory. This setting is usually automatically generated. If you're using IIS, however, MODx may not be able to
work 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).
request_controller
request_controller
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
The name of the GET parameter to identify Resource aliases when redirecting with FURLs.
request_param_id
request_param_id
The name of the GET parameter to identify Resource IDs when not using FURLs.
search_default
search_default
server_offset_time
server_offset_time
Select the number of hours time difference between where you are and where the server is.
server_protocol
server_protocol
session_cookie_domain
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 are
doing.
session_cookie_lifetime
session_cookie_lifetime
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 choose
the 'remember me' option on login.
session_cookie_path
session_cookie_path
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
session_handler_class
session_handler_class
For database managed sessions, use 'modSessionHandler'. Leave this blank to use standard PHP session management, or set to your own
custom session handling class.
session_name
session_name
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 are
doing.
settings_version
settings_version
Name: MODx Version
Type: String
Default: modx-2.0.0-pl
Do not change this value! Changing it could seriously affect your system.
signupemail_message
signupemail_message
Hello [[+uid]]
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 their
username and password.
The following placeholders are replaced by the Content Manager when the message is sent:
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't
know their username or password!
site_name
site_name
site_start
site_start
Make sure the ID you enter belongs to an existing Resource that has been published!
site_status
site_status
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
This message will only be displayed if the site_status option is set to No.
site_unavailable_page
site_unavailable_page
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
tree_root_id
tree_root_id
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 be
able to see Resources that are children of those specified Resources.
udperms_allowroot
udperms_allowroot
If true, allows users to create new resources in the root of the site.
This setting is deprecated in MODx Revolution. It has been replaced by the 'new_document_in_root' Permission in the
Administrator Policy.
unauthorized_page
unauthorized_page
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
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 size
setting in the php.ini.
use_alias_path
use_alias_path
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 to
the document will be displayed as '/parent/child.html'.
use_browser
use_browser
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 on
the server.
This setting is deprecated in MODx Revolution. Use the file_tree and file_manager Permissions instead.
use_editor
use_editor
If Yes, will enable any installed Rich Text Editor in the manager.
use_multibyte
use_multibyte
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 mbstring
PHP extension installed.
Turning this on will use the modx_charset setting for the charset in the mb_ PHP functions.
welcome_screen
welcome_screen
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
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
Select which RTE you would like to use when editing an Element.
Summary:
You can have firendly URLs fully functioning in under two minutes by following a simple three step process:
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 the
root 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 text
editor and edit it accordingly:
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 the
home 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 them
what you need to get it working. Italicized content left to show entire file, but is not necessary for Friendly URLS.
Next change the settings in the Friendly URLs Area of the System Settings in the System menu of the Revolution Manager to something like the
following 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 automatically generate alias. More than likely it was something I
was 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.
Make sure you have the following between the <head></head> tags:
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 easily
be dragged between contexts to move them from one context to another.
Note: there's nothing fundamentally different about resources in different contexts, except that they now inherit the configuration
settings of the context they are in. So, if you create a new context, you'll have to override the context settings in the context for
any 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 will
override any System Settings. Your new context will be completely empty, requiring you to include any and all settings you will be using.
See Also
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.
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 on
it, and you'll need to add a few settings:
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 back
to.
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 to
Apache'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 is
fine; 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.
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/ (or
whatever base path your webroot is in).
You'll need to copy 3 files from your MODx Revolution's root directory:
index.php
.htaccess
config.core.php
index.php
$modx->initialize('web');
.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 base_url context setting you set up earlier.
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 page
at 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
See Also
Contexts
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 a
single gateway, you would use a plugin to switch contexts registered to the OnHandleRequest event, something like so:
<?php
switch ($modx->getOption('http_host')) {
case 'domain2.tld:80':
case 'domain2.tld':
// if the http_host is of a specific domain, switch the context
$modx->switchContext('domain2.tld');
break;
default:
// by default, don't do anything
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 rules
altered 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 smart
enough to route requests to the appropriate context gateway, and you would need to configure the request_controller option in Context Settings
appropriately.
You can also still use a custom core location in either of these scenarios; this is independent of the context-driven multi-site capabilities.
A full list of documented customizable forms, along with their fields and other info, can be found here:
Examples
An example set of Rules:
See Also
FC-Resource
Resource Create/Update
Available Fields
Available Tabs
Hiding the Content Field
TVs
Resource Create/Update
resource/update
resource/create
Available Fields
Available Tabs
Field: modx-resource-content
Containing Panel: modx-panel-resource
Rule: Field Visible
Value: 0
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'd
like to affect. You can leave the Containing Panel blank.
FC-Template
This page is under construction.
FC-Chunk
FC-Snippet
FC-Plugin
What is a Rule?
What are the Rules available?
Creating a Rule
Constraints
See 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 a
Rule will only be applied if the User is part of that User Group.
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 with
a window:
Let's go into the fields.
Action This field lets you select any available Action. The rule will only apply to that Action, and no other. No
User Group Here you can optionally choose a User Group to tie this Rule to. If a User Group is chosen, the Rule will only apply Yes
to Users of that User Group.
Field The name of the field or tab to apply the Rule to. No
Containing The name of the containing panel that the Field or Tab exists in. Most all Rules will need this value. No
Panel
Value The value you'd like to set the Rule to. The type of value can vary depending on the Rule. No
Constraint If set, will restrict the Rule to a field value (via Constraint Field and Constraint) on this class. Example: modResource Yes
Class for the Resource Update screen.
Constraint If set, will restrict the Rule to the field specified, with the value of Constraint Yes
Field
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 for
resource/update to only occur on Resource's with a Template ID of 4. You'd simply set the Constraints like so:
Or, further, if you wanted to restrict the Rule to Resources with a menutitle of "boo":
See Also
Field Default
The Field Default Rule, when set, will automatically set the default value of a field.
Examples
An example Rule of setting the default Category for a Snippet for all Users in the User Group HR Department would look like this:
See Also
The root page Form Customization could not be found in space MODx Revolution 2.0.
Field Label
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 Users in the User Group "Marketing" would look like this:
See Also
The root page Form Customization could not be found in space MODx Revolution 2.0.
Field Visible
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 Users in the User Group "HR Department" would look like this:
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 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.
ID Description
You can also create a tab with the New Tab rule and move a TV there.
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, when set, will create a new tab in the panel.
Usage
Available TabPanels
Here are the IDs for the corresponding tab panels on various manager pages.
ID Corresponding 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 will change the title of any Tab.
Usage
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 Users to "Custom Fields" would look like this:
See Also
The root page Form Customization could not be found in space MODx Revolution 2.0.
Tab Visible
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 Users would look like this:
See Also
The root page Form Customization could not be found in space MODx Revolution 2.0.
TV Default
Usage
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
Examples
An example Rule of setting the label to "Boo!" for a TV with ID of 1 for all Users would look like this:
See Also
The root page Form Customization could not be found in space MODx Revolution 2.0.
TV Visible
Examples
An example Rule of hiding the a TV with ID of 1 for all Users would look like this:
See Also
The root page Form Customization could not be found in space MODx Revolution 2.0.
Security
Each user in MODx has a User object, which can be assigned to any number of User Groups. Each User Group then has attributes assigned to it
via Access Control Lists (ACLs). These ACLs take a variety of names depending on how they are applied, but all share one common principle -
they contain a list of Permissions. These Permissions allow access to different areas or actions within MODx.
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 allow/deny in MODx, meaning that access is "open" by default. Once an ACL is applied to an object, such as a
Context or Resource Group, those Contexts or Resource Groups will now only be accessible to the objects with appropriate
Permissions.
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 'Access
Permissions' 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 Role with more authority) of the 'HR Dept' User Group.
See Also
Users
What is a User?
User Settings
Users in the Front-End
User Fields
Grabbing the User via the API
See Also
What is a User?
Users can also be assigned to User Groups, which can have ACLs attached to them to provide Access Controls.
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 be
completely unique settings as well. The order of inheritance for Settings is:
When a user is logged into the frontend of your site, their username and ID can be accessed by the following Properties:
If a user is not logged in, ID will be blank, and Username will be "(anonymous)".
User Fields
Name Description
active Either 1 or 0. If not active, the user will not be able to log in.
Users also have a Profile attached to them. It contains the following fields:
Name Description
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.
failedlogincount The number of times the user has failed to log in since last logging in.
extended A JSON array that can be used to store extra fields for the User.
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 of
the user and returns it:
$profile = $modx->user->getOne('Profile');
return $profile ? $profile->get('email') : '';
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
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 User
Group by right-clicking on the User Group and either:
Assigning Policies
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.
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 to
fine-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 the
User 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 will
look 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 resource
group "TestResourceGroup4" to only Users in the "HR Department" User Group with at least a Role of Supervisor. Roles with lower authority
numbers would also inherit access - say you had a Coordinator Role with an authority of 2; Users in this User Group would have access to this
ACL as well.
See Also
Resource Groups
What is a Resource Group?
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". Lower numbers represent a stronger authority. E.g. a Role with Authority 10 will inherit
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 of
the 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. Authority=1 is the first authority and trumps
Authority=2 (i.e. the second authority).
Usage
One common example is to create Roles that mimic a basic employee position structure. Let's say we create the following Roles and Authority
levels:
Administrator - 0
Director - 1
Coordinator - 2
Supervisor - 3
Employee - 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 multiple
Users 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 Access
Policy (which is a set of Permissions) called "AccountPolicy" that has the following Access Permissions in it:
view_accounts
save_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 also means that John has these Permissions as well, since he is a "Coordinator" which has a stronger Authority level than "Supervisor".
So, John as Coordinator has "inherited" the Permissions than Mark had as Supervisor.
See Also
Policies
An Access Policy is a set of Permissions containing one or many Permissions, as defined in the manager.
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 Access Control Lists (ACLs) to a Context and User Group, with a specified Minimum Role. When done, this
means 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 the
Context specified in the ACL.
MODx comes with a default "Administrator" Policy that contains all the Permissions one would use in a Context ACL. It's best to duplicate this
policy when creating a custom access policy for restricting manager users.
They can also be Resource ACLs, that limit access to Resources based on Roles and Resource Groups. MODx comes packaged with a default
"Resource" Policy that contains all the basic Permissions one would use in a Resource Group ACL.
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.
Elements can be restricted from view by ACLs on Categories. For example, if you had a User Group called 'Developers', and wanted Users in that
group to be the only Group that could see Elements in the Category 'Gallery', you would create an ACL like such, in the "Element Category
Access" 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
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
Policies into a list, which is called an Access Control List (or ACL). From there, the Access Policy defines all the Permissions that the list contains.
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 the
Content 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 and
manage existing Permissions, as well as create new ones.
See Also
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
action_ok
edit_parser
element_tree The ability to view the Elements Tree on the left nav.
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.
See Also
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 are
generic 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
An ACL, or Access Control List, is a set of Permissions attached to an object. More information on ACLs can be found 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
A User Group
A Minimum Role
An Access Policy
This means that one can assign a ACL to a Context that will apply to:
Resource ACL
Resource ACLs behave a bit differently, and basically allow you to restrict access to Resources (such as Documents, Weblinks, etc) by Resource
Groups. They are comprised of 5 Parts:
A Resource Group
A User Group
A Minimum Role
An Access Policy
A Context
See Also
Security Tutorials
Security Tutorials
Here are some tutorials designed to help you get started with Security in MODx Revolution.
The Problem
You want a User to have manager editing access, but not have all the Permissions of an Administrator user. This tutorial, partially written by
BobRay, will help you through that.
The Solution
See Also
Introduction
Step-by-step explanation
1. Create a Resource Group
2. Link your member-only resource to the Resource Group
3. Create a User Group
4. Add Context access
5. Add Resource access
6. Add users to the user group
7. Flush permissions
Help! I can't get this to work, still!
See Also
Work in progress
Please note that this document is a work in progress and may still be inaccurate. It should, however, be possible to set up a
member 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 Manager
and 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 document
has 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 more
information and would like some examples, scroll down to the related subsection below.
1. Create a Resource Group (Security -> Resource Groups -> Create Resource Group)
2. Link your member-only resource to the Resource Group. (By editing the resource, or by dragging them from the resource tree to the right)
3. Create a User Group (Security -> Access Controls -> User Groups -> New User Group)
4. Edit the user group you created to add Context Access (Security -> Access Controls -> User Groups -> Right click your user group -> on
the 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!))
5. Add a resource group entry on the Resource Group Access tab (context: web, minimum role: Member (9999), access policy: resource)
6. Add users to the user group with the role of Member. (Security -> Manage Users)
7. Flush permissions (Security -> Flush Permissions) and try it in another browser (not just another browser window: another browser)
Step-by-step explanation
Work in progress
This 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 find
this section interesting.
To create a resource group, navigate to Security -> Resource Groups and click on the Create Resource Group button. In the popup you are
prompted to enter a name for the resource group. In the tutorial we expect you named it "Protected".
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 longer
show 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 would
rather return the 403-error, you will need to give the anonymous user group "load" permission for the resource group. More about this in a later
tutorial. At this moment in the tutorial, your page will not be visible as you have not yet added it to 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 a
new 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 the
group, and submit the form.
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 has
access 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 User
Group". You will find five different tabs:
1. General Information: this contains the name and if applicable the parent group.
2. Users: shows an overview of users in the user group and offers you to add new users.
3. Context Access: we will discuss this in a minute.
4. Resource Access: we will discuss this in step five.
5. 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 context
dropdown.
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 set
this 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 with
an 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 default
there are several Access Policies added. In this case, we'll set it to Resource. This Access Policy includes all default permissions for resource
access. If you ever want to change some of the permissions (which may be covered in a different tutorial or article) always duplicate the access
policy and don't modify it directly, as there's no way to recover changes made.
Context: web
Minimum 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.
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 out
of four fields are similar to the Context Access groups, namely the Context, Minimum Role and Access Policy. A new one is Resource Group
which, as you probably guessed, defines the resource group the user group can access.
The settings:
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 will
ask 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 sure
that, 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 your
Manager 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 resources
may cache their permissions.
Make sure you followed everything step by step and that you flushed permissions properly. If everything seems to be alright, check again and
then 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 about
what is incorrect so it can be fixed.
See Also
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 Package Management requires cURL or PHP Sockets. MODx will let you know if you don't
have either of these. If you are still having problems with Package Management after confirming these are installed, see
Troubleshooting Package Management.
Browse the available packages, opening the folders to expose the individual packages. Click Download to download whichever packages you'd
like 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 to
choose 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 New
Package 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 as
wayfinder-2.1.1-beta1.transport.zip. Then, click on 'Add New Package' in the packages grid. From there, select the 'Scan Local' option. The
package 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-up
menu.
See Also
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
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 Advanced distribution, make sure you have the "Core Package has been manually unpacked" and "Files in-place"
checkboxes unchecked, and that the core/, manager/ and connectors/ directories are writable.
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 break
your site.
Version-Specific Changes
For changes relating to specific versions, please see the following pages:
See Also
Automated upgrading from 0.9.6.*/Evolution is not yet available in MODx Revolution 2.0.0. A migration tool will be provided
sometime 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 strongly recommend backing up your data first. Once
you'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 the
Revolution core, as well as all of your tags to the new Tag Syntax. Component developers will hopefully already be converting their scripts by this
point, so you may be able to find Revolution-compatible scripts via Package Management, or on modxcms.com or in the forums.
Also, it's worth noting that there are no more "web users" or "manager users" - only Users. And the new permissions scheme is vastly different
than 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 Revolution
equivalents:
Evolution Revolution
Jot Quip
SiteMap GoogleSiteMap
MaxiGallery Gallery
eForm FormIt
Wayfinder Wayfinder
DocManager Batcher
AjaxSearch SimpleSearch
WebLogin Login
See Also
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 are
parsed in the order that they occur.
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 Snippet haven't executed yet.
Tags can now have tags within their properties. [[mySnippet? &tag=`test[call]`]] is now 100% a-okay.
_Using =,?,!,* is now OK in a Snippet property.
Although this has been mostly remedied in later versions of Evolution, there is still a performance hit in those versions. This, caching-wise, has
been 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 Snippets
that 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.
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 to
access 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 Access
Policy of "Load."
The settings friendly_url_prefix and friendly_url_suffix are no longer applicable, as Revolution handles those now through Content Types.
There are a few changes that have occurred in 2.0.0 RC-2 that will only apply to developers. If you are not:
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:
This means that the only Lexicon Entries stored in the database are overrides made by the user.
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 documentation
on these events on the System Events page, or view an exhaustive list via the 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*Save
On*BeforeSave
On*Remove
On*BeforeRemove
This means they will fire regardless of where they are executed. This allows you to fire events even when 3rd Party Components modify those
objects, such as when a 3PC creates a user. Please see the documentation on each respective event for more information.
See Also
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 all
the 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 then
deploying them on the new server... but in Revolution, you may have discovered that maybe something broke in the process... maybe the
manager 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 if
you move your site to a new folder on your current web server).
On a UNIX style system, you can create a compressed file using the tar command:
Forget me Not
A good mnemonic for the "-czf" option is Create Zip File.
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 and
you 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.gz
tar 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 might
inadvertently forget to copy those hidden files. It's better to rename or move the containing directory instead.
If you use mysqldump, be sure you use a username that has SELECT and LOCK permissions on all your MODx Revolution database tables –
usually it's best to simply use the same username and password that are defined in your configuration file (/core/config/config.inc.php).
Remember that mysqldump will prompt you for the password after you execute this command: when you type it (or paste it), you won't see
anything 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:
/* 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/';
Permissions
Before you can edit your config file, you may need to loosen up the permissions. After you've edited it, be sure you restore the
read-only permissions on the file.
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 the
following command (again, you need to customize the query depending on your database, prefix, and the path to your data):
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 new
one.
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.
Developing in MODx
Code Standards
Code Standards
General Practices
Indentation and Line Breaks
Trailing Spaces
Compression
HTML
Validation
Inline HTML in Snippets
Self-closing Elements
Terseness
Doctype
Tags and Attributes
Quotes
CSS
Inline Styles
CSS Validation
CSS Formatting
Pixels vs. Ems
Internet Explorer Bugs
Shorthand
Margin & Padding
Hex Colors
Background
Border
Font
Longhand
Javascript
Type Coercion
White-space
Variables, ID & Class
Quotes
Event Listeners
Event Delegation
Closures & Scope
Objects & Arrays
PHP
General
Parenthesis
Classes
Variables
Function Arguments and Class Variables
Arrays
Constants
File Structure
Prefixing
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 guidelines
for easier development and collaboration between developers.
This page was heavily borrowed from Fellowship One's Design Standards. Thanks!
General Practices
All indentation must be done with 4 spaces, not tabs. Make sure to change your editor settings to reflect this. Line breaks must be in UNIX format.
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 production
environments, but allowing users the option to toggle between compressed and uncompressed JS/CSS. This allows for easier debugging.
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. It
attempts to solve issues found in previous iterations of HTML and addresses the needs of Web Applications, an area previously not adequately
covered by HTML. (from html5.org)
Validation
All HTML must be HTML5-validated. MODx recommends using the W3C Validator.
MODx requires that no html be echo'ed or inline in a Snippet. MODx also recommends externalizing any HTML in PHP code into 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 directly
indicative 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 must
be 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 space
should 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 DOCTYPE
declaration has been simplified significantly. Additionally, there is no need to use CDATA to escape inline JavaScript, formerly a requirement to
meet XML strictness in XHTML.
"HTML5 Doctype"
<!DOCTYPE html>
"XHTML 1.0 Transitional Doctype"
All tags and attributes must be written in lowercase. Additionally, we prefer that any attribute values also be lowercase, when the purpose of the
text therein is only to be interpreted by machines. For instances in which the data needs to be human readable, proper title capitalization should
be followed, such as:
"For machines"
"For humans"
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"
"Incorrect"
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 only
makes maintenance a nightmare, but inextricably ties the presentation to the data it represents. All of our CSS will be stored in external files, with
one 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 with
text 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 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 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 be
listed 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, we
know 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 of
indentation. 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"
"Also incorrect"
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 font
sizing used to be popular, to accommodate for Internet Explorer 6 not resizing pixel based text. However, all major browsers (including IE7 and
IE8) 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 a
multiplier 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 if the browser default text size is 16px.
*/
#selector {
font-size: 0.813em;
line-height: 1.25em;
}
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"
<!--[if IE 7]>
<link type="text/css" rel="stylesheet" href="/assets/styleshseets/ie7.css" />
<![endif]-->
<!--[if IE 8]>
<link type="text/css" rel="stylesheet" href="/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, such
as the case with margin and padding. Developers should be aware of the TRBL acronym, denoting the order in which the sides of an element are
defined, in a clock-wise manner: Top, Right, Bottom, Left. If bottom is undefined, it inherits its value from top. Likewise, if left is undefined, it
inherits 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-shorthand
http://sonspring.com/journal/css-redundancy
http://dustindiaz.com/css-shorthand
"Correct"
#selector {
margin: 0 0 10px;
padding: 0 0 10px;
}
#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 as
possible. This means that colors such as full blue, which can be written lengthily as #0000FF, should be simply written as #00f. Obviously, for
colors 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;
}
#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 order
in which values are declared are: width, style, and color.
#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 which
to use. Note that method 2 follows the TRBL pattern.
#selector {
border-color: #fff #999 #666 #ccc;
border-style: solid dashed dotted double;
border-width: 1px 2px 3px 4px;
}
#selector {
border-top: 1px solid #fff;
border-right: 2px dashed #999;
border-bottom: 3px dotted #666;
border-left: 4px double #ccc;
}
By contrast, the same style declaration is extremely verbose using longhand. This should be avoided, except in instances where only one
particular 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 all
the aspects of the font into a single declaration, whereas the longhand splits it out over several lines. While the contrast between methods is not
as 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 scope
of 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 we
see a longhand declaration used, we know that we are specifically overriding only a very precise part of an overall style, thereby leaving other
aspects 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. This
sometimes creates awkward scenarios in which numerical values are seen as false or the existence of a string is mistaken for true. This is
typically 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-equals
operator ===. In similar fashion, it also has a strict negation operator !==. Consider the following examples of potential pitfalls when it comes to
evaluating comparisons.
As you can see in the example above, simply using == and != is insufficient because it makes for potentially unpredictable results. Therefore, the
stricter comparison operators should always be used. There is never a good reason to use the lesser form of comparison operators. To simply for
the existence of elements in the DOM, there is an even more abbreviated way, that leaves no room for ambiguity. If you are unsure if certain
elements will be present in an HTML page, use one of the following techniques.
function first_func() {
if (!document.getElementById('id_name')) {
return;
}
// If code gets here, element exists.
}
function second_func() {
if (!document.getElementsByTagName('div').length) {
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 comma
and colon (and semi-colon where applicable), but no spaces immediately inside the right and left sides of parenthesis. In short, we advocate
readability within reason. Additionally, braces should always appear on the same line as their preceding argument.
"Correct"
"Incorrect"
All JavaScript variables shall be written in completely lowercase letters, with underscores to separate words if need be. Likewise, all id and class
declarations in CSS shall be written in the same manner. Neither dashes nor camelCase shall be used, except for words that contain dashes
when 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 because
HTML 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"
"Incorrect"
Event Listeners
Rather than using attributes such as onload, onfocus, onsubmit, or onclick directly in markup, we will instead attach event listeners to these
elements via unobtrusive techniques. The reasoning for this is the same philosophy that is behind not using inline style="..." declarations. So
doing 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 some
resulting action. However, occasionally there may be multiple elements which match the criteria for which you are checking, and attaching event
listeners to each one might negatively impact performance. In such cases you should use event delegation instead.
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() {
var first_variable = 'value 1';
var second_variable = 'value 2';
function first_func() {
// Do something.
}
function second_func() {
// Do something.
}
})();
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 share
common 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 of
transmitting data, used as an alternative to XML.
var john_doe = {
first_name: 'John'
,last_name: 'Doe'
,job: 'Everyman Respresentative'
,email: '[email protected]'
,married: true
,age: 30
};
/*
Could also be written:
var john_doe = new Object();
*/
var john_doe = {};
john_doe.first_name = 'John';
john_doe.last_name = 'Doe';
john_doe.job = 'Everyman Representative';
john_doe.email = '[email protected]';
john_doe.married = true;
john_doe.age = 30;
"Array literal - preferred"
var doe_family = [
'John'
,'James'
,'Jane'
,'Jenny'
,'Jared'
,'Jerome'
];
/*
Could also be written:
var doe_family = new Array();
*/
var doe_family = [];
doe_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
if ($test) {
}
while ($test == $other) {
}
array_push($one,$two);
return $test;
Do not use parenthesis when using include, require, include_once, and require_once.
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 {
public $publicVar;
private $_privateVar;
private function _privateFunc() { }
public function publicFunc() { }
}
Variables
class modFactor {
public function testFunc($testVar, array &$anotherTest = array()) {
$this->_privateVar = $testVar;
$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
File Structure
Prefixing
$_lang['mycomponent.welcome_message'] = 'Welcome!';
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"
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 Model
The View
In the front-end, they are Templates, Chunks and Resources.
Templates
Chunks
Resources
In the Manager
The Controller
Snippets
Plugins
The 2nd C: The Connectors
See Also
What is MODx?
MODx Revolution is an Content Application Platform, built for developers, designers and users who want a powerful, scalable system with flexible
content 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, which
connects 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 limit
user 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 takes
advantage of the newly adopted standard for database persistence in PHP 5.1+, PDO. It implements the very simple, but effective Active Record
pattern for data access, as well as a flexible domain model that allows you to isolate domain logic from database-specific logic, or not, depending
on 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 management
system (DBMS) similar to a relational database, but with an object-oriented database model: objects, classes and inheritance
are directly supported in database schemas and in the query language. In addition, it supports extension of the data model with
custom data-types and methods.
Basically, tables in SQL databases become classes that can contain table-specific methods, inherit from base classes, and much more.
Revolution at its core is a Content Management Framework. It's not a PHP Application Framework like CodeIgnitor or Symfony, nor does it
purport to be one. With that said, it's much more than a typical CMS like Wordpress or others; it enables you to build Content Management
Applications 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-controller
architectural pattern, in programming.
The Model
The M stands for Model, which is the core classes that manipulate data records. These core classes, prefixed with 'mod' in Revolution, handle all
the 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 accessed
directly, 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.
Templates
Templates are what they sound like. They allow you to create templates that will encapsulate more page-specific data. Think of them like
headers/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 and
ease 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 client
accesses 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 in
manager/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 and
Plugins.
Snippets
Snippets are simply PHP code that can be placed anywhere in a page. They can be placed in Chunks, Templates, or Resources. They simply
execute 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 the
webpage 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 cache
handling, context redirection, and more.
Connectors are a new idea to MODx Revolution; they are access points for processors. The manager system in MODx Revolution uses them
extensively; 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 parameter
of '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; this
is 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 Access
Policy 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
MODx Revolution is an OOP Framework, built around the database ORM xPDO.
3rd-Party Components (3PCs) are collections of any sort of MODx Objects. They can be a collection of Snippets, Plugins and Chunks, or a single
Snippet, or just a collection of files. They are usually transported and installed via Transport Packages.
MODx doesn't necessarily limit where you can put your custom 3rd party component files, but we do have some recommendations. For files that
don'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 your
files 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 are
associated to specific system events. For example, in an average MODx page request, several events happen at certain points within the page
parsing process and plugins can be attached to any of these events to fulfill a desired function. Plugins aren't just limited to front-end processing
though, there are many events that are available in the MODx Manager.
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.
Custom Manager Pages, or CMPs, are custom pages in the manager built by 3rd Party developers to allow backend management of
Components. They use the modAction and modMenu objects to dynamically create manager pages that can be easily found and added with no
hacking of the core.
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 = new modX();
$modx->initialize('web');
This will initialize the MODx object into the 'web' Context. Now, if you want to access it under a different Context (and thereby changing its access
permissions, policies, etc), you'll just need to change 'web' to whatever Context you want to load.
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 into
core/components allows MODx developers to add an extra level of security to their Extras.
See Also
In developing MODx Revolution, the MODx Team has found the following environments invaluable:
Netbeans
Netbeans 6.8
Netbeans Subversion and JIRA plugins
Eclipse 3.6 Helios and EclipsePDT
Note: With the MODx project being housed on github.com/modxcms there is an additional and optional eclipse project eGit you
may also wish to install. opengeek (Jason Coward) strongly suggests learning and using git from the command line to
maximize your flexibility and potential. It may also be suggested to set up a local git repo and simply clone the respective
MODx and xPDO repositories, working from local copies. SVN is discouraged from continued usage in regards to future MODx
related development.
Additional suggestions: Install your IDE in a location which is static and remains consistent for long periods of time. You may
also want to isolate your workspace to a dedicated partition/drive, especially in operating systems (such as Soalris, Linux, and
Mac OS) which do not require erasure/formatting of the entire drive to install. By placing the development tools and projects in
dedicated 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 (such
as repos and file type associations with specific tools) are stored in the workspace and may actually inadvertently cause issues
if an alternate tool is being used in place of an older one.
Installation
Other IDEs
For Mac:
TextMate - IDE
Coda - IDE
Versions - SVN client
svnX - SVN client
For PC:
UltraEdit - IDE
E - IDE
TortoiseSVN - SVN client
Kate - IDE for Linux / KDE
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 soon
SimpleTest - this drives the PHP 4/5.0.x unit testing framework for xPDO, and we'll be adding a test harness to MODx soon
PHPDocumentor - all of the classes in MODx Revolution are documented in PHPDoc format, and we'll be developing tutorials and other
extended documentation for inclusion in the PHPDocs in DocBook XML format
Phing - 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
Overview
What is a Snippet?
How Do They Work?
Simple Example
Passing Values Into a Snippet
Database Interaction in Snippets
Why an ORM?
Example DB Code
Further Database Reading
Recommended Methods and Tips
Write your Snippets outside of MODx.
Don't try to mix PHP and HTML in a Snippet.
Don't Work on Live Snippets
Use 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 for
most 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 this
from a "chunk", so a helpful mnemonic might lie in the p's... as in "PHP", e.g. sni-"P(h)P"-et.
First off, most Snippets are cached, meaning they're stored as a temporary, dynamic function in the cache. If they're flagged as uncached, then
they 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:
<?php
return 'Hello, World!';
?>
If you named this "helloWorld", you could call this snippet by using [[helloWorld]] in your documents, templates, or Chunks.
Note how we returned the code rather than 'echo'ed the content out. Never use echo in a Snippet - always return the output.
Snippets can take input values using a modifed CGI web-form type notation. For example, if your Snippet looks something like this:
<?php
return 'My input was: ' . $input;
?>
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, not
single quotes!
Accessing the database layer in MODx is quite simple; MODx uses an Object Relational Model (ORM) called xPDO for database connectivity.
Why an ORM?
You might be asking, why use an ORM instead of just straight SQL? Well, a few reasons:
1. 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.
2. Parameter Escaping - No more having to worry about SQL injection; xPDO uses PHP's PDO to escape all variables passed in to the
SQL call to prevent any malicious calls.
3. 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',
));
if (!$chunk) return 'No line item chunk!';
return $chunk->process(array(
'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 actually
an xPDOObject, which is an object representation of the Resource.
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 hidden
from 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 query
from - 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 those
using foreach and do whatever we want with them.
This is pretty easy to do - just create an 'include' snippet, but make its content be this:
if (file_exists($file)) {
$o = include $file;
} else { $o = 'File not found at: '.$file; }
return $o;
[[!include? &file=`/absolute/path/to/my/snippet.php`]]
Then you can test them to make sure they work (e.g. on the bash command line, you can use the command php -l my_script.php to check the
script for syntax errors). Depending on your environment, you may also get some useful error messages to help you with debugging. Copy and
paste the code into MODx only when you're sure it's working.
Snippets execute PHP code. They should always begin with a <?php and end with a ?> You cannot mix PHP and HTML in a Snippet! For
example, the following code won't work:
You'll find that MODx will append PHP tags to beginning and end of the snippet, creating an invalid syntax, e.g.:
If you need to do something like this, use a Chunk - separate the PHP into a Snippet, load its output into a placeholder with the modx API
placeholder functions or chunk processing, and include the Snippet's placeholders in the Chunk:
$output = $modx->getChunk('myChunk',array(
'placeholderOne' => 'test',
'name' => 'Harry',
'scar' => 'Lightning',
));
return $output;
If you're writing new versions of Snippets, duplicate the old version! That way you can go back to the old version of the code if something doesn't
work correctly! MODx doesn't inherently do versioning control, so you have to backup code yourself.
Consider adding your properties for your snippet into the Properties grid, so that the user can add custom Property Sets to override them.
See Also
modX.runSnippet
modX.setPlaceholder
modX.regClientCSS
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 into
Chunks. This tutorial shows you how to do that in a Snippet.
Templating Snippets
Our Initial Snippet
Templating the Snippet
Adding A Row Class
Passing a Custom ID
See Also
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);
if (count($children) > 0) {
$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>';
}
return $output;
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. We
want more flexibility.
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="[[~[[+id]]]]">[[+pagetitle]]</a></li>
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 just
using 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 to
the 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).
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, replaces
the 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, powerful
snippet.
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 a
default 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:
Note how we're explicitly setting the 'rowCls' variable into our $resourceArray property array. We do this because we've already gotten the value
of 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 at
the top, below our getOption calls:
$id = (int)$modx->getOption('id',$scriptProperties,390);
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 we
want 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 are
folders, etc. The important part is that now you have the general idea.
$c = $modx->newQuery('modResource');
$c->where(array(
'published' => true,
'deleted' => false,
));
$children = $modx->getChildIds($id);
if (count($children) > 0) {
$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);
}
return $output;
See Also
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 custom
Template 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.
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're
added, 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">
var myCustomJSVar = 123;
</script>');
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 often
useful for custom analytics scripts, or JS that needs to be run at the body-level rather than in the HEAD.
regClientScript
Similar to regClientStartupScript except that it runs before the closing BODY tag:
$modx->regClientScript('assets/js/footer.js');
regClientHTMLBlock
Similar to regClientStartupHTMLBlock except that it runs before the closing BODY tag:
$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 also
recommends in any Extras you are distributing, to make sure inserting CSS or JS into a page is a toggleable option, so that the user can
customize the content or javascript framework should they so choose.
See Also
Plugins
What is a Plugin?
The Event Model
Handling an Event
Plugin Examples
Word Filter
Page-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 are
associated with specific System Events. For example, in an average MODx page request, several events happen at certain points within the page
parsing process and plugins can be attached to any of these events to fulfill a desired function. That means that when those events "fire," control
is transferred to any Plugin "listening" for that event. Once the Plugin's code has executed, control returns to the point after the spot where the
System 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 System
Events here.
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 like
so:
$eventName = $modx->event->name;
The code for a Plugin listening to more than one event looks like this:
$eventName = $modx->event->name;
switch($eventName) {
case 'OnWebPageInit':
/* do something */
break;
case 'OnWebPagePrerender':
/* do something 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
System Events: OnWebPagePrerender
Page-Not-Found Redirector:
See Also
System Events
System Events are the events in MODx that Plugins are registered to. They are 'fired' throughout the MODx code, allowing Plugins to interact with
MODx code and add custom functionality without hacking core code.
The system events table is found under {table_prefix}_system_eventnames, and has the following fields:
Service Types
The 'service' field in the System event is a number; the numbers reference different types of System Events. They are:
Available Events
This is not an exhaustive list as events are still being documented. Thank you for your patience. The TV, Template and Snippet
events are still to be documented. For a complete list, please either view a Plugin in the manager and see the System Events
tab, or view here. Note also that all WUsr (web-user) events have been removed.
See Also
OnBeforeCacheUpdate
Event: OnBeforeCacheUpdate
Event Parameters
None.
See Also
System Events
Plugins
OnBeforeChunkFormDelete
Event: OnBeforeChunkFormDelete
Event Parameters
Name Description
See Also
System Events
Plugins
OnBeforeChunkFormSave
Event: OnBeforeChunkFormSave
Fires after a form is submitted but before a Chunk is saved in the manager.
Event Parameters
Name Description
See Also
System Events
Plugins
OnBeforeDocFormDelete
Event: OnBeforeDocFormDelete
Event Parameters
Name Description
children An array of IDs of children of this resource which will also be deleted.
See Also
System Events
Plugins
OnBeforeDocFormSave
Event: OnBeforeDocFormSave
Fires before a Resource is saved in the manager via the editing form.
Event Parameters
Name Description
See Also
System Events
Plugins
OnBeforeManagerLogout
Event: OnBeforeManagerLogout
Event Parameters
Name Description
See Also
System Events
Plugins
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.
Event Parameters
See Also
System Events
Plugins
OnBeforeWebLogout
Event: OnBeforeWebLogout
Event Parameters
Name Description
System Events
Plugins
OnCacheUpdate
Event: OnCacheUpdate
Event Parameters
Name Description
See Also
System Events
Plugins
OnChunkFormDelete
Event: OnChunkFormDelete
Event Parameters
Name Description
See Also
System Events
Plugins
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.
Event Parameters
Name Description
See Also
System Events
Plugins
OnChunkFormRender
Event: OnChunkFormRender
Fires during the rendering of a form. Useful for rendering HTML straight into the Chunk form.
Event Parameters
Name Description
See Also
System Events
Plugins
OnChunkFormSave
Event: OnChunkFormSave
Event Parameters
Name Description
See Also
System Events
Plugins
OnDocFormDelete
Event: OnDocFormDelete
Event Parameters
Name Description
See Also
System Events
Plugins
OnDocFormPrerender
Event: OnDocFormPrerender
Event Parameters
Name Description
resource A reference to the modResource object. Will be null for new Resources.
See Also
System Events
Plugins
OnDocFormRender
Event: OnDocFormRender
Fires after a Resource editing form is loaded in the manager. Useful for inserting HTML into forms.
Event Parameters
Name Description
resource A reference to the modResource object. Will be null for new Resources.
System Events
Plugins
OnDocFormSave
Event: OnDocFormSave
Fires after a Resource is saved in the manager via the editing form.
Event Parameters
Name Description
See Also
System Events
Plugins
OnDocPublished
Event: OnDocPublished
Event Parameters
Name Description
See Also
System Events
Plugins
OnDocUnPublished
Event: OnDocUnPublished
Event Parameters
Name Description
See Also
System Events
Plugins
OnLoadWebPageCache
Event: OnLoadWebPageCache
Fires after a Resource is loaded from the cache. If the Resource is not cached, this event will not fire.
Event Parameters
None.
See Also
System Events
Plugins
OnManagerLogin
Event: OnManagerLogin
Event Parameters
Name Description
See Also
System Events
Plugins
OnManagerLogout
Event: OnManagerLogout
Fires after a user is logged out of the manager and their context session is removed.
Service: 2 - Manager Access Service Events
Group: None
Event Parameters
Name Description
See Also
System Events
Plugins
OnSiteRefresh
Event: OnSiteRefresh
Event Parameters
Name Description
See Also
System Events
Plugins
OnUserChangePassword
Event: OnUserChangePassword
Event Parameters
Name Description
See Also
System Events
Plugins
OnWebLogin
Event: OnWebLogin
Event Parameters
Name Description
See Also
System Events
Plugins
OnWebLogout
Event: OnWebLogout
Fires right after the user logs out of a context and their context session is removed.
Event Parameters
Name Description
See Also
System Events
Plugins
OnWebPagePrerender
Event: OnWebPagePrerender
Content Type headers have not yet been sent, nor has the output been flushed.
Event Parameters
None.
See Also
System Events
Plugins
OnBeforeEmptyTrash
Event: OnBeforeEmptyTrash
Event Parameters
Name Description
See Also
System Events
Plugins
OnBeforeManagerLogin
Event: OnBeforeManagerLogin
Fires before the login process is started for a user when logging in to the manager context.
Event Parameters
Name Description
See Also
System Events
Plugins
OnBeforeManagerPageInit
Event: OnBeforeManagerPageInit
Loaded right before a manager controller is run.
Event Parameters
Name Description
See Also
System Events
Plugins
OnBeforePluginFormDelete
Event: OnBeforePluginFormDelete
Event Parameters
Name Description
See Also
System Events
Plugins
OnBeforePluginFormSave
Event: OnBeforePluginFormSave
Fires after a form is submitted but before a Plugin is saved in the manager.
Event Parameters
Name Description
See Also
System Events
Plugins
OnBeforeSnipFormDelete
Event: OnBeforeSnipFormDelete
Event Parameters
Name Description
See Also
System Events
Plugins
OnBeforeSnipFormSave
Event: OnBeforeSnipFormSave
Fires after a form is submitted but before a Snippet is saved in the manager.
Event Parameters
Name Description
See Also
System Events
Plugins
OnBeforeTempFormDelete
Event: OnBeforeTempFormDelete
Event Parameters
Name Description
System Events
Plugins
OnBeforeTempFormSave
Event: OnBeforeTempFormSave
Fires after a form is submitted but before a Template is saved in the manager.
Event Parameters
Name Description
See Also
System Events
Plugins
OnBeforeTVFormDelete
Event: OnBeforeTVFormDelete
Event Parameters
Name Description
See Also
System Events
Plugins
OnBeforeTVFormSave
Event: OnBeforeTVFormSave
Fires after a form is submitted but before a Chunk is saved in the manager.
Event Parameters
Name Description
See Also
System Events
Plugins
OnBeforeUserActivate
Event: OnBeforeUserActivate
Never fired in MODx core; can be used by 3rd Party Components (such as Login) when a User is being activated.
Event Parameters
Depends on implementation.
See Also
System Events
Plugins
OnBeforeUserFormDelete
Event: OnBeforeUserFormDelete
Event Parameters
Name Description
See Also
System Events
Plugins
OnBeforeUserFormSave
Event: OnBeforeUserFormSave
Fires after a form is submitted but before a User is saved in the manager.
Event Parameters
Name Description
See Also
System Events
Plugins
OnBeforeWebLogin
Event: OnBeforeWebLogin
Fires before the login process is started for a user when logging in via a non-manager context.
Event Parameters
Name Description
See Also
System Events
Plugins
OnCategoryBeforeRemove
Event: OnCategoryBeforeRemove
Event Parameters
Name Description
See Also
System Events
Plugins
OnCategoryBeforeSave
Event: OnCategoryBeforeSave
Event Parameters
Name Description
See Also
System Events
Plugins
OnCategoryRemove
Event: OnCategoryRemove
Event Parameters
Name Description
See Also
System Events
Plugins
OnCategorySave
Event: OnCategorySave
Event Parameters
Name Description
See Also
System Events
Plugins
OnChunkBeforeRemove
Event: OnChunkBeforeRemove
Event Parameters
Name Description
See Also
System Events
Plugins
OnChunkBeforeSave
Event: OnChunkBeforeSave
Event Parameters
Name Description
See Also
System Events
Plugins
OnChunkRemove
Event: OnChunkRemove
Event Parameters
Name Description
See Also
System Events
Plugins
OnChunkSave
Event: OnChunkSave
Event Parameters
Name Description
See Also
System Events
Plugins
OnContextBeforeRemove
Event: OnContextBeforeRemove
Event Parameters
Name Description
See Also
System Events
Plugins
OnContextBeforeSave
Event: OnContextBeforeSave
Event Parameters
Name Description
mode Either 'new' (modSystemEvent::MODE_NEW) or 'upd' (modSystemEvent::MODE_UPD) depending on whether is a new object or an
existing one.
See Also
System Events
Plugins
OnContextFormPrerender
Event: OnContextFormPrerender
Fires prior to the context editing form loading. Useful for running custom javascript.
Event Parameters
Name Description
See Also
System Events
Plugins
OnContextFormRender
Event: OnContextFormRender
Event Parameters
Name Description
See Also
System Events
Plugins
OnContextRemove
Event: OnContextRemove
Event Parameters
Name Description
See Also
System Events
Plugins
OnContextSave
Event: OnContextSave
Event Parameters
Name Description
mode Either 'new' (modSystemEvent::MODE_NEW) or 'upd' (modSystemEvent::MODE_UPD) depending on whether is a new object or an
existing one.
See Also
System Events
Plugins
OnEmptyTrash
Event: OnEmptyTrash
Event Parameters
Name Description
See Also
System Events
Plugins
OnFileManagerUpload
Event: OnFileManagerUpload
Event Parameters
Name Description
directory A reference to the modDirectory object that the files are being uploaded to.
See Also
System Events
Plugins
OnHandleRequest
Event: OnHandleRequest
Event Parameters
None.
See Also
System Events
Plugins
OnInitCulture
Event: OnInitCulture
Event Parameters
None.
See Also
System Events
Plugins
OnLoadWebDocument
Event: OnLoadWebDocument
Fires directly before the Response is sent and after a Resource is loaded.
Event Parameters
See Also
System Events
Plugins
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 external
authentication 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 bypass
the default authentication via password.
Event Parameters
Name Description
See Also
System Events
Plugins
OnManagerLoginFormPrerender
Event: OnManagerLoginFormPrerender
Fires before the login form is rendered for the MODx manager.
Event Parameters
None.
See Also
System Events
Plugins
OnManagerLoginFormRender
Event: OnManagerLoginFormRender
Fires when the login form is rendered for the MODx manager. Useful for inserting custom HTML into the login form.
Event Parameters
None.
See Also
System Events
Plugins
OnManagerPageInit
Event: OnManagerPageInit
Fired in the manager request handler, before the manager page response is loaded.
Event Parameters
Name Description
See Also
System Events
Plugins
OnPageNotFound
Event: OnPageNotFound
Fires immediately before the user is forwarded to the error page if attempting to view a non-existent Resource.
Event Parameters
Name Description
response_code The response code to send. Defaults to "HTTP/1.1 404 Not Found"
error_header The header being sent: Defaults to "HTTP/1.1 404 Not Found"
See Also
System Events
Plugins
OnPageUnauthorized
Event: OnPageUnauthorized
Fires immediately before the user is forwarded to the unauthorized page if attempting to view a non-accessible Resource.
Event Parameters
Name Description
See Also
System Events
Plugins
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 the
Resource, use $modx->documentOutput.
Event Parameters
None.
See Also
System Events
Plugins
OnPluginBeforeRemove
Event: OnPluginBeforeRemove
Event Parameters
Name Description
See Also
System Events
Plugins
OnPluginBeforeSave
Event: OnPluginBeforeSave
Event Parameters
Name Description
System Events
Plugins
OnPluginEventRemove
Event: OnPluginEventRemove
Event Parameters
Name Description
See Also
System Events
Plugins
OnPluginFormDelete
Event: OnPluginFormDelete
Event Parameters
Name Description
See Also
System Events
Plugins
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.
Event Parameters
Name Description
mode Either 'new' or 'upd', depending on the circumstance.
See Also
System Events
Plugins
OnPluginFormRender
Event: OnPluginFormRender
Fires during the rendering of a form. Useful for rendering HTML straight into the Plugin form.
Event Parameters
Name Description
See Also
System Events
Plugins
OnPluginFormSave
Event: OnPluginFormSave
Event Parameters
Name Description
See Also
System Events
Plugins
OnPluginRemove
Event: OnPluginRemove
Happens right after a plugin is removed.
Event Parameters
Name Description
See Also
System Events
Plugins
OnPluginSave
Event: OnPluginSave
Event Parameters
Name Description
See Also
System Events
Plugins
OnPropertySetBeforeRemove
Event: OnPropertySetBeforeRemove
Event Parameters
Name Description
See Also
System Events
Plugins
OnPropertySetBeforeSave
Event: OnPropertySetBeforeSave
Name Description
See Also
System Events
Plugins
OnPropertySetRemove
Event: OnPropertySetRemove
Event Parameters
Name Description
See Also
System Events
Plugins
OnPropertySetSave
Event: OnPropertySetSave
Event Parameters
Name Description
See Also
System Events
Plugins
OnResourceGroupBeforeRemove
Event: OnResourceGroupBeforeRemove
Event Parameters
Name Description
group A reference to the modResourceGroup object.
See Also
System Events
Plugins
OnResourceGroupBeforeSave
Event: OnResourceGroupBeforeSave
Event Parameters
Name Description
See Also
System Events
Plugins
OnResourceGroupRemove
Event: OnResourceGroupRemove
Event Parameters
Name Description
See Also
System Events
Plugins
OnResourceGroupSave
Event: OnResourceGroupSave
Event Parameters
Name Description
System Events
Plugins
OnRichTextBrowserInit
Event: OnRichTextBrowserInit
Simply pass in a Javascript function that will handle the return url for the file selected from MODx.Browser to your RTE.
Event Parameters
None.
See Also
System Events
Plugins
OnRichTextEditorInit
Event: OnRichTextEditorInit
Event Parameters
Name Description
forfrontend If passed, this will indicate to the plugin that this is to be loaded in a front-end context, not the manager.
See Also
System Events
Plugins
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 Events
Group: Richtext Editor
Event Parameters
None.
See Also
System Events
Plugins
OnSiteSettingsRender
Event: OnSiteSettingsRender
Fires before the system settings page is rendered. Useful for adding custom HTML into the System Settings page.
Event Parameters
None.
See Also
System Events
Plugins
OnUserActivate
Event: OnUserActivate
Never fired in MODx core; can be used by 3rd Party Components (such as Login) when a User is being activated.
Event Parameters
Depends on implementation.
See Also
System Events
Plugins
OnUserBeforeRemove
Event: OnUserBeforeRemove
Event Parameters
Name Description
System Events
Plugins
OnUserBeforeSave
Event: OnUserBeforeSave
Event Parameters
Name Description
mode Either 'new' (modSystemEvent::MODE_NEW) or 'upd' (modSystemEvent::MODE_UPD) depending on whether is a new object or an
existing one.
See Also
System Events
Plugins
OnUserFormDelete
Event: OnUserFormDelete
Event Parameters
Name Description
See Also
System Events
Plugins
OnUserFormSave
Event: OnUserFormSave
Event Parameters
Name Description
See Also
System Events
Plugins
OnUserNotFound
Event: OnUserNotFound
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) a
modUser object.
Event Parameters
Name Description
See Also
System Events
Plugins
OnUserRemove
Event: OnUserRemove
Event Parameters
Name Description
See Also
System Events
Plugins
OnUserSave
Event: OnUserSave
Event Parameters
Name Description
mode Either 'new' (modSystemEvent::MODE_NEW) or 'upd' (modSystemEvent::MODE_UPD) depending on whether is a new object or an
existing one.
See Also
System Events
Plugins
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 external
authentication 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 bypass
the default authentication via password.
Event Parameters
Name Description
See Also
System Events
Plugins
OnWebPageComplete
Event: OnWebPageComplete
Fires after the Resource is loaded, response is sent, cache is stored (if applicable) and execution is completed.
Event Parameters
None.
See Also
System Events
Plugins
OnWebPageInit
Event: OnWebPageInit
Fires during the initialization process of a Resource, after modRequest::beforeRender is called, but before config placeholders are set and any
404 or unauthorized page checking are done, or a response is sent.
Event Parameters
None.
See Also
System Events
Plugins
xPDO
What is xPDO?
OpenExpedio is the name for open eXtensions to PDO. It's a light-weight ORB (object-relational bridge) (also see ORM). It implements the very
simple, 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 newly
adopted standard for database persistence in PHP 5.1+.
See Also
Advanced Development
Namespaces
Namespaces are organizational elements for Components. They relate lexicon strings and packages to one another, as well as provide a basic
way 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 custom
language strings for those 3rd Party Components.
$namespace = $modx->getObject('modNamespace','mynamespace');
echo $namespace->get('name').' has a path of: '.$namespace->get('path');
See Also
Package Management
Internationalization
Settings
Caching
There are many different caching features in Revolution. Different aspects of the caching can be extended with user-defined classes, so it is
possible to implement different kinds of features in custom component cache mechanisms (i.e. where you cache your own custom data), just not
throughout the entire core.
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 to
something 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.
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 plugin
event map for the context, and the plugin elements that are registered in the event map.
Within the web context (or another custom front-end context) there will be a subdirectory for resources/ containing files like 1.cache.php where the
1 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 and
plugins). 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.
If you enable this, xPDO can cache query result sets representing any objects and collections in the objects subdirectory. This can be enabled in
environments where database access is more expensive than PHP include time. This is separate from all of the other caching mechanisms which
are 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 about
any cache system and includes a memcached implementation. There is no tagging feature here as in the programmatic features of the
Zend_Cache library but there are definitely some ways to clear specific parts of any custom caching, as well as various parts of the
MODx-specific caching mechanisms described above.
Using modCacheManager
Using modCacheManager::clearCache
<?php
// clear all the usual stuff by default (all files with the extension .cache.php
// 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' => null, 'extensions' => array('.php', '.log'));
$modx->cacheManager->clearCache($paths, $options);
See Also
Caching in xPDO
xPDOCacheManager
What is a CMP?
Namespaces
Creating a Namespace
Using modAction and modMenu
Creating the modAction
Creating the modMenu
Returning the Page
Smarty
Plain-Old HTML
Scripts and CSS
Brief Overview of MODExt
Custom Connectors
Conclusion
See 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 the
MODx Revolution manager. It can be used to create custom administration interfaces for 3rd Party Components (Extras/3PCs), or it can simply
add functionality to the Revolution core.
In MODx Evolution, CMP's were handled by Modules, but Revolution does not use 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 to
load the Custom Manager Page. These paths are called 'Namespaces', and in case you're not familiar with MVC nomenclature, the generic term
for a script or function which generates a page is a 'controller'. The Manager will search for the controller file in the path defined by the
Namespace. 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 is
often used as part of a URL, MODx recommends to make the Namespace name lowercase; this helps avoid inconsistent behavior that may occur
with some webservers that may handle capitalization differently.
In the path, you can also use placeholders for MODx paths:
{core_path} - Resolves to the MODX_CORE_PATH. This is set in the /config.core.php at the root of the site, often it is
/home/username/modx_location/core/
{base_path} - Resolves to the MODX_BASE_PATH, often /home/username/modx_location/
Actions (modAction) and Menus (modMenu) work together to allow CMP developers to create Manager pages that directly hook into the default
Manager, without hacking the MODx core. modMenu objects are the actual menu items you see on the navigation bar in the Manager. modAction
objects tell the menu items what to do, usually in the form of sending a request to a controller file.
To create a modAction, go to System -> Actions. Right-click your Namespace from the 'Actions' tree and select "Create Action Here. The following
window will show something similar to the following:
There are several noteworthy fields here:
1. Controller: This is where you'll put the name of the controller file to look for. Make sure to leave off the .php extension. For example, if in
your Namespace path, there is a index.php that you'll want to use to handle your CMPs, set this field to 'index'.
2. Namespace: This is the name of the Namespace the new action belongs to. Make sure this is set to the Namespace for your Component.
3. Parent Controller: This is currently only for organizational purposes, and poses no programmatic change if set to something else.
4. Load Headers: If this is checked, MODx will load the Manager header and footer files. This is recommended unless you want to have a
completely separate view for your CMP.
5. Language Topics: A comma-separated list of Lexicon Topics to load prior to the page load. They are in the standard Lexicon loading
format.
6. 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.
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:
There are also several noteworthy fields here:
1. 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.
2. Description: The Lexicon entry key* that will be used for the menu item's description.
3. Action: The action that connects your menu to the appropriate connector. Select the modAction you just created.
4. Icon: This field is currently not in use in MODx Revolution 2.0.0.
5. Parameters: Allows you to specify other GET parameters to be added to the Menu's href URL should you want to, e.g. &x=1&y=2. Steer
clear of the a variable and other Reserved Parameters. Note that each variable must be prepended with an ampersand (&); this is
because 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=2
6. Handler: Allows you add a Javascript handler to execute instead of the default page loading action. If this is specified, the Menu will
default to that handler and ignore the HREF attribute entirely. Use this if you just want to execute a JavaScript action instead of load a
page.
7. Permissions: If you'd like to restrict view access to this menu item, you can do so here. Just specify the Permission name you'd want
users to have to have to see this menu item.
The Lexicon Entry you specify for the Text or Description must be in the "default" Topic for your Namespace.
Creating a CMP is very similar to a Snippet; you'll just return the page content using the PHP 'return' statement. MODx recommends you do not
use '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 Smarty templates. The MODx Manager is powered by Smarty, a templating engine which
focuses on making it easy for developers to create their own custom templates. To use a template in the Manager, you simply use the following to
output the content of your list.tpl onto the page:
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:
<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():
return $o;
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:
1. $src [string] - The path to your JavaScript file, or the content of the script to output.
2. $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 your
CSS file.
However, you don't have to use ExtJS in your Custom Manager Pages - you can use plain HTML, or another JS framework, if you like. If you do
decide to use another JS framework, MODx recommends that you not set "Load Headers" to true on the modAction, since this will load the ExtJS
scripts. You'll need to create your own header file and output that with your normal output.
More info on MODExt, the ExtJS integration for MODx Revolution, can be found 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 accessed
directly. Instead, by using a Connector as a proxy to connect to a Processor, additional authentication and security checks can be performed
before 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 must
be 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 a
Connector 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__ ) ) ) ) );
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' => ''
) );
?>
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 appropriate
Processor 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 seamlessly
into the core MODx installation, and allow for entire custom applications to be built with MODx technologies.
See Also
MODx Revolution introduces an entirely new program structure for its core. The manager is also built on what are called controllers and templates
, which use AJAX processing to send data to connectors that access processors.
The controllers are simply PHP files that load the correct Smarty template to display, and fetch any pre-render data for the template. Revolution
abstracts these controllers into the DB as modAction's, allowing 3rd party developers to easily create custom manager pages that 'hook' into the
current 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. They
have 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 smart
search.
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 the
manager page, leave this unchecked.
Language Topics - Language Topics are simply separations of language areas that allow for quicker i18n access. They can be found in
the 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 in
core/components/tinymce/lexicon/en/buttons.inc.php. They can simply either A) use a build script to install the lexicon via a transport package, or
B) have users import the lexicon topics using the Import Lexicon utility in Lexicon Management.
$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 3rd
party 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
Goal
Explanation and Mental Preparation
What we'll need:
Create the Namespace
Make the Controller File
Create the Action
Create the Menu Object
Make your CMP Translatable (Optional)
Create a Lexicon Directory
Identify your Lexicon key
Create the Topic File
Create 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 uploaded
to the webserver. Technically, such a page is called a Custom Manager Page, or CMP; please refer to the page on Custom Manager Pages for a
more detailed description.
In MODx Evolution (versions 1.x and earlier), this process was handled by "Modules", but those have been deprecated in Revolution.
"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?"
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 seemingly
simple task work. Allowing for internationalization and scalability requires that this process include several layers of abstraction that are not
immediately obvious. It goes far beyond what's possible with the simple anchor tag solution above. But rest assured, the extra steps will ensure
that the solution will be usable in a far greater number of scenarios.
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 many
of you will find yourself at some point getting into more complicated use-cases at which point you'll realize "AHA!!! THAT'S why they did it this
way!" 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 way
it is. Onward.
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 that
creating the Namespace inside the MODx manager does not actually create the directory; likewise, removing a Namespace from the manager will
not 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.
1. System->Namespaces
a. Click Create New
b. Name: mycmp (all lowercase, one word)
c. Path: {core_path}components/mycmp/ (note the use of the system "core_path" placeholder, and remember to include the
trailing slash)
2. Using your FTP client, create a directory inside core/components named mycmp.
Watch out for typos! Make sure the Namespace path matches the directory name!
Make the Controller File
For our first manager page, we're going to keep it simple. Create a file named index.php which contains the following:
<?php
return 'This is my first Custom Manager Page';
?>
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 print, or echo, or raw HTML in our PHP. If you use any of these, you'll find that the text floats to the
top of the page; remember that a Custom Manager Page is really acting as a function, so it should return a value.
The Action object identifies the location of your index.php file within the namespace.
1. System->Actions
2. Right-click mycmp from the list of namespaces and select "Create Action Here".
3. Controller: index (this should match the name of your PHP file WITHOUT the .php extension)
4. Check Load Headers
5. Optionally, enter in one or more Language topics (e.g. "mycmp:default" to load the default topic for the mycmp Namespace)
1. System->Actions (in the same window where you created the Action)
2. Right-Click "Components" and choose "Place Action Here"
a. Lexicon Key: My CMP
b. Description: My first custom manager page
c. Action: mynamespace - index
d. Save
3. 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 your
browser; 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 a variable or any other
Reserved 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!
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
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 other
languages.
Go to your Namespace path (usually in your Extra's core/components/mycmp/ directory) and place a "lexicon/" directory in there. From there, add
an 'en' directory as well ('en' for 'English' -- or use your language code of choice). You should have something like:
core/components/mycmp/lexicon/en/
1. System->Actions
2. Find your Menu action in the menu on the right (under Components)
3. Update Menu
4. Note the 'Lexicon Key' field. Set it to 'mycmp'.
5. 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/Menu
to 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 first and then
point your Action and Menu objects to reference them second, but here we're assuming that you are adding Lexicon entries after creating the
Action and Menu objects.
Create a file name default.inc.php in your new 'en' lexicon directory (i.e. core/components/mycmp/lexicon/en/default.inc.php), and place your
entries in them, in this format:
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.
1. Did you make sure you created a directory on your webserver with the EXACT path as defined by your Namespace?
2. Are you sure your controller file is using the return statement instead of using print or echo?
3. Your menu items aren't being translated? Be sure to clear your cache! Site->Clear Cache
4. Translations aren't appearing in your CMP? Make sure you specified the "lexicon" in the Action object (ie, "mycmp:default")
Reserved Parameters
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:
MODExt
What is MODExt?
Commonly-Used Components
More MODExt Components
Extending a MODExt Class
See Also
What is MODExt?
MODExt is an extension of the ExtJS 3.0 JavaScript Framework that provides extra, customized-to-MODx functionality. It drives MODx
Revolution's manager interface, and it is also available to developers wanting to use it in their CMP development. A developer simply needs to
use 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
Of course, there are more MODExt components at your disposal. For a full list (and source code to examine), browse to the
manager/assets/modext/widgets/core/ directory of your MODx installation.
Extending a MODExt component is actually quite simple. Let's extend the MODx.grid.Grid class to create our own custom grid, complete with a
custom toolbar.
First, create a new JavaScript file and place the following code:
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 additional
functionality -- yet!
Our basic configuration sets the grid up to work with a "list" connector, using the "getlist" action parameter. It also sets up paging, sorting, and
enables "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
MODx.combo.ComboBox
Extends: Ext.form.ComboBox
Key Features: Remote and local data stores; grid renderer.
The MODExt ComboBox class contains all of the functionality of a regular Ext ComboBox. It may be populated remotely by an array of JSON
objects 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, and
automatically takes care of displaying the correct displayValue in the grid cell:
MODx.FormPanel
MODx.FormPanel
Extends: Ext.FormPanel
Key Features: Drag-and-drop functionality; changed ("dirty") field checking functionality; connector functionality.
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
Key Features: Connector functionality; easily integrate toolbar items and MODx.Window; built-in context menu functionality.
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 each
row can easily be achieved by including a "menu" key for each data row in your processor:
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 the
myHandler function registered to your Grid object.
See Also
MODx.grid.LocalGrid
MODExt
MODx.grid.LocalGrid
MODx.grid.LocalGrid
Extends: Ext.grid.EditorGridPanel
Key Features: Similar to MODx.grid.Grid.
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 be
loaded through a local store.
See Also
MODx.grid.Grid
MODExt
MODx.Msg
MODx.Msg
Extends: Ext.Component
Key Features: AJAX connector features.
The MODx.Msg class provides the functionality of the Ext.MessageBox class, with the added benefit of using an AJAX callback function (for
confirmation 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
Key Features: Remotely-loaded toolbars; drag-and-drop to form fields functionality; connector functionality for removing and dragging/sorting.
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
Key Features: Drag-and-drop functionality; connector functionality for saving.
MODExt Windows are a convenient way to display record data from a Grid or AJAX request for editing. Windows automatically include a
FormPanel which you can add form fields (and other components) to. Submitting/saving a Window actually submits the FormPanel, and initiates
an AJAX request to your connector.
Internationalization
An Overview
Lexicon Entries
Loading and Using Lexicons
Lexicons Via Tag
Lexicons in Code
modLexicon::load()
modX::lexicon()
Lexicons with Placeholders
Lexicons for Settings
Conclusion
See Also
An Overview
Internationalization, or i18n, is the process of extrapolating text strings on a document to separate languages, so that the document may be
viewed 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:
A Lexicon Topic is a collection of Lexicon Entries. A Lexicon Entry is one single language string, with a key and a value. Revolution separates
Entries 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 accidentally
overwriting 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 important
fields 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.
Lexicons must first be loaded if they are to be used in the front-end; however, this is a trivial process.
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 the
MODx 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 a
System 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'd
simply 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 default
Namespace 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 (which
defaults 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');
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, that
says, "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]]!
[[!%welcome_message? &name=`John`]]
Or in PHP:
Note our ! prefix for the Tag; this makes sure the Tag isn't cached, since our string might be changing before the page cache
does.
So say you're creating System Settings for your 3rd Party Component (3PC). The syntax for auto-loading them into the Revolution Settings grid is
simple. Let's say we have a Namespace for our Component called 'gallery', and a setting called 'gallery.display_thumbs'
Recommended Format
The recommended format for 3PC developers is to use a prefix which identifies the parent component:
This helps to prevent name collisions; keep in mind that the $_lang array may have thousands of entries, so you want to make
sure 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:
Conclusion
Lexicons provide MODx Revolution users with a plethora of avenues and options to do their i18n work. Lexicons are composed of multiple Entries
for each Language, and are grouped into Topics. They can be called by PHP method calls, or by MODx Tags.
See Also
modX.lexicon
MODx Services
What is a Service?
A service is any object that is loaded via $modx->getService. It can be a custom class provided by the user, or by MODx itself.
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!');
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 an
implementation 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 From
address 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 new email template!');
$modx->mail->address('to','[email protected]');
$modx->mail->address('reply-to','[email protected]');
$modx->mail->setHTML(true);
if (!$modx->mail->send()) {
$modx->log(modX::LOG_LEVEL_ERROR,'An error occurred while trying to send the email: '.$err);
}
$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 optional (just like
PHPMailer), 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).
Simple - just extend modMail with that class, then load your class via getService. You'll get all the modMail functionality, but you will have to
provide the wrapper class (like modPHPMailer) to do so.
See Also
MODx Services
modX.getService
Package Management
MODx Revolution introduces what are called Transport Packages, which are compiled zips of almost anything - from snippets, components,
manager templates, to the core itself.
Revolution also has Providers, which are download locations that allow for downloading packages straight from within the MODx manager itself.
Downloading Packages
Installing Packages
Updating Packages
Uninstalling Packages
See 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 by
clicking '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 http://modxcms.com/extras/. The package zips
are loaded simply by uploading them to your core/packages/ directory, and then running the Package Management section of the manager. From
there, click on "Add New Package", and select the "Search Locally for Packages" option. MODx will then scan the core package directory, and
add any packages you have.
Downloading Packages requires you to either have cURL or sockets installed on your web server. If you do not have these
installed, the list of packages will show blank.
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 package
installation.
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 README
file 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:
Updating Packages
You can easily update any package that has been downloaded from a provider. Simply click the 'Check for Updates' context menu item (after
right-clicking on the package), and MODx will load a window showing any newer versions. Should your package be already up-to-date, a
message 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 that
was installed.
Uninstalling Packages
You can click on any package to either remove or uninstall a package. Removing a package removes the zip file entirely from your core/packages
directory. Uninstall simply uninstalls it.
Each is self-explanatory.
See Also
Transport Packages
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 to
transport 3rd-Party Components in a simple, easily-manageable format. In other words, Transport Packages can transport nearly anything - from
database 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 would
then 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 Revolution
instance - regardless of the server configuration.
MODx Revolution automatically "unpacks", or unzips, your transport packages for you. Once done, a subdirectory in your core/packages directory
will appear with the name of the zip file (minus ".transport.zip"). This directory will contain:
A manifest.php file
Subdirectories 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 be
restored. 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 open
the 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 and
make 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.
Transport Vehicles are the parts of a Transport Package. A package can contain as many Vehicles as it likes. Vehicles also come in different
types; the currently available ones are:
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 a
MODx 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.
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 in
the 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 and
xPDOObjectVehicle can have different keys. Let's go over the common ones:
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, as
auto-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 to
package in "related" objects to achieve the desired end result. A great example is if the packager wants to put all of his Snippets in a
Category. 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:
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 files
for the vehicle.
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 open
them 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 before the Vehicle is installed, upgraded or uninstalled. If they return false, the Vehicle is not installed, and is skipped.
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 after the Vehicle is installed, upgraded or uninstalled. Each will execute in turn regardless of any other resolver results.
Resolvers are useful for 'cleaning up' after a Vehicle is installed, or setting custom configuration options (such as ones setup in Setup Options
during install).
Usage
Transport Packages can be managed in the Package Management section of the Revolution manager. They can be added to the Revolution
instance by either:
1. Uploading the file manually to core/packages/, and then clicking "Add New Package" and selecting the "Search Locally for Packages"
option
2. Downloading the package from a Transport Provider. This allows updates to be remotely downloaded for a package as well.
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 License
Agreement should the package come with one, and prompting to read the README should the package contain one. Then it will present a form
with 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 Transport Provider, then the user can
check for updates for the package.
Related Pages
Package Management
Providers
Tutorial: Creating a 3rd Party Component Build Script
Providers
Transport Providers in MODx are remote sources that one can download Transport Packages from. Simply by specifying a service URL, you can
easily 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 the
modxcms.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 JSON
files, 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 a
Provider" 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 Structure
Starting the Build Script
Packaging in Objects
Validators and Resolvers
Lexicons
Package Attributes: License, Readme and Setup Options
Related 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 recommends
to 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 a
modAction, 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; but
it's definitely recommended, especially with the assets/components/quip/ and core/components/quip/ structures, since that makes creating the
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
*
* @package quip
* @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. Note
how the 'source_core' and 'source_assets' directories do not post-fix a foreslash onto their paths. This is required.
$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 build
dir.
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, as
well as where to put the package when finished. Our file will look somewhat like this:
<?php
/**
* Define the MODX path constants necessary for core installation
*
* @package quip
* @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 make
our errors and info messages nice and formatted - unless we're doing this from the cmd line, where we'll want just standard echo messages.
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, and
create a package.
$modx->loadClass('transport.modPackageBuilder','',false, true);
$builder = new modPackageBuilder($modx);
$builder->createPackage('quip','0.1','alpha7');
$builder->registerNamespace('quip',false,true,'{core_path}components/quip/');
Next, we'll register a Namespace to this package. Not all packages need Namespaces; but all 3rd Party Components do. Basically, a Namespace
is 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 uninstall
our package; we'd want it to remove the objects we'd install.
Plus, should we want to add any Lexicon Entries to this package (which we will), MODx does so by relating it to it's Namespace. Our package
builder will assign our Lexicon Entries to the Namespace, so we can easily manage just our Lexicon Entries; not any others.
Packaging in Objects
Objects are packaged as Vehicles in MODx Revolution; basically think of a vehicle as a sort of storage system that transports the data and/or files
into the zip package. Packages can contain many vehicles; vehicles can contain many objectsor files - however, vehicles that contain an object
must 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, with
some 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 (string/array) - Here you'd place the unique key that identifies the object you're creating. This will tell
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 (boolean) - Either true or false, this tells MODx whether or not to update the object if it is found in
the DB upon install (or update). Sometimes, if the object is already there, you may not want to update it - the update might erase the
user's current settings for that object.
xPDOTransport::PRESERVE_KEYS (boolean) - Either true or false, this tells MODx whether or not to rewrite the primary keys when the
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 if
you're wanting those to stay the same number, you'd set this to true. Note: If the object already exists, this feature only works if
xPDOTransport::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 bit
more complex scenario:
$action= $modx->newObject('modAction');
$action->fromArray(array(
'id' => 1,
'namespace' => 'quip',
'parent' => '0',
'controller' => 'index',
'haslayout' => '1',
'lang_topics' => 'quip:default,file',
'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'),
),
),
));
xPDOTransport::RELATED_OBJECTS (boolean) - Either true or false, this will tell MODx we want to search for related objects to this
object. This must be set for the next parameter to work.
xPDOTransport::RELATED_OBJECT_ATTRIBUTES (array) - This defines the types and details of the related objects we want to grab.
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" of
the 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') to
include 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 and
addMany (or addOne) on that action. You can go however deep in nesting that you want.
$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);
$modx->loadClass('transport.modPackageBuilder','',false, true);
$builder = new modPackageBuilder($modx);
$builder->createPackage('quip','0.1','alpha5');
$builder->registerNamespace('quip',false,true,'{core_path}components/quip/');
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 build
script clean, and isolate our actions/menus to a separate file for easy management.
$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 category
called '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:
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 place
it here. This makes it easy to run the build in future iterations; no need to continually update this call - just update that file.
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
*
* @package quip
* @subpackage build
*/
$properties = array(
array(
'name' => 'closed',
'desc' => 'If set to true, the thread will not accept new comments.',
'type' => 'combo-boolean',
'options' => '',
'value' => false,
),
array(
'name' => 'dateFormat',
'desc' => 'The format of the dates displayed for a comment.',
'type' => 'textfield',
'options' => '',
'value' => '%b %d, %Y at %I:%M %p',
),
/* ...removed others for brevity... */
);
return $properties;
/* add chunks */
$chunks = include $sources['data'].'transport.chunks.php';
if (is_array($chunks)) {
$category->addMany($chunks);
} else { $modx->log(modX::LOG_LEVEL_FATAL,'Adding chunks failed.'); }
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 case
we made a typo or something. Now, let's package all that into a vehicle:
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 users
install our package, too - so it'll look nice and sharp!
Validators and resolvers are basically scripts that run during the install process. Validators are run pre-install; meaning that they are run before the
main 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 copies
over 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' => "return MODX_CORE_PATH . 'components/';",
));
$vehicle->resolve('file',array(
'source' => $sources['source_assets'],
'target' => "return MODX_ASSETS_PATH . 'components/';",
));
$vehicle->resolve('php',array(
'source' => $sources['resolvers'] . 'setupoptions.resolver.php',
));
$builder->putVehicle($vehicle);
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 a
PHP 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 as
in 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're
moving 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 files
that 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 the
setup 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 about
our lexicons.
As of MODx Revolution RC-2, MODx will automatically find the lexicons in your lexicon directory, assuming that you put them in this structure in
the following place: '{namespace_path}lexicon/', where the Namespace path is the path you put for your Namespace earlier. You don't have to
build 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 your
Component.
Each package has what are called 'package attributes', which can be passed to any resolver or validator. You could pass pretty much anything
you want into the function modPackageBuilder::setPackageAttributes(), in an array format. There are, however, three special keys that we'll deal
with.
license (string) - This represents your license agreement. Should MODx find this not empty during install, it will prompt the user to agree
to 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 useful
to 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-inputted
options to the resolvers or validators. This means that you can take in user input before install, and process it during install!
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 modx/core/components/quip/docs directory after install, should the user want to view them later.
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 form
wouldn'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.
<?php
/**
* Build the setup options form.
*
* @package quip
* @subpackage build
*/
/* set some default values */
$values = array(
'emailsTo' => '[email protected]',
'emailsFrom' => '[email protected]',
'emailsReplyTo' => '[email protected]',
);
switch ($options[xPDOTransport::PACKAGE_ACTION]) {
case xPDOTransport::ACTION_INSTALL:
case xPDOTransport::ACTION_UPGRADE:
$setting = $modx->getObject('modSystemSetting',array('key' => 'quip.emailsTo'));
if ($setting != null) { $values['emailsTo'] = $setting->get('value'); }
unset($setting);
return $output;
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 not
yet.
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 go
gracefully, 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/pass
information 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.
*
* @package quip
* @subpackage build
*/
$success= false;
switch ($options[xPDOTransport::PACKAGE_ACTION]) {
case xPDOTransport::ACTION_INSTALL:
case xPDOTransport::ACTION_UPGRADE:
/* emailsTo */
$setting = $object->xpdo->getObject('modSystemSetting',array('key' => 'quip.emailsTo'));
if ($setting != null) {
$setting->set('value',$options['emailsTo']);
$setting->save();
} else {
$object->xpdo->log(xPDO::LOG_LEVEL_ERROR,'[Quip] emailsTo setting could not be found, so
the setting could not be changed.');
}
/* emailsFrom */
$setting = $object->xpdo->getObject('modSystemSetting',array('key' => 'quip.emailsFrom'));
if ($setting != null) {
$setting->set('value',$options['emailsFrom']);
$setting->save();
} else {
$object->xpdo->log(xPDO::LOG_LEVEL_ERROR,'[Quip] emailsFrom setting could not be found, so
the setting could not be changed.');
}
/* emailsReplyTo */
$setting = $object->xpdo->getObject('modSystemSetting',array('key' => 'quip.emailsReplyTo'));
if ($setting != 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;
case xPDOTransport::ACTION_UNINSTALL:
$success= true;
break;
}
return $success;
Note that $modx is not available here; you're actually running these scripts from within the transport object. The $modx object is available as a
different 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 the
package:
$builder->pack();
$mtime= microtime();
$mtime= explode(" ", $mtime);
$mtime= $mtime[1] + $mtime[0];
$tend= $mtime;
$totalTime= ($tend - $tstart);
$totalTime= sprintf("%2.4f s", $totalTime);
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/packages
directory.
Related Pages
Package Management
Transport Packages
Transport 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 more
experienced 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 of
social networking, user management systems, and other applications not yet conceptualized. This ability to extend the modUser class is just one
example of the underlying power of MODx Revolution. By following the steps detailed below you will quickly be on your way to developing your
own user "interfaces" or sub-systems.
Purpose
Extending modUser is for those situations when user authentication interaction needs overridden, extended, enhanced, etc. The focus is purely
user 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 modx_users table in the database. It simply means we are going to be
appending our own data to the end of the table by attaching our data sets via relationships and a schema. At no time should an extended
application actually attempt to completely replace the modUser Class. Instead we should be using it as a platform to build upon. The only
indication 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 your extension. If the user (object) has not been extended, do not allow your extension to interact with
them -- 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 not
begin writing "bloat" which is already in modUser. In other words use the Revolution resources for your extended users, but do not create code to
replace modUser.
Lastly, get familiar with modUser, before you begin to code. Some methods are not one-to-one as you might assume, such as attributes, which
can be assigned per context, resource, etc. Typically use the modUser suggestions to access modUser methods.
The first thing we need to accomplish, is to create an extended user schema which extends modUser. Please note that there is no aggregate
relation upwards from your "main" class which is extending modUser. An example follows:
<?xml version="1.0" encoding="UTF-8"?>
<model package="extendeduser" baseClass="xPDOObject" platform="mysql" defaultEngine="MyISAM"
tablePrefix="ext_">
<!-- inherit the modx user and extend it -->
<object class="extUser" table="users" extends="modUser">
<composite alias="Phones" local="id" foreign="user" cardinality="many" owner="local" />
<composite alias="Table2" local="id" foreign="user" cardinality="many" owner="local" />
</object>
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
Using Custom Database Tables in your 3rd Party Components for further information.
To access the extended class, we have to inform modUser that the user in question has been extended. The modx_users table in the database
contains a field specifically for this purpose: class_key. The default value in this field is modUser. As users are added to your site using your
extension 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 should
see a mysql directory) in this same folder. Edit the file to resemble the following:
<?php
/**
* @package extendeduser
* @subpackage user.mysql
*/
class extUser extends modUser {
function __construct(xPDO & $xpdo) {
parent :: __construct($xpdo);
$this->set('class_key','extUser');
}
}
?>
Access the System settings found in the System menu of the manager, and search for: extension_packages.
, extendeduser:{core_path}components/extendeduser/model/
extendeduser:{core_path}components/extendeduser/model/
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
* @package extendedUser
* @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
*
*/
if (!class_exists('Sampleclass')) {
class Sampleclass
{
function __construct(modX & $modx, array $config= array ()) {
function __destruct() {
unset ($this->extPath, $this->userObj, $this->userID, $this->_config);
}
/**
* Returns object of type Phone.
*/
public function getPhoneObj() {
$this->userObj->getOne('Phones');
return $this->userObj->Phones;
}
/**
* Returns object utUser instance of modUser Defaults to current user.
* @param $userID
*/
public function getUserObj($userID) {
return $this->modx->getObject('modUser', $userID);
}
/**
* Establishes the user.
* @param int $userID
*/
public function setUser($userID){
return $this->getUserObj($userID);
}
}
}
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:
<?php
$x = $modx->getService('extendeduser','Sampleclass',$modx->getOption('core_path',null,
MODX_CORE_PATH).'components/extendeduser/',$scriptProperties);
if (!($x instanceof Extendeduser)) {
$modx->log(modX::LOG_LEVEL_ERROR,'[Extendeduser] Could not load Extendeduser class.');
$modx->event->output(true);
}
return;
Noteworthy items
1. Any pre existing user, will still have modUser as the class_key and therefore will not be extended or produce user objects of type extUser
unless you change it
2. Double check the modx.mysql.schema.xml file to make sure you are not using classes or alias it is already using, as yours will supersede
the default moduser prohibiting you access to items such as the user attributes (with alias Profile)
3. The extUser will not have a table created in the database, but the attached relations will
4. The extended class table(s) must be in the same database as the regular modx_users table
5. Symptoms of step 3 (extension_packages path) not being correct:
a. Any user with the class_key of extUser will return an error upon login: "User cannot be found...". If this is the admin, access your
database directly, return the class_key to modUser, login correctly and then alter the path to a correct representation of the path.
b. The snippets attached to the class will intermittently work or fail altogether
6. To get counts from your data (i.e. how many phone numbers does this person have) use either (any criteria can be added):
It is completely possible to have multiple extended modUser systems active at the same time. It would even be feasible to extend Jason
Coward's rpx extension into a hybrid system utilizing the benefits of both systems. It is also completely possible to have multiple extended
modUser 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.
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 done
extensively.
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 a
real 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
rpm extension allows people to login via Facebook and other social networking medium
This section covers alternative development options and information for MODx developers.
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 = new modX();
$modx->initialize('web');
This will initialize the MODx object into the 'web' Context. Now, if you want to access it under a different context (and thereby changing its access
permissions, policies, etc), you'll just need to change 'web' to whatever Context you want to load.
From there, you can use any MODx methods, functions, or classes.
See Also
Developer Introduction
xPDO, the db-layer for Revolution
API Reference
http://api.modxcms.com
Class Reference
modX
This is the base class of MODx Revolution; it is used for many of the main processing methods of MODx. It extends the xPDO class.
Methods
See Also
modX.getChunk
modX::getChunk
Syntax
Example
Lets process this chunk and output its value. We have this Chunk, called "WelcomeChunk":
<p>Welcome [[+name]]!</p>
$output = $modx->getChunk('WelcomeChunk',array(
'name' => 'John',
));
return $output;
<p>Welcome John!</p>
See Also
Chunks
modX.getLoginUserName
modX::getLoginUserName
Returns the current user name, for the current or specified context.
Syntax
Example
$username = $modx->getLoginUserName();
See Also
Contexts
modX.getPlaceholder
modX::getPlaceholder
Syntax
Example
$value = $modx->getPlaceholder('MyPlaceholder');
See Also
modX.setPlaceholder
modX.setPlaceholders
modX.toPlaceholder
modX.toPlaceholders
modX.setPlaceholder
modX::setPlaceholder
Syntax
Example
$modx->setPlaceholder('name','Barry');
See Also
modX.getPlaceholder
modX.setPlaceholders
modX.toPlaceholder
modX.toPlaceholders
modX.setPlaceholders
modX::setPlaceholders
Note that unlike modX.toPlaceholders, this function does not add separators between the namespace and the placeholder key. Use
toPlaceholders() when working with multi-dimensional arrays or objects with variables other than scalars so each level gets delimited by a
separator.
Syntax
Example
See Also
modX.toPlaceholder
modX.toPlaceholders
modX.setPlaceholder
modX.getPlaceholder
modX.addEventListener
modX::addEventListener
Syntax
Example
$modx->addEventListener('OnChunkPrerender',12);
See Also
modX
Plugins
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
Example
if ($modx->checkForLocks($modx->getLoginUserID(),'edit_chunk','edit');
See Also
modX
modX.checkSession
modX::checkSession
Syntax
Example
$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:
Syntax
Example
$modx->executeProcessor(array(
'location' => 'context',
'action' => 'getList',
));
See Also
modX
modX.getAuthenticatedUser
modX::getAuthenticatedUser
Gets the user authenticated in the specified context.
Syntax
Example
$user = $modx->getAuthenticatedUser('sports');
See Also
modX
modX.getCacheManager
modX::getCacheManager
Overrides xPDO::getCacheManager.
Syntax
object getCacheManager()
Example
$cacheManager = $modx->getCacheManager();
$cacheManager->set('testcachefile','test123');
See Also
modX
modX.getChildIds
modX::getChildIds
Syntax
Example
Get all the children IDs for the Resource 23. Limit to 6 levels deep.
$array_ids = $modx->getChildIds(23,6);
See Also
modX
modX.getParentIds
modX.getConfig
modX::getConfig
Syntax
array getConfig ()
Example
$config = $modx->getConfig();
See Also
modX
modX.getContext
modX::getContext
Within a request, contexts retrieved using this function will cache the context data into the modX::$contexts array to avoid loading the same
context multiple times.
Syntax
Example
$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
Example
$map = $modx->getEventMap();
See Also
modX
modX.getLoginUserID
modX::getLoginUserID
Returns the current user ID, for the current or specified context.
Syntax
Example
$id = $modx->getLoginUserID('sports');
See Also
modX
modX.getParentIds
modX::getParentIds
Example
Get all of the parent IDs for the Resource with ID 23.
$parentIds = $modx->getParentIds(23);
See Also
modX
modX.getChildIds
modX.getParser
modX::getParser
Returns an instance of modParser responsible for parsing tags in element content, performing actions, returning content and/or sending other
responses in the process.
Syntax
object getParser()
Example
$parser = $modx->getParser();
See Also
modX
modX.getRegisteredClientScripts
modX::getRegisteredClientScripts
Syntax
string getRegisteredClientScripts ()
Example
Get all registered scripts into an array.
$scripts = $modx->getRegisteredClientScripts();
See Also
modX
modX.getRegisteredClientStartupScripts
modX::getRegisteredClientStartupScripts
Syntax
string getRegisteredClientStartupScripts ()
Example
$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
Example
$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
Example
$modx->getRequest('myResponse','/path/to/');
See Also
modX
modX.getService
modX::getService
Syntax
object getService (string $name, [string $class = ''], [string $path = ''], [array $params = array
()])
Examples
$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
modX
MODx Services
modX.getSessionState
modX::getSessionState
Returns the state of the SESSION being used by modX.
modX::SESSION_STATE_UNINITIALIZED
modX::SESSION_STATE_UNAVAILABLE
modX::SESSION_STATE_EXTERNAL
modX::SESSION_STATE_INITIALIZED
Syntax
integer getSessionState ()
Example
$state = $modx->getSessionState();
See Also
modX
modX.getTree
modX::getTree
Syntax
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
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"):
Syntax
array getVersionData ()
Example
$vers = $modx->getVersionData();
echo $vers['full_version'];
See Also
modX
modX.handleRequest
modX::handleRequest
Syntax
mixed handleRequest ()
Example
See Also
modX
modX.hasPermission
modX::hasPermission
Syntax
Example
Deny the user access if they don't have the permission 'edit_chunk' in their loaded Policies.
See Also
Policies
modX.initialize
modX::initialize
This includes preparing the session, pre-loading some common classes and objects, the current site and context settings, extension packages
used to override session handling, error handling, or other initialization classes.
Syntax
Example
$modx->initialize('sports');
See Also
Contexts
modX.invokeEvent
modX::invokeEvent
Syntax
Example
$modx->invokeEvent('OnChunkRender',array(
'id' => $chunk->get('id'),
));
See Also
modX
modX.lexicon
modX::lexicon
This may also be a modLexicon object as well, if the Lexicon has been loaded. PHP supports having objects and methods with
the same name.
Syntax
Example
Output the translation of the 'welcome_message' Entry, and sets the 'name' Placeholder in it.
See Also
Internationalization
modX.makeUrl
modX::makeUrl
Syntax
string makeUrl (integer $id, [string $context = ''], [string $args = ''], [mixed $scheme = -1])
Examples
$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
Syntax
string parseChunk (string $chunkName, array $chunkArr, [string $prefix = '[[+'], [string $suffix =
']]'])
Example
See Also
modX
modX.getChunk
modX.regClientCSS
modX::regClientCSS
Syntax
$modx->regClientCSS('assets/css/style.css');
See Also
modX
modX.regClientHTMLBlock
modX.regClientScript
modX.regClientStartupHTMLBlock
modX.regClientStartupScript
modX.getRegisteredClientScripts
modX.getRegisteredClientStartupScripts
modX.regClientHTMLBlock
modX::regClientHTMLBlock
Syntax
Example
See Also
modX
modX.regClientCSS
modX.regClientScript
modX.regClientStartupHTMLBlock
modX.regClientStartupScript
modX.getRegisteredClientScripts
modX.getRegisteredClientStartupScripts
modX.regClientScript
modX::regClientScript
Syntax
Example
Add some JS to the end of the page.
$modx->regClientScript('assets/js/footer.js');
See Also
modX
modX.regClientCSS
modX.regClientHTMLBlock
modX.regClientStartupHTMLBlock
modX.regClientStartupScript
modX.getRegisteredClientScripts
modX.getRegisteredClientStartupScripts
modX.regClientStartupHTMLBlock
modX::regClientStartupHTMLBlock
Syntax
Example
$modx->regClientStartupHTMLBlock('<tag></tag>');
See Also
modX
modX.regClientCSS
modX.regClientHTMLBlock
modX.regClientScript
modX.regClientStartupScript
modX.getRegisteredClientScripts
modX.getRegisteredClientStartupScripts
modX.regClientStartupScript
modX::regClientStartupScript
Syntax
Example
See Also
modX
modX.regClientCSS
modX.regClientHTMLBlock
modX.regClientScript
modX.regClientStartupHTMLBlock
modX.getRegisteredClientScripts
modX.getRegisteredClientStartupScripts
modX.reloadConfig
modX::reloadConfig
Reload the config settings. Useful in cases where you've loaded some Settings dynamically.
Syntax
array reloadConfig ()
Example
$modx->reloadConfig();
See Also
modX
Settings
System Settings
modX.removeAllEventListener
modX::removeAllEventListener
Syntax
void removeAllEventListener ()
Example
$modx->removeAllEventListener();
See Also
modX
modX.removeEventListener
modX::removeEventListener
Syntax
Example
$modx->removeEventListener('OnChunkRender');
See Also
modX
modX.runSnippet
modX::runSnippet
Syntax
Example
$output = $modx->runSnippet('Welcome',array(
'name' => 'John'
));
echo $output; // prints 'Welcome John!'
See Also
modX
Snippets
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; these
are 'unavailable' (the default), and 'fatal'.
Syntax
Examples
$modx->sendError('unavailable');
$modx->sendError('fatal');
See Also
modX
modX.sendErrorPage
modX::sendErrorPage
Syntax
Example
Send the user to the default Error page for the site.
$modx->sendErrorPage();
See Also
modX
modX.sendUnauthorizedPage
modX.sendForward
modX.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
Example
Send the user to Resource ID 234 without actually changing the URL.
$modx->sendForward(234);
See Also
modX
modX.sendRedirect
modX.sendErrorPage
modX.sendRedirect
modX::sendRedirect
Syntax
void sendRedirect (string $url, [integer $count_attempts = 0], [string $type = ''])
Examples
$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
modX
modX.sendForward
modX.sendErrorPage
modX.sendUnauthorizedPage
modX::sendUnauthorizedPage
Example
$modx->sendUnauthorizedPage();
See Also
modX
modX.sendErrorPage
modX.sendForward
modX.sendRedirect
modX.setDebug
modX::setDebug
Syntax
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
Be aware that switching contexts does not allow custom session handling classes to be loaded. The gateway defines the session handling that is
applied to a single request. To create a context with a custom session handler you must create a unique context gateway that initializes that
context directly.
Syntax
$modx->switchContext('sports');
See Also
Contexts
modX.toPlaceholder
modX::toPlaceholder
Recursively validates and sets placeholders appropriate to the value type passed.
Syntax
void toPlaceholder (string $key, mixed $value, [string $prefix = ''], [string $separator = '.'])
Example
$modx->toPlaceholder('name','John','my');
See Also
modX.toPlaceholders
modX.setPlaceholder
modX.setPlaceholders
modX.getPlaceholder
modX.toPlaceholders
modX::toPlaceholders
Each recursive level adds to the prefix, building an access path using an optional separator.
Syntax
void toPlaceholders (array|object $subject, [string $prefix = ''], [string $separator = '.'])
Example
See Also
modX.toPlaceholder
modX.setPlaceholder
modX.setPlaceholders
modX.getPlaceholder
modX.unsetPlaceholder
modX::unsetPlaceholder
Syntax
Example
$modx->unsetPlaceholder('myPlaceholder');
See Also
modX.unsetPlaceholders
modX.toPlaceholder
modX.toPlaceholders
modX.setPlaceholder
modX.setPlaceholders
modX.getPlaceholder
modX.unsetPlaceholders
modX::unsetPlaceholders
Syntax
Example
$modx->unsetPlaceholders(array('my.name','my.email'));
See Also
modX.unsetPlaceholder
modX.setPlaceholder
modX.setPlaceholders
modX.toPlaceholder
modX.toPlaceholders
modChunk
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
Syntax
Example
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
Syntax
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
Methods
See Also
Page: Users
Page: modUser
Page: Users
Page: modUser
modUser.isAuthenticated
modUser::isAuthenticated
Separate session contexts can allow users to login/out of specific sub-sites individually (or in collections).
Syntax
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
Syntax
Example
$modx->addSessionContext('sports');
See Also
Page: Users
Page: modUser
Page: Users
Page: modUser
modUser.changePassword
modUser::changePassword
Syntax
Example
$modx->changePassword('b33r4me', 'boo123');
See Also
Page: Users
Page: modUser
Page: Users
Page: modUser
modUser.endSession
modUser::endSession
Syntax
void endSession ()
Example
$user->endSession();
See Also
modUser
modUser.getSessionContexts
modUser::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
Syntax
array getSettings ()
Example
$settings = $user->getSettings();
See Also
modUser
Settings
modUser.hasSessionContext
modUser::hasSessionContext
Checks if the user has a specific session context, or in other words, is "logged into" a certain context.
Syntax
Example
See Also
modUser
Contexts
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
Example
$user->isMember('Staff');
$user->isMember(array('Staff','Investors'));
See Also
modUser
modUser.loadAttributes
modUser::loadAttributes
Syntax
Example
Load attributes for the 'sports' context and the modResource target.
$user->loadAttributes('modResource','sports',true);
See Also
modUser
modUser.removeSessionContext
modUser::removeSessionContext
Syntax
Example
$user->removeSessionContext('sports');
See Also
modUser
Contexts
modUser.removeSessionContextVars
modUser::removeSessionContextVars
Syntax
Example
Remove all session vars for the User in the 'sports' Context.
$user->removeSessionContextVars('sports');
See Also
modUser
Contexts
modUser.removeSessionCookie
modUser::removeSessionCookie
Syntax
API Doc: http://api.modxcms.com/modx/modUser.html#removeSessionCookie
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
The Scenario
Our Model
Building the Schema
Using our New Model
See 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 in
your 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 your
own 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 store
locations 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 DB
connection, 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:
id
name
address
city
state
zip
country
phone
fax
active
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 XML
representation of our database table(s). It is then parsed by xPDO into PHP-format "maps", which are array representations of the schema and its
relationships.
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; but
it's definitely recommended, especially with the core/components/storefinder/ structures, since that makes creating the transport package (should
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 for
now, we're going to do this in MySQL.
In our XML file, let's start out with the first few lines:
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 some
attributes 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 xPDO
separates 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 custom
xPDOObject 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 not
standard 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" is our representation of a table, which will generate into an xPDOObject class when we're through. There are some attributes to note
here:
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. Extended
classes 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:
As you can see here, each column in our table has a field definition tag. From there, we have
attribute properties for each field. Most of these are optional, depending on the database type of the column. Some of those attribute properties
are:
</object>
</model>
So now we have a completed XML schema for our model! You can view the full version
here. Let's move on to the schema build script.
<?php
define('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 of
that 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
*
* @package storefinder
* @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,
'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, It
includes our build.config.php file to tell the schema script where to find MODx Revolution. Thirdly, it loads the necessary classes, initializes the
modX 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 lines
to 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.
$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("%2.4f s", $totalTime);
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! Our
storefinder/core/model/storefinder/ directory is now filled with all of our map and class files!
$base_path = dirname(dirname($modx->getOption('core_path'))).'/MODx
Components/tutorials/storefinder/trunk/core/components/storefinder/';
/* change above line to your path */
$o = '';
$f = $base_path.'snippet.storefinder.php';
if (file_exists($f)) {
$o = include $f;
} else {
$modx->setLogTarget('ECHO');
$modx->log(modX::LOG_LEVEL_ERROR,'StoreFinder not found at: '.$f);
}
return $o;
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 our
Component. Then we'll simply delete this 'StoreFinder' snippet from our MODx Revolution instance, and install our package. You can find more
about building packages by going here. If you don't want to build a transport package (we recommend doing so, it makes upgrades FAR easier!),
you 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
/**
* @package storefinder
*/
$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 our
target 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 that
model 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. After
running, 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 only once (unless you want duplicate data). That should populate your table with some data, and then output 'Total: 2', assuming you
didn'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 this
tutorial will be dealing with finishing our Component's scenario. You can stop reading if you want.
See Also
Generating the xPDO Model Code
http://svn.modxcms.com/docs/display/revolution/PHP+Coding+in+MODx+Revolution%2C+Pt.+I
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 that
you're familiar with Revolution's Tag Syntax before we start.
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 splittingred.com. If you'd like a demo before reading, try there.
Needed Extras
Optional Extras
We'll create one called 'BlogPostTemplate'. Our content looks something like this:
[[$pageHeader]]
<div id="content" class="blog-post">
<h2 class="title"><a href="[[~[[*id]]]]">[[*pagetitle]]</a></h2>
<p class="post-info">
Posted on [[*publishedon:strtotime:date=`%b %d, %Y`]] |
Tags: [[*tags:notempty=`[[!tolinks? &items=`[[*tags]]` &key=`tag` &target=`1`]]`]] |
<a href="[[~[[*id]]]]#comments" class="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">Tags: [[!tolinks? &items=`[[*tags]]` &key=`tag` &target=`1`]]</span>
`]]
<br class="clear" />
</div>
<hr />
<div class="post-comments" id="comments">[[!Quip?
&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, and
adjust 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 you
from doing that.
First off, you'll notice that I have two chunks: "pageHeader" and "pageFooter". These chunks contain my common HTML tags that I would put in
the 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 changes
in 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 same
page.
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="[[~[[*id]]]]#comments" class="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 just
yet, so dont worry - and then pass it as a property to the 'tolinks' snippet. The tolinks snippet comes with tagLister, and translates delimited tags
into 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 page
besides 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' property
in 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 for
each new Blog Post we create. Neat!
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 of
it like a beginning excerpt for a blog post, that will show up on our main page when we're listing the latest blog posts.
Okay, now we're in the comments part of BlogPostTemplate. As you can see here, we're using Quip for our commenting system. You could feel
free to use another system, such as Disqus, here if you choose. For this tutorial, we'll go with Quip. Our code is as follows:
Okay, cool. Note we have two Snippet calls here - one for displaying the comments for this thread ( Quip), and another for displaying the reply
form (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 going
to 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
Documentation. We recommend reading there for how to set it up.) with the 'replyResourceId' property.
Next, we want to specify - in both the Quip and Quip Reply calls - a 'closeAfter' property. This tells Quip to automatically close commenting on
these 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 in
the 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 has
access 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 of
tags.
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, and
make 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 to
browse 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 getResources and getPage that automatically filters results by a 'tags' TV. So
basically, 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.
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. It
should contain this:
<div class="post">
<h2 class="title"><a href="[[~[[+id]]]]">[[+pagetitle]]</a></h2>
<p class="post-info">Posted by [[+createdby:userinfo=`fullname`]]
[[+tv.tags:notempty=` | <span class="tags">Tags:
[[!tolinks? &items=`[[+tv.tags]]` &key=`tag` &target=`1`]]
</span>`]]</p>
<div class="entry">
<p>[[+introtext:default=`[[+content:ellipsis=`400`]]`]]</p>
</div>
<p class="postmeta">
<span class="links">
<a href="[[~[[+id]]]]" class="readmore">Read more</a>
| <a href="[[~[[+id]]]]#comments" class="comments">
Comments ([[!QuipCount? &thread=`blog-post-[[+id]]`]])
</a>
| <span class="date">[[+publishedon:strtotime:date=`%b %d, %Y`]]</span>
</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 is
empty, 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!
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 and
tolinks Snippet calls to reflect that.
Adding Posts
Okay, now we're ready to actually add blog posts, now that our structure is all setup.
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 month
container 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 up
in your getResources calls.
Remember, though, that whatever structure you build under the sections, that's not going to determine your navigation - Archivist will handle that.
What it will determine, however, is the URL of your blog posts. So have fun.
Okay - go ahead and create a new Resource, and set it's Template to 'BlogPostTemplate'. Then you can start writing your post. You can specify
in 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!
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`
]]
[[+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 getArchives snippet,
and 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 we
reference 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're
done with that. Our Resource, for reference purposes, we'll say has an ID of 30.
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 actually
pretty 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 Archivist Snippet does is generate a month-by-month list of posts (you can add all kinds of other options, but see it's documentation
for 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 Section
Resources).
That's it! Archivist will actually automatically handle the rest - including all your URL generation for archives - archives/2010/05/ will show all the
posts within May 2010, where archives/2009/ will show all posts in 2009. Pretty sweet, huh?
Advanced Options
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 of
Member (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 posts
are made. They can then either login to the manager to moderate comments, or click on links directly in the emails to approve or reject the
comments. 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, but
Quip will handle the rest.
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 getResources to display a top 5 list of Resources in your Section Resources (34,35), and sort by their publishedon date.
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="[[~[[+id]]]]">[[+pagetitle]]</a>
[[+publishedon:notempty=`<br /> - [[+publishedon:strtotime:date=`%b %d, %Y`]]`]]
</li>
What about a widget that shows a few of the latest comments across your posts? Simple - Quip packages a nice little snippet called
QuipLatestComments that can handle this easily.
Place the call wherever you want the comment list to show:
[[!QuipLatestComments? &tpl=`latestCommentTpl`]]
<li class="[[+cls]][[+alt]]">
<a href="[[+url]]">[[+body:ellipsis=`[[+bodyLimit]]`]]</a>
<br /><span class="author">by [[+name]]</span>
<br /><span class="ago">[[+createdon:ago]]</span>
</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' Output Filter we used here. This filter is built into
MODx 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 documentation for the snippet for more configuration options.
This part is ridiculously easy; tagLister does this for you. Just place this wherever you want:
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 ton
more configuration options, but we'll leave you with this.
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 customize
and 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 splittingred.com, if you'd like to see a full-scale demo of it in action.
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 support
OOP projects? Is it faster? Will it be easy to learn?
The codebase in Revolution has switched to xPDO, an object relational bridge modeling tool built by Jason Coward. In layman's terms, this
means 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.
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);
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 other
databases) From there you can view an XML representation of all the MODx DB tables.
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 the
schema:
The class property tells you what the name of the class will be. The table property shows the actual MySQL table, and extends shows what object
it extends. modElement is a base class for all Elements in MODx - snippets, modules, chunks, templates, etc.
This tag represents a column in the database. Most of these attributes are pretty straightforward.
Okay, this is where we get into DB relationships. An Aggregate relationship is a relationship where, in laymans terms, if you were to delete this
chunk, it wouldn't delete the Category that it's related to. If it were a Composite relationship, it would. There is "dependence" in the Composite
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, in
the next article.
See Also
xPDO: Defining a Schema
xPDO: Related Objects
Creating Objects
Removing an Object
More Complex Queries
See 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:
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 is
simply:
$template->remove();
Yes. That's it. Done. It will also remove any composite relationships - with modTemplates, these are the modTemplateVarTemplate objects, which
map 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',
),null,1);
$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 group
number, 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 to
do most things - you could also have used $modx->getCollectionGraph for this as well.
In the next article, we'll talk about how this is used in the context of MODx processors with JSON.
See Also
xPDO: Creating Objects
xPDOObject::remove
xPDOQuery
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 which
processor to send to. Processors are sent the sanitized REQUEST data, and then when finished respond with a JSON message back to the
browser.
This allows for quick, easy requests that reduce the load on the server and the browser. You can also do multiple, asynchronous requests to
processors 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 (in
our 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
First off, we include the root index.php file for the processors, which does some slight variable checking and includes licensing. Then, we load the
proper lexicon foci. In MODx Revolution, i18n language files are separated into smaller files by their 'foci', which is a term we've coined for 'focus
area'. 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 browser
via $modx->error->failure(). The response is a string message translated via the lexicon.
// default values
if ($_POST['name'] == '') $_POST['name'] = $modx->lexicon('chunk_untitled');
// if the name already exists for this chunk, send back an error
$name_exists = $modx->getObject('modChunk',array('name' => $_POST['name']));
if ($name_exists != null) return $modx->error->failure($modx->lexicon('chunk_err_exists_name'));
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']));
if ($category == null) {
$category = $modx->newObject('modCategory');
if (empty($_POST['category'])) {
$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 the
category in the database and prepares it for later association to the Chunk.
Events are pretty much the same invoke-wise in Revolution as they were in 096 - however they are more optimized in their loading.
Now, how manager actions work in Revolution is a little different. This stores a lexicon string key ('chunk_create'), the class key of the object
being 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?
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.
The Problem
Creating the Resources
Doing the Front-End Loading
Wait, I want the Page Titles as the tab headers!
Using getResources
Using Wayfinder
Using a getField Snippet
Conclusion
The Problem
We want in our site to use jQuery's tabs to load our Resources via AJAX. How do we do that in MODx? This tutorial will show you just how easy it
is to accomplish this in MODx Revolution.
<div id="tabs">
<ul>
<li><a href="[[~92]]">Resource with ID 92</a></li>
<li><a href="[[~546]]">Resource with ID 546</a></li>
<li><a href="[[~123]]">Resource with ID 123</a></li>
</ul>
</div>
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 like
this:
<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:
<script type="text/javascript">
$(function() { $("#tabs").tabs(); });
</script>
<div id="tabs">
<ul>
[[Wayfinder? &startId=`123` &level=`1` &rowTpl=`myRowTpl`]]
</ul>
</div>
Or, you can use a Snippet such as this one to grab the pagetitle:
<?php
/**
* Grabs a field for a specified Resource
*/
/* setup some default properties */
$id = $modx->getOption('id',$scriptProperties,false);
$field = $modx->getOption('field',$scriptProperties,'pagetitle');
<script type="text/javascript">
$(function() { $("#tabs").tabs(); });
</script>
<div id="tabs">
<ul>
<li><a href="[[~92]]">[[getField? &id=`92` &field=`pagetitle`]]</a></li>
<li><a href="[[~546]]">[[getField? &id=`546` &field=`pagetitle`]]</a></li>
<li><a href="[[~123]]">[[getField? &id=`123` &field=`pagetitle`]]</a></li>
</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 for
the Documents be blank (or minimal) so that it only loads the parsed content itself.
This will successfully load your MODx Resources into jQuery tabs.
The Problem
When working in collaboration, teams of developers and designers often collaborate via Subversion (SVN) to make development easier between
multiple people. MODx, however, stores its data in the database. This has many benefits generally, but DB-stored code cannot be
version-controlled via SVN.
The Solution
For Resources, it's simple. Just use Static Resources, and point the content to a file in your SVN checkout.
For Elements, all you need is a simple "include" snippet. The code:
[[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 Templates and TVs as well; just plop the include snippet wherever you
need filesystem-based files.
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 with
this 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 is
used in these comparisons.
In this schema file, each table is defined as an object, and all objects are wrapped in the following model tag:
Also note that each object defines a table attribute, but this attribute does NOT include any table prefix you may have defined for your site.
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 definition
of that object, but if an id from a table is used as a foreign key in another table, this dependency will noted in both tables; this isn't the case with
every ORM out there.
Things to Note
1. The XML precision matches up exactly to MySQL precision defined after each datatype: it reflects how many characters are visible in a
column. E.g. int(2) could store any 2 digit number.
2. The null="false" in the XML corresponds to the NOT NULL definition in MySQL.
3. attributes="unsigned" in the XML corresponds directly to the UNSIGNED definition in MySQL
system_settings
This is a fairly simple table too, but it includes 2 foreign keys.
XML
MySQL
user_group_roles
This table includes a single foreign key... actually, its id is used as a foreign key by another table. This is a good reminder that in xPDO, the
foreign key relationships are defined in both directions.
XML
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 Plugin and associate it to the OnDocFormRender and OnDocFormSave events. Our code would look like this:
<?php
/**
* Register a form field to forms
*/
switch ($modx->event->name) {
case 'OnDocFormPrerender':
/* if you want to add custom scripts, css, etc, register them here */
break;
case 'OnDocFormRender':
$v = '';
if (isset($scriptProperties['resource'])) {
/* 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 do the HTML */
$fields = '
<div class="x-form-item x-tab-item">
<label class="x-form-item-label" style="width:150px;">Home</label>
<div class="x-form-element">
<input type="text" name="home" value="'.$v.'" class="x-form-text x-form-field" />
</div>
</div>
';
$modx->event->output($fields);
break;
case 'OnDocFormSave':
/* do processing logic here. */
$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.
The Resources
But 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 that
process. 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 Atlassian-driven development environment, comprised of Jira, Fisheye/Crucible, and Confluence. All resources use an
SSO-driven authentication interface, so no need for multiple logins. For now, you only need 2 - one for the Atlassian apps, and the other for the
MODx 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.
If you want to contribute to Confluence, the official documentation wiki, work on bugs, or actually commit some code, the first step is to fill out and
send in a Contributor License Agreement (CLA) right after creating a JIRA account. A CLA protects your contributions, but also gives MODx
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—more
like copied directly from—the same one used by Apache and the Dojo Foundation.
You can submit a patch by using Subversion (SVN). For the current time, MODx is on SVN for development, although this will soon be moving to
Git. Simply checkout MODx Revolution and start working on your patch. Then, you can use SVN's "Create Patch" to create the patch and submit
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 over
Australia after they skipped Africa, and ends the game by throwing the board at the others while yelling Orwellian quotations) the core dev team
will 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 those
commits reviewed by fellow team members in Crucible. Don't worry if your first Crucible review is scathing - we usually do that to the newbies. At
least we don't do the glue-on-the-keyboard initiation anymore (darn lawyers).
Sometimes a patch you submit wont make it in. That may be for a myriad of reasons:
So don't take offense. We really appreciate any and all contributions to MODx, and we seriously consider everything that this wonderful
community gives to it. MODx has thrived because of this community. However, some things just wont match with the MODx vision and design
philosophy; 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
In developing MODx Revolution, the MODx Team has found the following environments invaluable:
Netbeans
Netbeans 6.8
Netbeans Subversion and JIRA plugins
Eclipse
Installation
Other IDEs
For Mac:
TextMate - IDE
Coda - IDE
Versions - SVN client
svnX - SVN client
For PC:
UltraEdit - IDE
E - IDE
TortoiseSVN - SVN client
Kate - IDE for Linux / KDE
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 soon
SimpleTest - this drives the PHP 4/5.0.x unit testing framework for xPDO, and we'll be adding a test harness to MODx soon
PHPDocumentor - all of the classes in MODx Revolution are documented in PHPDoc format, and we'll be developing tutorials and other
extended documentation for inclusion in the PHPDocs in DocBook XML format
Phing - will be used to allow automation of nightly builds, various distribution builds, unit testing, and many other development tasks
General
Parenthesis
Classes
Variables
Function Arguments and Class Variables
Arrays
Constants
File Structure
Prefixing
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
if ($test) {
}
while ($test == $other) {
}
array_push($one,$two);
return $test;
Do not use parenthesis when using include, require, include_once, and require_once.
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 {
public $publicVar;
private $_privateVar;
private function _privateFunc() { }
public function publicFunc() { }
}
Variables
class modFactor {
public function testFunc($testVar, array &$anotherTest = array()) {
$this->_privateVar = $testVar;
$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
File Structure
Prefixing
$_lang['mycomponent.welcome_message'] = 'Welcome!';
Using GitHub