Sunteți pe pagina 1din 298

MODx Official Documentation

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.

You asked. We listened. Here is our answer: Revolution.

Want to contribute to Revolution development or testing? See our Becoming a Core Contributor page.

Revolution 2.0 Official Documentation


This is the official documentation space for MODx Revolution 2.0.

Getting Started

Making Sites with MODx

Administering Your Site

Developing in MODx

Case Studies and Tutorials

MODx Community Information

You can download the documentation in 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

Supported Operating Systems

Linux x86, x86-64


Mac OS X
Windows XP, Server

Supported Web Servers

Apache 1.3.x - 2.2.x (uses htaccess for Friendly URLs by default)


IIS 6.0
lighttpd (Setup and Friendly URL Guide)
Zeus

PHP Compatibility

5.1.1 and above (excluding 5.1.6 and 5.2.0)


Required extensions:
zlib
JSON (or PECL library)
mod_rewrite (for friendly URLs/.htaccess)
GD lib (required for captcha and file browser)
PDO, specifically pdo_mysql (for xPDO)
SimpleXML
Safe_mode off
Register_globals off
PHP memory_limit 24MB or more, depending on your server

PHP Configuration Options

./configure --with-apxs2=/usr/local/bin/apxs --with-mysql --prefix=/usr/local --with-pdo-mysql


--with-zlib

MySQL Database Requirements

4.1.20 or newer, with the following permissions:


SELECT, INSERT, UPDATE, DELETE are required for normal operation
CREATE, ALTER, INDEX, DROP are required for installation/upgrades and potentially for various add-ons
CREATE TEMPORARY TABLES may be used my some 3rd party add-ons
excludes version 5.0.51 (Why not 5.0.51?)
InnoDB storage engine
MyISAM storage engine

Supported Browsers (for Backend Manager Interface)

Mozilla Firefox 3.0 and above


Apple Safari 3.1.2 and above
Microsoft Internet Explorer 8 and above

IE7 is not fully supported at this time, but may be in a feature release. You are free to use it, but might experience bugs here
and there.
MySQL 5.0.51 Issues

Why does MODx not support MySQL server version 5.0.51?

MySQL 5.0.51, including 5.0.51a, has serious bugs with PDO, specifically grouping, ordering and prepare statements.

It will cause uncorrectable errors in normal queries in MODx, as well as other open source applications, and therefore MODx does not support
installations with MySQL 5.0.51 installed. Please upgrade your MySQL installation.

MySQL 5.0.51 Server Buglist

Here are just some of the bugs that occur:

http://bugs.mysql.com/bug.php?id=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:

From the MODx Site

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.

"Normal" vs. "Advanced"

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:

Normal Distribution: Basic Installation


Advanced Distribution: Advanced Installation
Building from Git: Git Installation
After finishing installation, if you are still having issues, please read the Troubleshooting Installation page.

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.

When you're finished, click Next to proceed.

Database Options

From here, you will get a form asking you for your database information:

Add in your database hostname, which is the URL at which your database is located. For most users, this will be 'localhost'. If you have 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.

Collations and Charsets

This will then popup another form for setting your database charset and collation:
For most users you can leave these values at what they are. However, if you need to change them, make sure the collation matches the charset.
Click the 'Create or test selection of your database.' after you've finished.

Creating an Administrator User

This form will now present you with a few fields for setting up your administrator user. Specify a username that you want to be the 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

Some other special cases:

WAMPServer 2.0i

Please see this article: Problems with WAMPServer 2.0i

MAMP on MacOSX

MAMP (including latest 1.8.4) works fine with MODx Revolution, with one exception. You cannot use eAccelerator as the caching system, as 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.

Vista and XAMPP

There have been reported problems with installing Revolution on 64-bit Vista with XAMPP. We cannot guarantee a working solution on that 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

MODx Revolution on Debian

MODx Revolution on Debian

Debian packages in old versions of MySQL drivers in, so to get it up-to-date and working with the PDO drivers in MODx Revolution, you'll have to
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:

vi /etc/apt/sources.list (you can choose a different mirror):


deb http://packages.dotdeb.org stable all
deb-src http://packages.dotdeb.org stable all
deb http://php53.dotdeb.org stable all
deb-src http://php53.dotdeb.org stable all

apt-get update
apt-get upgrade php5

vi /etc/php5/apache2/php.ini
date.timezone = Europe/Amsterdam

/etc/init.d/apache2 reload

Lighttpd Guide

Lighttpd Guide for Setup and Friendly URLs.


Friendly URL Setup

Lighttpd Guide for Setup and Friendly URLs.

This is still a work in progress, and currently only covers the URL rewriting aspect.
This guide assumes you already have a working lighttpd, mysql, and PHP installation.
This guide only covers proper settings and the use of friendly URL Rewriting.

Friendly URL Setup

lighttpd does not use the same system, or even same idea as Apache does for URL rewriting. All URL rewriting is done in 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"

Directly under this you should add the following code.

url.rewrite-once = ( "^/(assets|manager|core|connectors)(.*)$" => "/$1/$2",


"^/(?!index(?:-ajax)?\.php)(.*)\?(.*)$" => "/index.php?q=$1&$2",
"^/(?!index(?:-ajax)?\.php)(.*)$" => "/index.php?q=$1"
)
This does not mean you are done! Lighttpd handles url-rewrites a bit differently. You HAVE to exclude any files or folders you do
not want rewritten in the config file. Excluded dirs/files in the example above are (assets|manager|core|connectors). If you wish
to add more to these, simple add another | followed by the folder or filename you wish to omit from url rewriting.

After this is done, you will have working friendly URLs again in lighttpd.

Problems with WAMPServer 2.0i

How to get WAMPServer 2.0i working on MODx Revolution

Mary (einsteinsboi) has a great blog post about using WAMPServer 2.0i with MODx Revolution, and some problems you might encounter.

http://codingpad.maryspad.com/2010/01/11/modx-revolution-and-wamp/

A short summary and explanation is below.

WAMPServer uses mismatched MySQL Server and Client builds

Usually it is best to make sure in any server configuration that your MySQL server and client build versions are the same. WAMPServer 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.

Renaming or Moving the Core


MODx Revolution allows you to rename and/or move the core/ directory to enhance your level of security. You can also move the core/ directory
outside of the webroot to further secure your MODx installation.

Should you choose to rename or move the core, MODx recommends doing so before installing. Simply rename or move the core, and setup/ - 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.

Changing the Configuration Key

From here, MODx will ask you to choose a language. Do so at this time. MODx will then prompt you with a welcome page, and below will ask 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:

Specify a custom, unique config key and click next.

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.

Below that, you will be presented with two checkbox options:


These will be grayed out during new installations. (During upgrades, it is recommended that you uncheck these as well.) Click 'Next' to proceed to
the next step.

Database Options

From here, you will get a form asking you for your database information:

Add in your database hostname, which is the URL at which your database is located. For most users, this will be 'localhost'. If you have 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.

Collations and Charsets

This will then popup another form for setting your database charset and collation:

For most users you can leave these values at what they are. However, if you need to change them, make sure the collation matches the charset.
Click the 'Create or test selection of your database.' after you've finished.

Creating an Administrator User


This form will now present you with a few fields for setting up your administrator user. Specify a username that you want to be the 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.

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.

Make sure you change both the path and URL!

When done, click 'Next' to proceed.

Pre-Installation Checks

MODx will then proceed with a list of checks to verify that your system is ready for installing. If any of these fail, you'll need to proceed with 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:

git clone http://github.com/modxcms/revolution.git

Or, if you'd like to contribute back, fork it in your GitHub repository and clone that repository with:

git clone git@github.com:yourgitusernamehere/revolution.git

Forking it with your GitHub account will allow you to contribute back to MODx by sending pull requests by clicking the "Pull Request" button 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:

git checkout -b 2.0 origin/2.0

And git will handle the rest.

There will be other branches in the future, centered around new features. New branches will be feature-specific and merged into
the 2.1 branch when they are complete and stable.

Run the Build

If this is the first time you are building from Git, copy the file _build/build.config.sample.php to _build/build.config.php and edit the properties 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?).

Upgrading Your Local Git Repository After Commits

Simply run these two commands:

git fetch origin


git rebase origin/2.0

And Git will update your install. (Substitute '2.0' for '2.1' or 'master' if you're running from another branch.)

When a commit is made, this message might show up in the commit:

[ReUp] - If your updates require a core transport rebuild (such as anything modified in the _build directory, database model changes, or
default data changes), then prefix your commit message with this. If you see this message, simply rebuild the core transport and run
setup/ again.

If this message does not show up, you're done after you fetch and rebase.

Sending Pull Requests

If you've fixed a bug or added an improvement, and you're working on a fork of the revolution repository, you can send a pull request to 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.

You'll need to submit a CLA before we can accept your code.

Switching Branches

If you want to switch to a different branch, simply type these commands:

git fetch origin


git checkout 2.1

Of course, replacing 2.1 with the name of the branch you want to switch to. After you've done so, run the build and run setup/ again, 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

Using MAMP on Mac OS X

If you use MAMP on Mac OS X, you may get problems (errors about DYLD libraries not being included) when trying to execute
''transport.core.php'' from the terminal. This is because the MAMP PHP libraries won't be on the dynamic linker path by default.

To adjust the dynamic linker library path to include the MAMP PHP libraries, run the following command via the terminal:

export DYLD_LIBRARY_PATH=/Applications/MAMP/Library/lib:$\{DYLD_LIBRARY_PATH\}

You can then execute ''transport.core.php'' by using the absolute path to the MAMP PHP executable:

/Applications/MAMP/bin/php5/bin/php transport.core.php

Troubleshooting Installation

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

PDO Error Messages

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

If this fails, then your PDO setup is not configured correctly.

Common Error Messages

Here are some common problems that might occur during installation and their solutions:

"I get a blank white screen instead of the options page!"

You probably copied config.inc.tpl to config.inc.php, which is incorrect. Make the config.inc.php file an empty, writable file.

If you renamed the config.inc.tpl to config.inc.php, rename it back to config.inc.tpl and create a blank file named config.inc.php that is writable.

"I clicked install and got a blank white screen!"

Make sure your 'memory_limit' setting in php.ini is set to at least 32M. For slower servers, you might need to up it to 64M.

"Cannot connect to database" in the database options page

One of the common causes of this problem is that you're using a non-standard port for MySQL. Try putting this syntax into the hostname field
(replacing the data with your mysql server's host and port):

my.database.com;port=3307

Warning: PDO::__construct() [pdo.--construct]: [2002] Argument invalid (trying to connect via unix://) OR "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

Resource / Elements / File tree not appearing

Additional, page "flake outs" may stem from items stored within your own browser's cache, which may result with the resource / elements / 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.

A more complete solution:

1. Under System Clear Cache


2. Under Security Flush Permissions and then Flush Sessions
3. This will dump everything and log you out
4. Last step Clear your browser cache

I can't login to the manager after installing!

If you're redirecting back to the login screen every time, try setting this in your .htaccess file in the root of your MODx install:

php_value session.auto_start 0

More common issues to come...

Still Having Issues?

If you're still having problems, post your error and your server environment information in our forums here, and we'll try and address your issue as
soon as possible.

Successful Installation, Now What Do I Do?

Creating the first page


Creating a Template

After a successful installation, you'll be presented with the Manager login page. Use the login and password you specified during the installation,
log in. You will be presented with something like this
Since Revolution RC1 doesn't come with any default content, there aren't any pages, which is why this list of error messages. Therefore, the first
thing to do is create a page to make these errors go away.

Creating the first page

This being MODx, there are several ways to create a new resource (document). The main Site menu has a "New Document" menu item, the 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!

Using MODx Revolution from SVN

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:

http://svn.modxcms.com/svn/tattoo/tattoo/branches/2.0/ - Latest 2.0 dev


http://svn.modxcms.com/svn/tattoo/tattoo/branches/2.0-ui/ - For default mgr UI/design work

Run the Build

If this is the first time you are building from SVN, copy the file build.config.sample.php to build.config.php and edit the properties to point at a 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.

Upgrading After Commits

When a commit is made to an SVN branch, one of two messages might show up in the commit.

[REBUILD/UPGRADE REQUIRED] - If your updates require a core transport rebuild (such as anything modified in the _build directory,
database model changes, or default data changes), then prefix your commit message with this. If you see this message, simply 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.

Using MAMP on Mac OS X

If you use MAMP on Mac OS X, you may get problems (errors about DYLD libraries not being included) when trying to execute
''transport.core.php'' from the terminal. This is because the MAMP PHP libraries won't be on the dynamic linker path by default.

To adjust the dynamic linker library path to include the MAMP PHP libraries, run the following command via the terminal:

export DYLD_LIBRARY_PATH=/Applications/MAMP/Library/lib:$
{DYLD_LIBRARY_PATH}

You can then execute ''transport.core.php'' by using the absolute path to the MAMP PHP executable:

/Applications/MAMP/bin/php5/bin/php transport.core.php

An Overview of MODx

What is MODx?
MODx is a Content Application Platform. What does this mean? Well, that depends on who you are:

End-Users (Average Joe)

MODx offers you a system that lets you publish your offline content onto the web in any form, shape or presence you want. It 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.

Designers (Cool Carl)

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.

Developers (Badass Billy)

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.

So What Happens on a Request?


MODx loads the requested Resource, fetches that Resource's Template, and then places the Resource's content in that Template. MODx then
parses the resulting combined content, including any tags that might be in it, in the order they are reached. From there, it outputs the response to
the user's browser.
See Also

Glossary of Revolution Terms

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

A synonym for the MODx manager interface.


Category

An optional classifying name that can be attached to any Element or PropertySet (and other objects in later versions of Revolution) that separates
it from other similar objects.

Chunk Tags

Tags in the form [[$ChunkName]] that can be used in reference to Chunks.

Component

Also called "Third-party Component", or 3PC, a Component usually provides extra functionality to MODx, usually in the form of an Add-on, 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

A specific type of Resource, usually pertaining to a normal website page.

Document Identifier

See Resource Identifier.

Extension

Also called "Core Extension". A MODx Third-party Component that modifies the MODx Core, such as a custom User or authentication class,
caching mechanisms, or context manipulation classes.

File Resolver
A type of xPDOVehicle Resolver that copies files from the source location to the target location in a Transport Package.

Language Tags

Tags in the form [[%LanguageStringKey]] that reference MODx Lexicon entries.

Lexicon

A Lexicon is a dictionary of words and phrases organized by Culture (more specific than language, i.e. en-UK) that is used to internationalize 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.

Lexicon Topic (formerly Foci)

A set of Lexicon Entries focused on a particular subject. Revolution only loads Lexicon Entries as it needs them, by their Topic, to reduce load
times.

Link Tags

Tags in the form [[~ResourceId]] that reference the URL of a particular Resource.

Manager

The back-end of the MODx interface.

Namespace

An organizational tag for Components to use to identify Lexicon Entries, Settings, and other objects related to the Component in a Revolution site.
Also specifies an absolute path in which the Component may be found.

Placeholder Tags

Tags in the form [[+PlaceholderName]] that reference MODx Placeholders, usually set with $modx->setPlaceholder('placeholderName','value') 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.

Resolver (for xPDOVehicles)

Post-processor: a script or predefined action that is evaluated after a Vehicle is installed or uninstalled. Resolvers always occur after the vehicle'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

Tags in the form [[SnippetName]], also referred to as Snippet Calls.

Static Resource

A specific type of Resource that is a direct reference to a specific file on the MODx site. The content is replaced with the contents of that file.

Symlink

A type of Resource that references a single, local MODx Resource; the Resource's content will replace the Symlink's content.

System Setting

A site-wide variable accessible to the MODx site. Can be overridden by Context Settings and User Settings.

Template Variables

Custom Resource Fields created by the user on the Document Create/Edit Screen and referenced using Content Tags.

Transport Package

A packaged and zipped collection of Transport Vehicles, that can be easily distributed ("transported") from one Core Workspace to another.

Transport Provider (formerly Provisioner)

A web service that enables remote installation of Transport Packages directly from the MODx manager application.

Transport Vehicles

An intelligent container that encapsulates any artifact that can be distributed in a Transport Package. Transport Vehicles store their payloads in a
portable format.

User Setting

A user-specific setting that either creates a new setting or overrides the similar Context Setting and System Setting. Used to provide 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.

Validator (for xPDOVehicles)

Pre-processor: a script or predefined action that executes prior to the vehicle being installed or uninstalled. If the validator returns true, 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).

Explanation of Directory Structure

The root directory of MODx is split into several subdirectories, each with its own set of responsibilities and tasks. Some of these directories can 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/

A cache of every RSS feed in MODx.

core/cache/web/

Unlike the cache in MODx Evolution, the MODx Revolution cache is split up into several parts. Every context (ie. web and mgr) has 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.

To load a lexicon, one would use a format such as this:

$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

This is a work-in-progress roadmap for MODx Revolution.

Tasks in purple are already finished in Git.

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

Fix any major issues in JIRA

Revolution 2.1

Add Custom Dashboards


Simplify setup/ process
More API methods

Revolution 2.2

Transport Package dependencies


Add Static Elements
Add Heirarchical Elements

Revolution 2.3

Permissions/ACLs for specific Users


User Group Settings

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

Making Sites with MODx

This section contains information on creating your site using MODx.

Structuring Your Site

This section contains information about MODx objects that are used in the structure of your website.

Resources

What is a Resource?
Managing Resources
Resource 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:

General Resource Fields

Name Description

id The ID of the Resource.

template A reference to the Template that this Resource is using

published If the Resource is Published, or viewable on the front-end.

pagetitle The title of the Resource.

longtitle A longer title of the Resource.

description An extended description of the Resource.

introtext Also called 'Summary', an introductory excerpt of the Resource's content. Useful for blogs or searching.

alias The URL alias to use, if your site is using Friendly URLs. A Resource with alias 'home' and Content Type 'html' would render
'home.html' if it isn't a Container.

parent The Parent Resource's ID.

menutitle The title to show for the Resource when displayed in a menu.

menuindex The order index of the Resource in a menu. Higher order means later.

hidemenu Also called 'Hide from Menus', if set, this Resource will not show in most Menu or Navigation snippets.

content The actual content of the Resource.

Settings Resource Fields

Name Description

isfolder Labeled as 'Container', this specifies whether or not the Resource renders with a / in Friendly URLs instead of its suffix.

searchable If the Resource is searchable.

cacheable If the Resource is cacheable.

createdby The ID of the user who created the Resource.

editedby The ID of the last user to edit the Resource.

deleted If the Resource is deleted or not.

deletedby The ID of the user who deleted the Resource.

publishedby The ID of the user who last published the Resource.

createdon The date the Resource was created.


publishedon The date the Resource was published.

editedon The date the Resource was last edited.

Using Resource Fields

Resource fields can be accessed from anywhere by using the Template Variable syntax, ie:

[[*pagetitle]] // renders the pagetitle.


[[*id]] // renders the Resource's ID
[[*createdby]] // renders the ID of the user who created this Resource

They can also have Output Filters applied to them:

// Renders a limited version of the introtext field.


// If it is longer than 100 chars, adds an ...
[[*introtext:ellipsis=`100`]]

// Grabs the user who last edited the Resource's username


[[*editedby:userinfo=`username`]]

// Grabs the user who published the Resource's email


[[*publishedby:userinfo=`email`]]

Accessing Resource Fields in a Snippet

Grabbing the Resource Fields in a 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:

/* output the current Resource's pagetitle */


$output = $modx->resource->get('pagetitle');
return strrev($output);

Linking to a Resource

In MODx, links to Resources are dynamically managed via "Resource Tags". They look like this:

[[~123]]

where '123' is the ID of the Resource to link to. You can put these tags anywhere, and MODx will dynamically render the URL for the Resource.

You can also get the Resource Tag by dragging a Resource from the left tree into the content panel.

Also see Named Anchor.

URL Parameters for Resource Tags

Adding URL parameters in your Resource Tag is quite simple in Revolution. Let's say we have Resource ID 42 that resolves to a URL of
'store/items.html'. We want to add a 'tag' parameter to the URL, with a value of 'Snacks' and a 'sort' parameter of 'Taste'. Here's how you'd do it:

[[~42? &tag=`Snacks` &sort=`Taste`]]

This would render as:

store/items.html?tag=Snacks&sort=Taste

Note that those are backticks instead of apostrophes.

URL Schemes in Resource Tags


You can specify the scheme for a Resource in your tag:

[[~123? &scheme=`https`]]

Would render the URL using 'https' instead of the normal http.

The available schemes are:

Name Description

https Renders the link with https instead of http

full Renders the link as an absolute URL

See Also

Content Types

What are Content Types?


Usage
Creating New Content Types
See Also

What are Content Types?

Content types are specific filesystem types for your resources. They are associated with file extensions and tell the MODx Parser what type 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.

Creating New Content Types

First, go to System -> Content Types. You'll see a grid populated with all the current Content Types. Click on 'New Content Type', and a 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

What is a Named Anchor?

A named anchor is a link to content within the current resource.

A typical named anchor will be similar to:

<a name="prohibited"></a>

Accessing the Named Anchor

To generate a link to the current Resource, while using a named anchor of "prohibited":

<a href="[[~[[*id]]]]#prohibited">Prohibited Activities</a>

To generate a link to a Resource with ID 12, while using a named anchor of "prohibited":

<a href="[[~12]]#prohibited">Prohibited Activities</a>

Static Resource

What is a Static Resource?

A Static Resource is a Resource abstraction of an actual file on the filesystem. You can point a Static Resource to any file on your webserver; this
allows you to manage these Resources externally.

Static Resources can also have tags inside their content fields to determine the path of the file - so you can specify custom paths to set as 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

Using Resource Symlinks

Understanding a Symlink

A MODx Revolution symlink simply takes the content of one resource and displays it in another. You can add a new template or what 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.

How do I use this feature?

1) Create the symlink with a name, an alias and a reference to the "master" document itself.
2) If you do not see it -- try clearing the site cache / document cache.

Example:
For my site this example would feed the contents of my college degrees page to the me.html

Why Symlink?

A document needs moved in the structure and google has it listed at the old 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

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

Page ID: [[*id]]<br/>


IntroText (Summary): [[*introtext]]<br/>
MenuTitle: [[*menutitle]]

<hr/>

[[*content]]

</body>
</html>
Note the important [[*content]] tag; this tag tells MODx where to put the Resource's content.

MODx stores template data in its database; you cannot create a template by uploading a file to the filesystem, you 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.

Using Resource Fields in the Template

As you noticed from our Template sample code above, the fields of a Resource can be referenced using the [[*fieldName]] syntax. A list 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>

Template Variables in Templates

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

That reference is then replaced with the contents of the Chunk.

You can also pass properties to a Chunk. Say you had a chunk named 'intro' with the contents:

Hello, [[+name]]. You have [[+messageCount]] messages.

You could fill those values with:

[[$intro? &name=`George` &messageCount=`12`]]

Which would output:

Hello, George. You have 12 messages.

You could even take it one step further, by adding a Template Variable that allows the user to specify their name per Resource:

[[!$intro? &name=`[[*usersName]]` &messageCount=`[[*messageCount]]`]]

or in the Chunk itself:

Hello, [[*usersName]]. You have [[*messageCount]] messages.


Processing Chunk via the API

Chunks are also frequently used to format the output of Snippets. A Chunk can be processed from a Snippet using the process() function; for
example, given the following Chunk named 'rowTpl':

<tr class="[[+rowCls]]" id="row[[+id]]">


<td>[[+pagetitle]]</td>
<td>[[+introtext]]</td>
</tr>

the following Snippet code retrieves it and processes it with an array of properties for all published Resources, and returns formatted results as a
table, setting the class to "alt" if for even rows:

$resources = $modx->getCollection('modResource',array('published' => true));


$i = 0;
$output = '';
foreach ($resources as $resource) {
$properties = $resource->toArray();
$properties['rowCls'] = $i % 2 ? '' : 'alt';

$output .= $modx->getChunk('rowTpl',$properties);
$i++;
}
return '<table><tbody>'.$output.'</tbody></table>';

Modifying a Chunk Via the API

Chunks can also be manipulated by the MODx API:

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

/* get an existing chunk and delete it from the database */


$chunk = $modx->getObject('modChunk', array('name' => 'MyObsoleteChunk'));
if ($chunk) $chunk->remove();
?>

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:

[[!Wayfinder? &startId=`0` &level=`1`]]

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

which would override the set's value on `level` of 1, setting it instead to 2.

Installing Snippets

You can also download and install Snippets via 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]].

Tag Format Changes for Content Elements and Content Tags

Content Elements Evolution (Old) Revolution (New)

Templates no tag representation no tag representation

Template Variables [*templatevar*] [[*templatevar]]

Chunks {{chunk }} [[$chunk]]

Snippets [[snippet]] [[snippet]]

Plugins no tag representation no tag representation

Modules no tag representation does not exist in Revolution


Content Tags

Placeholders [+placeholder+] [[+placeholder]]

Links [~link~] [[~link]]

System Settings [(system_setting)] [[++system_setting]]

Language no tag representation [[%language_string_key]]

Adopting this simplified format allows the new parser to be fully-recursive, following a source-order mechanism that does not depend on 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. &param=`?=&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`]]

This would output:

Hello George!

The syntax for properties follows the same syntax as 096/Evolution snippet properties.

Customizing Content

MODx offers many ways of customizing content. This section covers the ways to do so that are built into the core of MODx.

Template Variables

What is a Template Variable?


Usage
See Also
What is a Template Variable?

A Template Variable (TV) is a custom Resource Field that is created by the site developer. Imagine that you represent the pages on your site 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:

Hello [[+name]], you have [[+messageCount]] messages.

You could fill the data with the call:

[[*intromsg?name=`George` &messageCount=`123`]]

Which would output:

Hello George, you have 123 messages.

Output Filters are also great tools to be applied to TVs. Say you wanted to limit a TV's output to 100 chars. You'd simply use the 'limit' output filter:

[[*bioMessage:limit=`100`]]

See Also

Creating a Template Variable

General Settings
Rendering Options (Input Type)
Properties
Template and Resource Group Access
See Also

This page outlines how to create a Template Variable in MODx Revolution.

General Settings

First off, you'll want to specify a name for the Template Variable. TVs in MODx are case-sensitive, so you'll want to be careful about the name 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

We can then also specify a default value for the TV as well.

Next, we'll select the output rendering options. We'll select 'Date' as well, and as you'll note, below this box (depending on the Output Render
selected) some form fields will show:

Allowing us to edit more fine-grained options for that Output Render.

Properties

From there, we can specify any default properties we want for the TV. "How can you use properties on a TV?", you might ask. Well, let's say we'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]]

This would output in the place of the TV:

Viewing: Cars

Template and Resource Group Access

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

What are @ Bindings?

In the context to Template Variables, a Data Source is the location of the information to be displayed. A Data source can come from any of the
following sources:

an externally generated file that is sent via FTP to the server


a Database table accessible to MODx
a Resources in the resource tree
a Chunk in the Elements tree
the result of an evaluated PHP script

These Data Sources can be tied (or "bound") to a Template Variable for formatting and displaying in document. In addition, the bound data in 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

What is the @CHUNK Binding?

The @CHUNK Binding returns the parsed content of any specified Chunk.

Syntax

@CHUNK chunk_name

Binds the variable to a document. Where chunk_name is the name of the chunk. The returned value is a string containing the content of 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

What is the @DIRECTORY Binding?

The DIRECTORY binding reads the contents of a directory. This can really useful when you tie it into a List control widget, e.g. if you want to 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:

<img src="[[*myTV]]" alt="" />

Additional Info

Can you filter which files are selected? E.g. using *.jpg? The following DOES NOT WORK:

@DIRECTORY /list/*.jpg # doesn't work!

There are PHP code snippets out there that emulate this functionality. See the following forum thread:
http://modxcms.com/forums/index.php/topic,3124.0.html

Security

Depending on how the file is used on the page, it may pose a security risk. Be careful if you were using this binding to select JavaScript files to 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

What is the @EVAL Binding?

The @EVAL Binding executes the specified PHP code. It should be used with careful security precautions.

Syntax

@EVAL php_code_here
Usage

Simply put a PHP statement after the @EVAL tag:

@EVAL return "The time stamp is now ".time();

@EVAL $a = 'dog'; return $a;

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

What is the @FILE Binding?

The @FILE Binding returns the contents of any specified file.

Syntax

@FILE file_path

Binds the variable to a file, where 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:

Unable to render embedded object: File (filebinding.png) not found.

And viola! We have our dynamically rendering @FILE binding.

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

What is the @RESOURCE Binding?

The @RESOURCE Binding returns the parsed contents of any specified Resource.

Syntax

@RESOURCE resource_id

Binds the variable to a Resource, where resource_id is the ID of the Resource. The returned value is a string containing the parsed content of the
Resource.

Usage

To output the contents of a Resource with ID of 12:

@RESOURCE 12

See Also

Template Variables
Bindings

SELECT Binding

What is the @SELECT Binding?

The @SELECT binding calls a database query based on the provided value and returns the result.

Syntax

@SELECT * FROM site_content

To write one of these, you need to be familiar with 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:

add an '@' symbol in front of your SELECT


omit the final semi-colon and any comments
put the query on a single line (no returns!)
You can place this binding in one of two places:

On a page (edit the page in the manager). The page where you want to put this binding must be using a valid template, and that 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.

REMEMBER: The query must be on ONE LINE. No 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.

More Complex Example: Template Variables

What if you need to write a query that accesses the template variables associated with a particular page? Those variables aren't directly stored 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`,

FROM site_content as page


JOIN site_tmplvar_contentvalues as tv_val ON page.id=tv_val.id
WHERE
page.parent='95'
AND tv_val.tmplvarid='6' /* 6 is the opening_date */
AND DATE_FORMAT(STR_TO_DATE(tv_val.value, '%d-%m-%Y %H:%i:%s'), '%Y-%m-%d %H:%i:%s')>'2008-10-24
13:04:57'
;
MODx uses the MyISAM table engine, not InnoDB, so it does not rigidly enforce the foreign key constraints that are inferred by
the table structure.

Errors

What if your MySQL statement executes perfectly, but once you put it in your SELECT binding, it fails? Well, there are some pit-falls. 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.

Next Step: Formatting

Ok, so you can return a bunch of data from the database... now what? If you need to format it intelligently, you might get some mileage out of 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

Template Variable Output Types

Output Types for TVs

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

Date TV Output Type

Date TV Output Type

This type allows you to output any TV input as a Date, formatted in the way you want.

Output Properties

It's output properties look like:


Name Description

Date Format A format string similar to the PHP strftime method.

Default If no value is set for the TV, use the current time? This defaults to 'no', which will output a blank value.

See Also

Delimiter TV Output Type

Delimiter TV Output Type

This type allows you to output any TV input as a delimited list. It's very useful for TV inputs that store multiple values, such as the multiple 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

It's output properties look like:

Name Description

Delimiter A delimiter to separated each item with.

See Also

HTML Tag TV Output Type

HTML Tag TV Output Type

This type allows you to wrap an HTML tag around the input.

Output Properties

It's output properties look like:


Name Description

Tag Name The tag to create, such as div, img, span, etc.

Tag ID The dom ID of the tag.

Class Any CSS classes to add to the tag.

Style Any style attributes to add to the tag.

Attributes Any other attributes you want to add to the tag.

See Also

Adding a Custom TV Input Type

What are Custom TV Input Types?

MODx Revolution allows you to create your own custom TV input types (similar to the textbox, radio, textarea, richtext, etc types already
available) for your Template Variables.

Creating the Files

To create a custom TV input type (let's say, one called "test"), you need a few things. Let's say my "test" TV input type loads a Template selecting
combobox.

I'd first need 2 files:

An input controller - put here: core/model/modx/processors/element/tv/renders/mgr/input/test.php


An input template - put here: manager/templates/default/element/tv/renders/input/test.tpl

The input controller, test.php, would have:

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

And there you go! A custom TV input type.

You don't have to use the ExtJS code as shown here to have a custom input type. It could even just be a straight HTML input.
It's really up to you.

See Also

Adding a Custom TV Output Type

What are TV Output Types?


Creating the Files
Setting up the Input Properties Controller
Setting up the Input Properties Template
Setting up the Output Controller
Using the Custom TV Output Type
See Also

What are TV Output Types?

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.

Creating the Files

Let's create a custom TV Output Type called "button". This will render an input button (or more than one) with a specified value and an optional
name, with some other fields for attributes. You'll need 3 files:

An input properties controller - put here: core/model/modx/processors/element/tv/renders/mgr/properties/button.php


An input properties template - put here: manager/templates/default/element/tv/renders/properties/button.tpl
An output controller - put here: core/model/modx/processors/element/tv/renders/web/output/button.php

Setting up the Input Properties Controller

This is the PHP file that will load the mgr template for managing the TV output type's properties. We'll have it contain just this:
<?php
// any custom php you want to run here
return $modx->smarty->fetch('element/tv/renders/properties/button.tpl');

Setting up the Input Properties Template

This is the template for the default manager theme to render properties with. We'll use ExtJS to render some pretty form fields:

<div id="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.

Setting up the Output Controller

Now we get into the good stuff. This controller will handle exactly how the button is outputted. Our file looks like this (comments inline):

<?php
$o= '';
$buttons= $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];

/* separate the attributes into html tag format */


foreach ($attr as $k => $v) $attributes.= ($v ? ' '.$k.'="'.$v.'"' : '');
$attributes .= ' '.$params['attrib'];

/* Output the image with attributes */


$o .= '<button'.rtrim($attributes).'>'.$text.'</button>'."\n";
}
}

return $o;

Using the Custom TV Output Type

So, how does it look? Well, it should render an output form like this when editing the TV - I've added some custom values to it as well:

So we'll save this, and then let's go edit in in a Resource. We'll specify two buttons, separating with ||. We could also just do one button. And, we'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:

And there you go! A custom TV output type!

See Also

Properties and Property Sets

What are Properties?


What are Property Sets?
Assigning Property Sets to Elements
Creating Properties in a Property Set
Importing and Exporting Properties
Using Properties Programmatically
Using getOption
Conclusion

What are Properties?

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.

What are Property Sets?

Property Sets are user-defined collections of properties for an Element. They can be attached to one, or more, Elements via that Element's 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:

Default Element Properties -> Property Set -> Tag-defined Properties

So, if the default property of 'user' was 0, then it would then be set to 2 by the property set, and then to 1 by the call.

The property set can also contain properties not defined in either the default element properties, or in the tag call. This can be useful to 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.

Assigning Property Sets to Elements


Property Sets can only be used on Elements that they are assigned to. This can be done via either the element's properties grid, or Tools ->
Property Sets.

For example, here's an image of a property set named 'TestPropertySet' in a snippet's editing page:

As you can see here, there is a property set loaded with some properties. Properties in green are default properties that have been overridden 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.

Creating Properties in a Property Set

To create a Property in a Property Set, you'll simply need to just load the Property Set you want to work on, and then click "Create Property". 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.

Importing and Exporting Properties

You can also import and export properties using the grid. Simply click on the corresponding buttons at the bottom.

When you import properties, it will overwrite your properties in the grid currently. Make sure that you want to do this before
importing!

Using Properties Programmatically


Properties are available in a snippet via the $scriptProperties array:

$prop = $scriptProperties['propertyName'];

Note that if a parameter is sent in the snippet call that has the same name as a property in a property set, the parameter value will override the
default value in the property set.

Using getOption

You can also get a snippet property with $modx->getOption() like this:

$modx->getOption('propertyName', $scriptProperties, 'default');

Conclusion

Input and Output Filters

What are Filters?


Input Filters
Output Filters
Examples
See Also

What are Filters?

Filters in Revolution allow you to manipulate the way data is presented or parsed in a tag. They allow you to modify values from inside your
templates.

Input Filters

Currently input filters process tag calls. More documentation to come.

Output Filters

In Revolution, Output Filters behave similarly to PHx calls in MODx Evolution - except they're built into the core. The syntax is like such:

[[element:modifier=`value`]]

They can also be chained (executed left to right):

[[element:modifier:anothermodifier=`value`:andanothermodifier:yetanother=`value2`]]

The list of string modifiers:

Modifier Description Example

cat Appends the option's value (if not empty) to the input value [[+numbooks:cat=` books`]]

lcase Similar to PHP's strtolower [[+title:lcase]]

ucase Similar to PHP's strtoupper [[+headline:ucase]]

ucwords Similar to PHP's ucwords [[+title:ucwords]]

ucfirst Similar to PHP's ucfirst [[+name:ucfirst]]

htmlent Similar to PHP's htmlentities. Uses the current value the system setting [[+email:htmlent]]
"modx_charset"

esc,escape Safely escapes character values [[+email:escape]]


strip Replaces all linebreaks, tabs and multiple spaces with just one space [[+textdocument:strip]]

stripString Strips string of specified value [[+name:stripString=`Mr.`]]

replace Replaces one value with another [[+pagetitle:replace=`Mr.==Mrs.`]]

stripTags,notags Similar to PHP's strip_tags [[+code:strip_tags]]

len,length Similar to PHP's strlen [[+longstring:strlen]]

reverse Similar to PHP's strrev [[+mirrortext:reverse]]

wordwrap Similar to PHP's 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.

limit Limits a string to a certain number of characters. Defaults to 100. [[+description:limit=`50`]]

ellipsis Adds an ellipsis to and truncates a string if it's longer than a certain number of [[+description:ellipsis=`50`]]
characters. Defaults to 100.

tag Displays the raw element without the :tag. Useful for documentation. [[+showThis:tag]]

math Returns the result of an advanced calculation (expensive on processor. not


recommended)

add,increment,incr Returns input incremented by option (default: +1) [[+downloads:incr]]


[[+blackjack:add=`21`]]

subtract,decrement,decr Returns input decremented by option (default: -1) [[+countdown:decr]]


[[+moneys:subtract=`100`]]

multiply,mpy Returns input multiplied by option (default: *2) [[+trifecta:mpy=`3`]

divide,div Returns input divided by option (default: /2) Does not accept 0. [[+rating:div=`4`]]

modulus,mod Returns the option modulus on input (default: %2, returns 0 or 1) [[+number:mod]]

ifempty,default,empty Returns the input value if empty [[+name:default=`anonymous`]]

notempty Returns the input value if not empty [[+name:notempty=`Hello


[[+name]]!`]]

nl2br Similar to PHP's nl2br [[+textfile:nl2br]]

date Similar to PHP's strftime. Value is format. [[+birthyear:date=`%Y`]]

strtotime Similar to PHP's strtotime. Takes in a date. [[+thetime:strtotime]]

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.

md5 Similar to PHP's md5. [[+password:md5]]

cdata Wraps the text with CDATA tags [[+content:cdata]]

userinfo Returns the requested user data. The element must be a modUser ID. The value [[+userId:userinfo=`username`]]
field is the column to grab.

isloggedin Returns true if user is authenticated in this context.

isnotloggedin Returns true if user is not authenticated in this context.

urlencode Similar to PHP's urlencode [[+mystring:urlencode]]

urldecode Similar to PHP's urldecode [[+myparam:urldecode]]

Examples

A good example of chaining would be to format a date string to another format, like so:
[[+mydate:strtotime:date=`%Y-%m-%d`]]

Directly accessing the modx_user_attributes table in the database using filters instead of a Snippet can be accomplished simply by utilizing the
userinfo filter. Select the appropriate column from the table and link to it, like so:

User Internal Key: [[+userId:userinfo=`internalKey`]]<br />


User name: [[+userId:userinfo=`username`]]<br />
Full Name:[[+userId:userinfo=`fullname`]]<br />
Role: [[+userId:userinfo=`role`]]<br />
E-mail: [[+userId:userinfo=`email`]]<br />
Phone: [[+userId:userinfo=`phone`]]<br />
Mobile Phone: [[+userId:userinfo=`mobilephone`]]<br />
Fax: [[+userId:userinfo=`fax`]]<br />
Date of birth: [[+userId:userinfo=`dob`:date=`%Y-%m-%d`]]<br />
Gender[[+userId:userinfo=`gender`]]<br />
Country: [[+userId:userinfo=`country`]]<br />
State: [[+userId:userinfo=`state`]]<br />
Zip Code: [[+userId:userinfo=`zip`]]<br />
Photo: [[+userId:userinfo=`photo`]]<br />
Comment: [[+userId:userinfo=`comment`]]<br />
Password: [[+userId:userinfo=`password`]]<br />
Cache Password: [[+userId:userinfo=`cachepwd`]]<br />
Last Login: [[+userId:userinfo=`lastlogin`:date=`%Y-%m-%d`]]<br />
The Login:[[+userId:userinfo=`thislogin`:date=`%Y-%m-%d`]]<br />
Number of Logins: [[+userId:userinfo=`logincount`]]

Note that the user ID and username is already available by default in MODx, so you dont need to use the "userinfo" filter:

[[+modx.user.id]] - Prints the ID


[[+modx.user.username]] - Prints the username

Also, 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`]]

This will pass these properties to the snippet:

Param Value Example Result

input The element's value. The value of [[+file]]

options Any value passed to the modifier. 'notitle'

token The type of the parent element. + (the token on `file`)

name The name of the parent element. file

tag The complete parent tag. [[+file:makeDownloadLink=`notitle`]]

And then the return value of that call would be whatever the snippet returns.

See Also

Properties and Property Sets


Templates
Template Variables
Snippets

Administering Your Site


This section contains information about maintaining and administering your MODx site.

Settings

What are Settings?


Settings are site-wide variables that can be used by either the MODx Core or by 3rd-Party Components to provide site, context, or user-level
customization.

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:

System Setting -> Context Setting -> User Setting

Let's say I set the System Setting named 'use_editor' to 0. However, I created a Context Setting called 'use_editor' for the 'mgr' context and set 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.

Retrieving Settings in PHP


Getting settings is simple in MODx Revolution; simply use getOption. For example, to get the setting 'site_start', simply:

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

Default values with getOption

getOption supports 3 parameters:

1. The key of the setting


2. An array to search first before looking for the setting
3. A default value should the setting not be found.

So, for example, if I were in a Snippet and wanted some default properties at the top, I could use getOption. 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

A description of each setting follows:

allow_duplicate_alias

allow_duplicate_alias

Name: Allow Duplicate Aliases


Type: Yes/No
Default: No

If set to 'yes', this will allow duplicate aliases to be saved.

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

Name: Allow Duplicate Emails for Users


Type: Yes/No
Default: Yes

If enabled, Users may share the same email address.

allow_tags_in_post
allow_tags_in_post

Name: Allow Tags in Post


Type: Yes/No
Default: Yes

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

Name: Automatic Check for Package Updates


Type: Yes/No
Default: Yes

If 'Yes', MODx will automatically check for updates for packages in Package Management. This may slow the loading of the Packages grid.

auto_check_pkg_updates_cache_expire

auto_check_pkg_updates_cache_expire

Name: Cache Expiration Time for Automatic Package Updates Check


Type: Number
Default: 15

The number of minutes that Package Management will cache the results when checking for package updates.

auto_menuindex

auto_menuindex

Name: Menu Indexing Default


Type: Yes/No
Default: Yes

Select 'Yes' to turn on automatic menu index incrementing by default.

blocked_minutes

blocked_minutes

Name: Blocked Minutes


Type: Number
Default: 60

The number of minutes that a user will be blocked for if they reach their maximum number of allowed failed login attempts.

cache_action_map

cache_action_map

Name: Enable Action Map Cache


Type: Yes/No
Default: Yes

When enabled, actions (or controller maps) will be cached to greatly reduce manager page load times.
cache_context_settings

cache_context_settings

Name: Enable Context Setting Cache


Type: Yes/No
Default: Yes

When enabled, context settings will be cached to reduce load times.

cache_db

cache_db

Name: Enable Database Cache


Type: Yes/No
Default: No

When enabled, objects and raw result sets from SQL queries are cached to significantly reduce database loads.

This can take up a good amount of hard drive space. Only do this if you have enough space to scale the requests.

cache_db_expires

cache_db_expires

Name: Expiration Time for DB Cache


Type: Number
Default: 0

Default time for the expiration of the database cache. If set to '0', the cache will never expire unless a record is updated.

cache_default

cache_default

Name: Cacheable Default


Type: Yes/No
Default: Yes

Makes all new resources cacheable by default.

cache_disabled

cache_disabled

Name: Disable Global Cache Options


Type: Yes/No
Default: No

If true, disables all MODx caching features.

This feature is experimental. MODx recommends not turning off caching site-wide, as it can significantly slow down your site.

cache_handler

cache_handler

Name: Cache Handler Class


Type: String
Default: xPDOFileCache

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

Name: Cache JSON Data


Type: Yes/No
Default: No

If Yes, caching will be stored JSON format.

cache_json_expires

cache_json_expires

Name: Expiration Time for JSON Cache


Type: Number
Default: 0

Expiration time for JSON format cached data. A value of zero means the cache never expires.

cache_lexicon_topics

cache_lexicon_topics

Name: Cache Lexicon Topics


Type: Yes/No
Default: Yes

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

Name: Cache Non-Core Lexicon Topics


Type: Yes/No
Default: Yes

When disabled, non-core Lexicon Topics will be not be cached. This is useful to disable when developing your own Extras.

cache_resource

cache_resource

Name: Enable Partial Resource Cache


Type: Yes/No
Default: Yes

Partial Resource caching is configurable by resource when this feature is enabled. Disabling this feature will disable it globally.

cache_resource_expires

cache_resource_expires

Name: Expiration Time for Partial Resource Cache


Type: Number
Default: 0

Expiration time for the Partial Resource Cache setting. A value of 0 means the cache will never expire.
cache_scripts

cache_scripts

Name: Enable Script Cache


Type: Yes/No
Default: Yes

When enabled, MODx will cache all Scripts (Snippets and Plugins) to file to reduce load times. MODx recommends leaving this set to 'Yes'.

compress_css

compress_css

Name: Use Compressed CSS Stylesheets


Type: Yes/No
Default: Yes

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

Name: Use Compressed Javascript Libraries


Type: Yes/No
Default: Yes

When this is enabled, MODx will use a compressed version of its custom JavaScript libraries. This greatly reduces load and execution time.
Disable only if you are modifying core elements.

This setting will not work in Git deployments of Revolution. Leave at 'No'.

concat_js

concat_js

Name: Use Concatenated Javascript Libraries


Type: Yes/No
Default: Yes

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

A container suffix of / will render a Resource with an alias of 'test' as:

www.mysite.com/test/

whereas a container suffix of '.html' will render:

www.mysite.com/test.html

cultureKey

cultureKey

Name: Language
Type: String
Default: en

Select the language for all non-manager Contexts, including web.

custom_resource_classes

custom_resource_classes

Name: Custom Resource Classes


Type: String
Default:

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

Name: Default Template


Type: Template
Default: 1

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

Name: Path to CSS file


Type: String
Default:

The path to your CSS file that you wish to use within the editor. The best way to enter the path is to enter the path from the root of your server, 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

Name: CSS Selectors for Editor


Type: String
Default:

emailsender

emailsender

Name: Site Admin Email Address


Type: String
Default: Set at Install

The e-mail address used when sending users their usernames and passwords.

emailsubject

email_subject

Name: Email Subject


Type: String
Default: Your login details

The subject line for the default signup email.

error_page

error_page

Name: Error Page


Type: Number
Default: 1

The ID of the resource you want to send users to if they request a Resource which doesn't actually exist.

failed_login_attempts

failed_login_attempts

Name: Failed Login Attempts


Type: Number
Default: 3

The number of times a user can attempt to login before being blocked.

fe_editor_lang

fe_editor_lang

Name: Front-end Editor Language


Type: String
Default: english

The language for the editor to use when used as a front-end editor.

This setting is deprecated and no longer in use.

feed_modx_news

feed_modx_news

Name: MODx News Feed URL


Type: String
Default: http://feeds.feedburner.com/modx-announce

Set the URL for the RSS feed for the MODx News panel in the manager.

feed_modx_news_enabled

feed_modx_news_enabled

Name: Enable MODx News Feed


Type: Yes/No
Default: Yes

If 'No', MODx will hide the News feed in the welcome section of the manager.

feed_modx_security

feed_modx_security

Name: MODx Security Feed URL


Type: String
Default: http://feeds.feedburner.com/modxsecurity

Set the URL for the RSS feed for the MODx Security panel in the manager.

feed_modx_security_enabled

feed_modx_security_enabled

Name: Enable MODx Security Feed


Type: Yes/No
Default: Yes

If 'No', MODx will hide the Security feed in the welcome section of the manager.

filemanager_path

filemanager_path

Name: File Manager Path


Type: string
Default:

Determines the root of the file browser for the currently-logged in user in the manager backend.

Usage

Specify either a relative path from the MODx root directory, or an absolute path. It can be outside of the webroot.

friendly_alias_urls

friendly_alias_urls

Name: Use Friendly URL Aliases


Type: Yes/No
Default: No

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

Name: Friendly URL Prefix


Type: String
Default:

This setting is deprecated and is no longer available in MODx Revolution.

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

Name: Friendly URL Suffix


Type: String
Default: .html

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

Name: Use Friendly URLs


Type: Yes/No
Default: No

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

Name: Mail Charset


Type: String
Default: UTF-8

The (default) charset for e-mails, e.g. 'iso-8859-1' or 'UTF-8'.

mail_encoding

mail_encoding

Name: Mail Encoding


Type: String
Default: 8bit
Sets the Encoding of the message. Options for this are "8bit", "7bit", "binary", "base64", and "quoted-printable".

mail_smtp_auth

mail_smtp_auth

Name: Use SMTP Authentication


Type: Yes/No
Default: No

Sets SMTP authentication. Utilizes the mail_smtp_user and mail_smtp_pass settings.

mail_smtp_helo

mail_smtp_helo

Name: SMTP Helo Message


Type: String
Default:

Sets the SMTP HELO of the message (Defaults to the hostname).

mail_smtp_hosts

mail_smtp_hosts

Name: SMTP Hosts


Type: String
Default: localhost

Sets the SMTP hosts. All hosts must be separated by a semicolon. You can also specify a different port for each host by using this format:
hostname:port (e.g. "smtp1.example.com:25;smtp2.example.com"). Hosts will be tried in order.

mail_smtp_keepalive

mail_smtp_keepalive

Name: SMTP User


Type: Yes/No
Default: No

Prevents the SMTP connection from being closed after each mail sending. Not recommended.

mail_smtp_pass

mail_smtp_pass

Name: SMTP Password


Type: String
Default:

The password to authenticate to SMTP against.

mail_smtp_port

mail_smtp_port

Name: SMTP Port


Type: String
Default: 587

Sets the default SMTP server port.

mail_smtp_prefix
mail_smtp_prefix

Name: SMTP Connection Prefix


Type: String
Default:

Sets connection prefix. Options are "", "ssl" or "tls".

mail_smtp_single_to

mail_smtp_single_to

Name: SMTP Single-To


Type: Yes/No
Default: No

Provides the ability to have the TO field process individual emails, instead of sending to entire TO addresses.

mail_smtp_timeout

mail_smtp_timeout

Name: SMTP Timeout


Type: Number
Default: 10

Sets the SMTP server timeout in seconds. This function will not work in win32 servers.

mail_smtp_user

mail_smtp_user

Name: SMTP User


Type: String
Default:

The user to authenticate to SMTP against.

mail_use_smtp

mail_use_smtp

Name: Use SMTP in Mail


Type: Yes/No
Default: No

If true, MODx will attempt to use SMTP in mail functions.

manager_date_format

manager_date_format

Name: Manager Date Format


Type: String
Default: Y-m-d

The format string, in PHP date() format, for the dates represented in the manager.

manager_direction

manager_text_direction

Name: Manager Text Direction


Type: String
Default: ltr
The direction that the text will be rendered in the Manager, left to right or right to left.

manager_lang_attribute

manager_lang_attribute

Name: Manager HTML and XML Language Attribute


Type: String
Default: en

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

Name: Language for the Manager


Type: String
Default: en

The language for the MODx Manager interface. This will override any cultureKey setting for the mgr context.

manager_theme

manager_theme

Name: Manager Theme


Type: String
Default: default

The current Theme for the backend Manager.

manager_time_format

manager_time_format

Name: Manager Time Format


Type: String
Default: g:i a

The format string, in PHP date() format, for the time settings represented in the manager.

modx_charset

modx_charset

Name: Character Encoding


Type: string
Default: UTF-8

The character encoding used. Please note that MODx primarily works with UTF-8.

new_file_permissions

new_file_permissions

Name: New File Permissions


Type: string
Default: 0644

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

Name: New Folder Permissions


Type: String
Default: 0755

When creating a new folder in the File Manager, the File Manager will attempt to change the folder permissions to those entered in this setting.
This may not work on some setups, such as IIS, in which case you will need to manually change the permissions.

password_generated_length

password_generated_length

Name: Password Auto-Generated Length


Type: Number
Default: 8

The length of the auto-generated password for a User.

password_min_length

password_min_length

Name: Password Minimum Length


Type: Number
Default: 8

The minimum length in characters for a password for a User.

phpthumb_cache_maxage

phpthumb_cache_maxage

Name: phpThumb Max Cache Age


Type: Number
Default: 30 (days)

Delete cached thumbnails that have not been accessed in more than X days.

phpthumb_cache_maxfiles

phpthumb_cache_maxfiles

Name: phpThumb Max Cache Files


Type: Number
Default: 10000 (10k files)

Delete least-recently-accessed thumbnails when cache has more than X files.

phpthumb_cache_maxsize

phpthumb_cache_maxsize

Name: phpThumb Max Cache Size


Type: Number
Default: 100 (mb)

Delete least-recently-accessed thumbnails when cache grows bigger than X megabytes in size.

phpthumb_cache_source_enabled

phpthumb_cache_source_enabled

Name: phpThumb Cache Source Files


Type: Yes/No
Default: No

Whether or not to cache source files as they are loaded. Recommended to leave at No.

phpthumb_far

phpthumb_far

Name: phpThumb Force Aspect Ratio


Type: Yes/No
Default: No

The default far setting for phpThumb when used in MODx. Defaults to C to force aspect ratio toward the center.

phpthumb_zoomcrop

phpthumb_zoomcrop

Name: phpThumb Zoom-Crop


Type: String
Default: 0

The default zc setting for phpThumb when used in MODx. Defaults to 0 to prevent zoom cropping.

proxy_auth_type

proxy_auth_type

Name: Proxy Authentication Type


Type: String
Default: BASIC

Supports either BASIC or NTLM.

proxy_host

proxy_host

Name: Proxy Host


Type: String
Default:

The hostname of your proxy server. If this is specified, MODx will attempt to use the proxy when connecting to the RSS feeds or Package
Management.

proxy_password

proxy_password

Name: Proxy Password


Type: String
Default:

The password to authenticate with on your proxy server.

proxy_port

proxy_port

Name: Proxy Port


Type: String
Default:

The port for your proxy server.


proxy_username

proxy_username

Name: Proxy Username


Type: String
Default:

The username to authenticate against with your proxy server.

publish_default

publish_default

Name: Published Default


Type: Yes/No
Default: No

If true, makes all new resources published by default.

rb_base_dir

rb_base_dir

Name: Resource Path


Type: String
Default:

The physical path to the resource directory. This setting is usually automatically generated. If you're using IIS, however, MODx may not be able 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).

This setting is deprecated in MODx Revolution. Please use filemanager_path instead.

rb_base_url

rb_base_url

Name: Resource Browser Base URL


Type: String
Default:

Enter the virtual path to resource directory. This setting is usually automatically generated. If you're using IIS, however, MODx may not be able 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).

This setting is deprecated in MODx Revolution. Please use filemanager_path instead.

request_controller

request_controller

Name: Request Controller Filename


Type: String
Default: index.php

The filename of the main request controller from which MODx is loaded. Most users can leave this as index.php.

request_param_alias
request_param_alias

Name: Request Alias Parameter


Type: String
Default: q

The name of the GET parameter to identify Resource aliases when redirecting with FURLs.

request_param_id

request_param_id

Name: Request ID Parameter


Type: String
Default: q

The name of the GET parameter to identify Resource IDs when not using FURLs.

search_default

search_default

Name: Published Default


Type: Yes/No
Default: Yes

If true, makes all new resources searchable by default.

server_offset_time

server_offset_time

Name: Server Offset Time


Type: Number
Default: 0

Select the number of hours time difference between where you are and where the server is.

server_protocol

server_protocol

Name: Server Protocol


Type: String
Default: http

If your site is on a normal http or secure https connection.

session_cookie_domain

session_cookie_domain

Name: Session Cookie Domain


Type: String
Default: localhost

Use this setting to customize the session cookie domain.

This setting isn't in MODx by default, as it's best to let PHP calculate this on its own. Only set this if you are sure of what you are
doing.

session_cookie_lifetime
session_cookie_lifetime

Name: Session Cookie Lifetime


Type: Number
Default: 604800

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

Name: Session Cookie Path


Type: String
Default: /

Use this setting to customize the cookie path for identifying site specific session cookies.

This setting isn't in MODx by default. It's best to let PHP figure this out on its own. Only set this if you know what you are doing.

session_cookie_secure

session_cookie_secure

Name: Session Cookie Secure


Type: Yes/No
Default: No

Enable this setting to use secure session cookies.

session_handler_class

session_handler_class

Name: Session Handler Class


Type: String
Default: modSessionHandler

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

Name: Session Name


Type: String
Default: modxcmssession

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

The current version of MODx.

Do not change this value! Changing it could seriously affect your system.

signupemail_message

signupemail_message

Name: Signup Email Message


Type: String
Default:

Hello [[+uid]]

Here are your login details for [[+sname]] Content Manager:

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:

[[+sname]] - Name of your web site


[[+saddr]] - Your web site email address
[[+surl]] - Your site url
[[+uid]] - User\'s Login name or id
[[+pwd]] - User\'s password
[[+ufn]] - User\'s full name.

Leave the [[+uid]] and [[+pwd]] in the e-mail, or else the username and password won't be sent in the mail and your users won't
know their username or password!

site_name

site_name

Name: Site Name


Type: String
Default: MODx Revolution

The name of your MODx site.

site_start
site_start

Name: Site Start


Type: Number
Default: 1

The ID of the resource you want to use as your homepage.

Make sure the ID you enter belongs to an existing Resource that has been published!

site_status

site_status

Name: Site Status


Type: Yes/No
Default: Yes

If false, your visitors will see the 'Site unavailable message', and won't be able to browse the site.

Note that users with the 'view_offline' Permission (such as users in the Administrator group) will still be able to browse the site in offline mode.

site_unavailable_message

site_unavailable_message

Name: Site Unavailable Message


Type: String
Default:

Message to show when the site is offline or if an error occurs.

This message will only be displayed if the site_status option is set to No.

site_unavailable_page

site_unavailable_page

Name: Unavailable Page


Type: Number
Default: 1

Enter the ID of the Resource you want to use as an offline page here.

Make sure this ID you enter belongs to an existing resource that has been published!

strip_image_paths

strip_image_paths

Name: Rewrite Browser Paths


Type: Yes/No
Default: Yes
If this is set to 'No', MODx will write file browser resource src's (images, files, flash, etc.) as absolute URLs. Relative URLs are helpful should you
wish to move your MODx install, e.g., from a staging site to a production site. If you have no idea what this means, it's best just to leave it set to
'Yes'.

This setting is deprecated in MODx Revolution and is no longer in use.

tree_root_id

tree_root_id

Name: Tree Root ID


Type: String
Default:

Set this to a comma-separated list of valid IDs of Resources to start the left Resource tree at below those nodes as the root. The user will only be
able to see Resources that are children of those specified Resources.

udperms_allowroot

udperms_allowroot

Name: Allow Root Resource Creation


Type: Yes/No
Default: No

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

Name: Unauthorized Page


Type: Number
Default: 1

The ID of the resource you want to send users to if they have requested a secured or unauthorized resource.

This will only work if you have a 'load' Permission (via a Load policy or custom policy) set to the context that the Resource being accessed is in.

Make sure the ID you enter belongs to an existing resource that has been published and is publicly accessible!

upload_maxsize

upload_maxsize

Name: Maximum Upload Size


Type: Number
Default: 1048576

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

Name: Use Friendly Alias Path


Type: Yes/No
Default: No

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

Name: Use File Browser


Type: Yes/No
Default: Yes

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

Name: Enable RichText Editor


Type: Yes/No
Default: Yes

If Yes, will enable any installed Rich Text Editor in the manager.

use_multibyte

use_multibyte

Name: Use Multibyte Extension


Type: Yes/No
Default: Determined at Install

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

Name: Show Welcome Screen


Type: Yes/No
Default: No

If set to true, the welcome screen will show on the next successful loading of the welcome page, and then not show after that.

which_editor

which_editor

Name: RichText Editor to Use


Type: Richtext Combo
Default:

Selects which rich text editor to use when editing Resources. You can download and install additional Rich Text editors from Package
Management.

which_element_editor

which_element_editor

Name: Which Element Richtext Editor


Type: Richtext Combo
Default:

Select which RTE you would like to use when editing an Element.

Using Friendly URLs

Summary:
You can have firendly URLs fully functioning in under two minutes by following a simple three step process:

1) Working .htaccess sample

MODx supplies an ht.access file for you to edit to match your server settings. This file will need to renamed to .htaccess when uploaded to 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:

1. change the DOMAINNAME to your domain name in lowercase.


For me: shawnwilkerson
2. change the TLD to the com, net, org, info, whatever you have
For me: com

# Friendly URLs Part


RewriteEngine On
RewriteBase /
RewriteCond %{HTTP_HOST} .
# Force all pages to go to www.domain.com for SEO
RewriteCond %{HTTP_HOST} !^www\.DOMAINNAME\.TLD [NC]
RewriteRule (.*) http://www.DOMAINANME.TLD/$1 [R=301,L]
# Friendly URLs
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php?q=$1 [L,QSA]
# Additional Settings Follow
ExpiresActive On
ExpiresByType image/gif A2592000
ExpiresByType image/jpeg A2592000
ExpiresByType image/png A2592000
BrowserMatch "MSIE" brokenvary=1
BrowserMatch "Mozilla/4.[0-9]{2}" brokenvary=1
BrowserMatch "Opera" !brokenvary
SetEnvIf brokenvary 1 force-no-vary

/htdocs is fine or /public_html or what ever. your server uses


The .htacess has to be at the same level as the main index.php or above the document MODx is presenting.

Be aware some control panels like to write their own .htaccess just above the site level so the best place to put it is where 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.

2) Configure MODx Revolution

Next change the settings in the Friendly URLs Area of the System Settings in the System menu of the Revolution Manager to something like 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.

3) Edit your template(s)

Make sure you have the following between the <head></head> tags:

<base href="[[!++site_url]]" />

4) Clear the MODx Revolution cache

And you're done!

Contexts

Contexts allow MODx configuration settings to be overridden, extended, isolated, or shared across domains, sub-domains, sub-sites, multi-sites,
cultural-specific sections, specific web applications, etc.

You can easily create a context from the Contexts menu under Tools. The context will then show up in your resource tree. Resources can 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

Creating a Subdomain from a Folder using Virtual Hosts


This case study will show an example usage of Contexts to create a separate subdomain manageable in a single Revolution install. We're going
to create a subdomain at dev.modxcms.com (of course, not really, but you get the idea).

Creating the Context

First off, go to System -> Contexts. Then, create a new context. We'll call it "dev".

Go ahead and create a "Dev Home" Resource in the Resource tree to the left. Place it in the root of the "dev" context. Also, while you're at it,
create a "Dev Docs" Resource as well with an alias of "documentation". We'll use this to test our context links.

Your tree should look something like this:

From there, go ahead and right-click on the "dev" Context in the tree, and click "Edit Context". From here we'll see a Context Settings tab. Click on
it, and you'll need to add a few settings:

site_start - Set this to the ID of your "Dev Home" resource.


base_url - Set this to "/" (no quotes) since we're making the root of the URL our base.
http_host - Set this to "dev.modxcms.com" (or your subdomain name)
site_url - Set this to "http://dev.modxcms.com/" (or your subdomain url). Don't forget the trailing slash. Remember this setting is
(scheme+http_host+base_url).

You can add other context-specific settings, such as error_page, unauthorized_page, and others if you so choose.

Note: If you're going to be linking back to the 'web' context from this context, you'll want to add those same Context Settings
(with 'web'-specific values, of course) to the 'web' context. This allows MODx to know where to redirect 'web' context URLs back
to.

After creating the settings, clear your site cache.

Creating the Virtual Host

Now we need to do some Apache work. (If you're not using Apache, you can at least follow the same idea and customize it to your server.) Go 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 dev@modxcms.com
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.

Great! Restart your server (apachectl graceful).

The Subdomain Files

Now we're going to need to create the actual files to load the subdomain. Go create a "dev/" subdirectory in /home/modxcms.com/public_html/ (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

Copy those to the dev/ directory.

Now, you'll need to edit a couple of them.

index.php

Edit index.php, and find this line (near the end):

$modx->initialize('web');

Change 'web' to 'dev'. Save the file and close.

.htaccess

You'll only need to edit one line here (and maybe not at all). Find this line (near the top):

RewriteBase /

Make sure that's set to /, not anything else. It should match the 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

And you're done!

See Also

Contexts

Using One Gateway Plugin to Manage Multiple Domains

You have a choice when sharing a single database and manager across multiple domains. You can choose to use the primary front-end context
(known as 'web') to handle all domains or you can create a unique gateway file for each domain to directly initialize a specific context. If you use 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.

Customizing the Manager

What is Form Customization?


What are Rules?
What Forms can I Customize?
Examples
See Also

What is Form Customization?


Form Customization is a new feature that allows users to create Rules, which govern how manager pages render their forms in the MODx
Revolution Manager. It is similar to ManagerManager in MODx Evolution.

What are Rules?


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.

A rule can be one of the following:

What Forms can I Customize?


Technically any manager page can be customized. Form Customization bases its methods off of Actions, which are database representations of
any MODx Revolution Manager page. This also means that Custom Manager Pages can also have Rules applied to them, should the CMPs be
using MODExt.

A full list of documented customizable forms, along with their fields and other info, can be found here:
Examples
An example set of Rules:

will make the Resource Update page look like so:

and TV part look like:

See Also

Form Customization Pages

Documented Form Customization Pages

FC-Resource

Resource Create/Update
Available Fields
Available Tabs
Hiding the Content Field
TVs

This page is under construction.

Resource Create/Update

These pages encompass the following Actions:

resource/update
resource/create

Available Fields

Field Name Containing Panel

Page Title pagetitle modx-panel-resource


Published published modx-panel-resource

Long Title longtitle modx-panel-resource

Description description modx-panel-resource

Introtext introtext modx-panel-resource

Link Attributes link_attributes modx-panel-resource

Alias alias modx-panel-resource

Menu Title menutitle modx-panel-resource

Menu Index menuindex modx-panel-resource

Hide from Menus hidemenu modx-panel-resource

Container isfolder modx-panel-resource

Rich Text richtext modx-panel-resource

Published On publishedon modx-panel-resource

Publish Date pub_date modx-panel-resource

Un-Publish Date unpub_date modx-panel-resource

Searchable searchable modx-panel-resource

Cacheable cacheable modx-panel-resource

Deleted deleted modx-panel-resource

Empty Cache syncsite modx-panel-resource

Content Type content_type modx-panel-resource

Content Disposition content_dispo modx-panel-resource

Class Key class_key modx-panel-resource

Available Tabs

These tabs are available for renaming/hiding:

Tab Name (ID) Containing TabPanel

Create/edit Resource modx-resource-settings modx-resource-tabs

Page Settings modx-page-settings modx-resource-tabs

Template Variables modx-panel-resource-tv modx-resource-tabs

Access Permissions modx-resource-access-permissions modx-resource-tabs

Hiding the Content Field

Use these settings:

Field: modx-resource-content
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

This page is under construction.

FC-Snippet

This page is under construction.

FC-Plugin

This page is under construction.

Form Customization Rules

What is a Rule?
What are the Rules available?
Creating a 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.

What are the Rules available?

The available Rules are:

Creating a Rule

First off, go to the Form Customization page, under the Security main menu. From there, click "Add" in the grid's toolbar. You'll be presented with
a window:
Let's go into the fields.

Field Description Optional

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.

Description An optional, for you, description of the Rule. Yes

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:

Constraint Class: modResource


Constraint Field: template
Constraint: 4

Or, further, if you wanted to restrict the Rule to Resources with a menutitle of "boo":

Constraint Class: modResource


Constraint Field: menutitle
Constraint: boo

Constraints allow a superior level of flexibity to Form Customization Rules.

See Also

Field Default

The Field Default Rule

The Field Default Rule, when set, will automatically set the default value of a field.

This will only work on "create" pages, not "update" pages.

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

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

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

The Move TV to Tab rule will move any TV to the tab you specify.

Usage

Specify the ID of the tab to move to in the "name" field of the Rule. Then specify the TV's ID prefixed with 'tv' in the "value" field.

The list of available tabs is:

ID Description

modx-resource-settings The first tab, or Create/Edit resource tab.

modx-page-settings The second tab, or Page Settings tab.

You can also create a tab with the 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

The New Tab Rule, when set, will create a new tab in the panel.

Usage

Specify the ID of this new tab in the "Name" field.


Specify the tabpanel ID in the "Containing Panel" field
Set the Rule to "New Tab"
Set the title of the new tab in the "Value" field

Available TabPanels

Here are the IDs for the corresponding tab panels on various manager pages.
ID Corresponding Page

modx-resource-tabs The Resource edit/create page.

modx-chunk-tabs The Chunk page.

modx-snippet-tabs The Snippet page.

modx-plugin-tabs The Plugin page.

modx-template-tabs The Template page.

modx-tv-tabs The Template Variable page.

modx-user-tabs The User page.

modx-usergroup-tabs The User Group page.

modx-context-tabs The Context page.

Tab Panels on non-Resource pages are only available in 2.0.0-pl and up.

Examples

An example rule for creating a new tab in the Resource edit page would look like so:

See Also
The root page Form Customization could not be found in space MODx Revolution 2.0.
Tab Title

The Tab Title Rule

The Tab Title Rule will change the title of any Tab.

Usage

The values for the Rule should be as follows:

Name: The ID of the tab to change.


Containing Panel: The ID of the tabpanel to change.
Rule: Tab Title
Value: The new text to change the tab title to.

For a list of tabs available for each page, see Form Customization Pages.

Examples

An example Rule of changing the title for the Page Settings tab for all 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

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

The TV Default Value Rule


The TV Default Value Rule, if set, will set the default value for a TV.

This only works on "create" Actions, not "update" Actions.

Usage

The values for the Rule should be as follows:

Name: The ID of the TV prefixed with 'tv'; for example, for the TV with ID 23, 'tv23'
Containing Panel: 'modx-panel-resource'
Rule: 'TV Default Value'
Value: The value you want to set as the default.

Examples

An example Rule of setting the default value for a TV with ID 23 to "test" is:

See Also
The root page Form Customization could not be found in space MODx Revolution 2.0.

TV Title
The TV Title Rule

The TV Title Rule will change the title of a TV.

Examples

An example Rule of setting the label to "Boo!" for a TV with ID of 1 for all Users would look like this:

See Also
The root page Form Customization could not be found in space MODx Revolution 2.0.

TV Visible

The TV Visible Rule

The TV Visible Rule, if set to 0, will hide a TV from a User.

Examples

An example Rule of hiding the a TV with ID of 1 for all Users would look like this:
See Also
The root page Form Customization could not be found in space MODx Revolution 2.0.

Security

Security in MODx Revolution


Security in MODx Revolution is primarily driven by an Attribute-Based Access Control (ABAC) paradigm.

Each user in MODx has a 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.

ACLs usually have the following:

Principal - The object that is getting the access permissions. This is in MODx, by default, a User Group.
Target - The object they apply to, for example, a Context or Resource Group.
Access Policy - The list of Permissions that is gained by this ACL.
Authority - The minimum Authority level required to use this ACL (see Roles).

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

Security Tutorial Video

Demonstrates by applying concepts to the MODx Sample Site to:

Restrict RSS feed to Directors and up


Restrict Blog to Staff only
Create a 'secure' context for Directors and up only
Restrict some element categories to administrators only

Example: Context Access

A good example is creating a Context named 'test', and assigning an ACL to it. This can be done by editing the Context, and going to the '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?

A User is simply a representation of a login in MODx Revolution.

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:

System Settings -> Context Settings -> User Settings

Users in the Front-End

When a user is logged into the frontend of your site, their username and ID can be accessed by the following Properties:

[[+modx.user.id]] - Prints the ID


[[+modx.user.username]] - Prints the username

If a user is not logged in, ID will be blank, and Username will be "(anonymous)".

User Fields

Users contain the following fields:

Name Description

id The ID of the user.

username The username of the user.


password The user's encrypted password.

active Either 1 or 0. If not active, the user will not be able to log in.

remote_key A remote user Key used by remote authentication apps.

remote_data A JSON array of data used by remote authentication apps.

Users also have a Profile attached to them. It contains the following fields:

Name Description

internalKey The ID of the user.

fullname The full name of the user.

email The email of the user.

phone The phone number.

mobilephone The cellphone number.

fax The fax number.

blocked Either 1 or 0. If blocked is set to true, the user will not be able to log in.

blockeduntil A timestamp that, when set, will prevent the user from logging in until this date.

blockedafter A timestamp that, when set, will prevent the user from logging in after this date.

logincount The number of logins for this user.

lastlogin The last time the user logged in.

thislogin The time the user logged in in their current session.

failedlogincount The number of times the user has failed to log in since last logging in.

sessionid The User's session ID that maps to the session table.

dob The date of birth.

gender 0 for neither, 1 for male and 2 for female.

address The physical address.

country The country of the user.

city The city of the user.

zip The zip or postal code for the user.

state The physical state or province of the user.

photo An optional field for a photo. Not used in the UI.

comment An optional comment field for comments on the User.

website The website of the user.

extended A JSON array that can be used to store extra fields for the User.

Grabbing the User via the API

The current user can be gotten in the API via the $modx->user reference. For example, this snippet outputs the username of the user:

return $modx->user->get('username');

Note that to grab Profile fields, you'll need to first get the modUserProfile object via the Profile alias. For example, this snippet grabs the email 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

What is a User Group?

A User Group is simply a collection of Users.

Usage

Go to Security -> Access Controls. From there you will see a tree of User Groups and their respective Users. You can assign a User to a User
Group by right-clicking on the User Group and either:

Adding the User via the context menu item


Editing the User Group and adding a User on the grid there

Assigning Policies

A quick clarification on which policies to use:

Policies assigned on the Context Access tab should be based on the standard Administrator policy.
Policies assigned on the Resource Group Access tab should be based on the standard Resource policy.
Policies assigned on the Element Category Access tab should be based on the standard Element policy.

Roles in User Groups

Users can have specific Roles within a User Group, should you choose. They can also exist in the User Group without a Role. Roles allow you 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?

A Resource Group is a collection of Resources, e.g. "Members Only" pages.

Usage

You can add or delete members of a Resource Group in two different ways:

Option 1

Go to Security -> Resource Groups. From there you will see a tree of Resource Groups and a tree of Resources.

then drag the Resources from the right tree directly into Resource Groups in the left tree:

Option 2

When you are editing a Resource directly, click on its "Access Permissions" tab, and check which Resource Groups it is a member of.

See Also

Roles

What is a Role?
A role is a position or status held within a certain situation. In MODx, it can be used to group Users into a position or status within a User Group,
e.g. "Editor" or "Front-end Read Only".

Roles in MODx use an integer value called "Authority". 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).

You should generally avoid duplicate authority numbers.

Usage

One common example is to create Roles that mimic a basic employee position structure. Let's say we create the following Roles and 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

What is an Access Policy?

An Access Policy is a set of Permissions containing one or many Permissions, as defined in the manager.

Creating and Editing

To create an Access Policy in the manager, navigate to the "Security" menu and select "Access Policies". From there you can add new policies.
To edit an Access Policy in the manager, simply right-click the Policy you want to edit.

Usage

Policies can be used in a myriad of different ways. Here are 3 example usages that come by default in MODx:

Context Access

Access Policies can be assigned as 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.

Resource Group Access

They can also be Resource ACLs, that limit access to Resources based on Roles and Resource Groups. MODx comes packaged with a default
"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.

Element Category Access

Elements can be restricted from view by ACLs on Categories. For example, if you had a User Group called 'Developers', and wanted Users in 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

Here's an example custom policy:

and its permissions:

Any User that had access to this Policy would have the permissions 'view_accounts' and 'save_accounts'.
See Also

Permissions

What is a Permission?

A Permission in Revolution is simply a single access control that allows you to deny or allow access to a task. They are collected in Access
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

Permissions - Administrator Policy

The Administrator Policy

This policy is packaged into MODx and is given to users on the 'mgr' context who want to have full access to managing MODx content.

Default Permissions

Name Description of Access

about The About page.

access_permissions Any Access Permission-related pages and actions.

action_ok

actions The Actions page.

change_password User can change their user password.

change_profile User can change their profile.

content_types The Content Types page.

create Basic "create" access on objects.

credits View the Credits page.

customize_forms View and manage the Customizing the Manager page.

database The System Info page

database_truncate The ability to truncate a database table.

delete_category To delete or remove any Categories.

delete_chunk To delete or remove any Chunks.

delete_context To delete or remove any Contexts.


delete_document To delete or remove any Resources.

delete_eventlog To empty the Event Log.

delete_plugin To delete or remove any Plugins.

delete_snippet To delete or remove any Snippets.

delete_template To delete or remove any Templates.

delete_tv To delete or remove any Template Variables.

delete_role To delete or remove any Roles.

delete_user To delete or remove any Users.

edit_category To edit any Categories.

edit_chunk To edit any Chunks.

edit_context To edit any Contexts.

edit_document To edit any Resources.

edit_locked Allows a user to override a lock and edit a locked Resource.

edit_parser

edit_plugin To edit any Plugins.

edit_role To edit any Roles.

edit_snippet To edit any Snippets.

edit_template To edit any Templates.

edit_tv To edit any Template Variables.

edit_user To edit any User.

element_tree The ability to view the Elements Tree on the left nav.

empty_cache To empty the site cache.

export_static To export the site to static HTML.

file_manager To use the file manager, including creating/deleting files.

file_tree To view the Files Tree on the left nav.

flush_sessions Can flush Sessions across the site.

frames To use the MODx Manager UI at all.

help To view the Help page.

home To view the Welcome page.

import_static To view or use the Import pages.

languages To edit or view Lexicon Languages.

lexicons To edit or view Lexicons and Internationalization.

list Basic permission to "list" any object. List means to get a collection of objects.

load Basic permission to "load" any object, or be able to return it as an instance at all.

logout To be able to logout as a user.

logs To view the logs, such as error and manager logs.

menus To edit or save any top Menu items.

messages To send or view any personal Messages.

namespaces To edit or view Namespaces.


new_category To create a new Category.

new_chunk To create a new Chunk.

new_context To create a new Context.

new_document To create a new Resources.

new_plugin To create a new Plugin.

new_role To create a new Role.

new_snippet To create a new Snippet.

new_template To create a new Template.

new_tv To create a new Template Variable.

new_user To create a new User.

packages To use any Transport Packages in the Package Management system.

property_sets To view and edit Properties and Property Sets.

providers To view and edit Providers across the site.

publish_document To publish or unpublish any Resource.

purge_deleted To empty the Recycle Bin.

remove Basic permission to remove any object.

remove_locks To remove all existing Locks throughout the site.

resource_tree To view the Resource Tree in the left nav.

save Basic save permission for any object.

save_category To save any Categories.

save_chunk To save any Chunks.

save_context To save any Contexts.

save_document To save any Resources.

save_plugin To save any Plugins.

save_role To save any Roles.

save_snippet To save any Snippets.

save_template To save any Templates.

save_tv To save any Template Variables.

save_user To save any User.

search To use the Search page.

settings To view and edit any System Settings.

steal_locks To "steal" locks, overriding a current lock on a document.

unlock_element_properties To be able to edit the default properties for any Element.

view Basic permission to "view" any object.

view_category To view any Categories.

view_chunk To view any Chunks.

view_context To view any Contexts.

view_document To view any Resources.

view_eventlog To view the Event Log.


view_offline

view_plugin To view any Plugins.

view_role To view any Roles.

view_snippet To view any Snippets.

view_template To view any Templates.

view_tv To view any Template Variables.

view_unpublished To view any unpublished Resources.

view_user To view any User.

workspaces To utilize Package Management.

See Also

Permissions - Resource Policy

The Resource Policy

This policy is packaged into MODx and is given to users on any context who want to have basic object access to content. The Permissions 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

What is an ACL (Access Control List)?


Usage
Context ACL
Resource ACL
See Also

What is an ACL (Access Control List)?

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 ACL is referenced of 4 parts:

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:

All the Users in a User Group


...with at least the Minimum Role specified
...that will give the Users all the Permissions in the Access Policy assigned.

Resource ACL

Resource ACLs behave a bit differently, and basically allow you to restrict access to Resources (such as Documents, Weblinks, etc) by Resource
Groups. They are comprised of 5 Parts:

A Resource Group
A User Group
A Minimum Role
An Access Policy
A Context

This means that an ACL applied to a Resource Group will:

Effect all the Users in the specified User Group


... with at least the Minimum Role specified
... give the Resource Permissions (save, load, delete, etc) in the Policy specified
... to all the Resources in the Resource Group

See Also

Security Tutorials

Security Tutorials

Here are some tutorials designed to help you get started with Security in MODx Revolution.

Giving a User Manager Access

The Problem

You want a User to have manager editing access, but not have all the Permissions of an Administrator user. This tutorial, partially written by
BobRay, will help you through that.
The Solution

First off, create your user. Then:

1. Create a new role (say, Editor) with an authority of say, 10.


2. Add the User to the admin group with a role of Editor
3. In Access Controls | Policies, duplicate the administrator policy and rename it to whatever you want, say "AdminLite".
4. Edit the AdminLite Policy to use whatever Permissions you want the User to have.
5. In Access Controls | User Groups, right click on the admin group and select "Update Group."
6. On the Context Access tab, add two new ACL entries to the grid:
a. Context: 'mgr' Minimum Role: 'Editor', Access Policy 'AdminLite'
b. Context: 'web' Minimum Role: 'Editor', Access Policy 'AdminLite'
7. Click on Security -> Flush Sessions and re-login.

See Also

Making Member-Only Pages

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.

1. Create a Resource Group


A resource group is a collection of resources which you can link to user groups and access list entries. When you have created a resource group,
you can easily classify pages to be only visible for certain user groups, or roles within user groups.

To create a resource group, navigate to Security -> Resource Groups and click on the Create Resource Group button. In the popup you are
prompted to enter a name for the resource group. In the tutorial we expect you named it "Protected".

2. Link your member-only resource to the Resource Group

Now that you have a resource group, you should add resources to it. There are two ways to achive this.

First of all, you can go to Security -> Resource Groups and drag resources from the right resource tree to the left resource groups ("Protected").
The second option is editing your resource, and ticking the right box on the "Access Permissions" tab.

It is important to realize that as soon as you have protected a resource by assigning it to one or more resource groups, those pages will no 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.

3. Create a User Group

You have a resource group with resource applied to them, now you'll need to decide who will be able to view the resources. For this, we'll make 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.

4. Add Context access

At this point the user group means nothing, it has zero access. To start with, we'll add context access to the user group. When a user group 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.

So, to sum up, apply this context access:

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.

5. Add Resource access

Move on to the Resource Access tab. This tab defines the resources your user has access to if they are protected by resource groups. Three 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:

Resource Group: whatever you named it, for example "Protected"


Context: web
Minimum role: Member (9999)
Access Policy: Resource
6. Add users to the user group

Now add some users to the user group. You can do this by editing the user, or by going back to the Users tab and adding them from there. It 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.

Help! I can't get this to work, still!

Make sure you followed everything step by step and that you flushed permissions properly. If everything seems to be alright, check again 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

Bob's permissions guide: http://bobsguides.com/revolution-permissions.html

Forum topic discussing this tutorial: http://modxcms.com/forums/index.php/topic,51259.0.html

Security: http://svn.modxcms.com/docs/display/revolution/Security

Installing a Package

Installing a Package
This page will guide you through the process of installing a Package via Package Management.

Downloading packages through 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.

Go to System -> Package Management.


Then click the Download Extras link.

Browse the available packages, opening the folders to expose the individual packages. Click Download to download whichever packages you'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

Troubleshooting Package Management

Troubleshooting Package Management

This page is dedicated to problems with Package Management, specifically with downloading and installing packages.

Most issues can be resolved by making sure you have cURL installed, and that the core/packages/ directory is writable by PHP.
Upgrading MODx

Upgrading MODx Revolution 2.0


This document assumes you are upgrading from a standard install. For Git users, please see Git Installation.

Uploading the Files


For traditional distribution users, simply upload the files over the existing ones, and then run setup. For the advanced distribution, do the same,
but you'll only need to do so for the core/ and setup/ directories.

Make sure that you don't overwrite core/config/config.inc.php, and that it's writable.

Beginning Setup
Simply follow the upgrade process, selecting whichever upgrade you want to perform (normal or database).

If you are upgrading using the 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.

If you get errors during setup, please read Troubleshooting Installation.

After Setup
Make sure to remove the setup/ directory via the last option after setup has completed, so that no one can run setup after you and possibly break
your site.

Version-Specific Changes
For changes relating to specific versions, please see the following pages:

For Upgrades Coming From Prior to 2.0.0-rc2

Upgrades after 2.0.0-rc-2 should run smoothly without issues.

See Also

Upgrading from MODx Evolution

Automated upgrading from 0.9.6.*/Evolution is not yet available in MODx Revolution 2.0.0. A migration tool will be 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

Ditto getResources, getPage, tagLister, Archivist

Jot Quip

SiteMap GoogleSiteMap

MaxiGallery Gallery

eForm FormIt

Wayfinder Wayfinder

DocManager Batcher

AjaxSearch SimpleSearch

WebLogin Login

See Also

Bob's Guide to Upgrading to Revolution

Functional Changes from Evolution

Changes from MODx Evolution to MODx Revolution

Much has changed from MODx Evolution in the new Revolution release. This document will attempt to address some of the major ones.

Tag Syntax

Tags have changed their basic syntax. You can view the Tag Syntax changes here.

Parsing Order

In Evolution, pages were parsed via eval and done as a whole - in Revolution, we implemented "Source Order" parsing. This means tags are
parsed in the order that they occur.

So what does that mean? Well, a few things:

Don't put Snippet calls that assign placeholders at the end of a Resource, or after the Resource. The placeholders will simply be blank,
since the 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.

No More 5000-Document limit

Although this has been mostly remedied in later versions of Evolution, there is still a performance hit in those versions. This, caching-wise, 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.

Error Page vs Unauthorized Page

This is a change from MODx Evolution. In Revolution, if a web page is protected in the front end so that only logged-in users can see it, the
default behavior is for anonymous users to be redirected to the Error (page not found) page rather than the Unauthorized page when they try 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."

(credit to Bob's Guides)

FURL Suffixes and Prefixes -> Content Types

The settings friendly_url_prefix and friendly_url_suffix are no longer applicable, as Revolution handles those now through Content Types.

Upgrading to Revolution 2.0.0-rc-2

Upgrading to Revolution 2.0.0-rc-2

There are a few changes that have occurred in 2.0.0 RC-2 that will only apply to developers. If you are not:

Writing translatable Extras for Revolution


Writing plugins for Revolution

then you do not have to read this document.

Lexicon Changes

First off, you might ask, "Why such a big change so late in the game?" Well, for one, we didnt realize the limitations of the RC1 lexicon system,
and how it hampered Extras development and prevented us from having a stable multi-lingual distribution. What has been changed is:

Dropped entirely the modLexiconTopic and modLexiconLanguage tables.


Changed the 'topic' field on modLexiconEntry to be a varchar of the topic name.
Refactored the entire modLexiconEntry logic so that now DB records of modLexiconEntry are only for overridden entries. Otherwise, they
are cached from the lexicon topic files (the .inc.php files.)
Redid the entire Lexicon Management section to now be a grid that only allows overriding of Entries. In other words, you can only edit
existing entries, and when you edit them, they show up in green, signifying they have been overridden.

This means that the only Lexicon Entries stored in the database are overrides made by the user.

There are some real benefits to the new approach:

Much, much easier translation abilities.


Much faster lexicon loading time, since its file and array based rather than DB and Object based.
You can now successfully change any lexicon entry without harming your upgrade path.
Cuts down on the size of the core.transport.zip and massively decreases build and setup times.
Much easier development. Just put a 'lexicon/' directory in your root of your Namespace's path (like most current Extras do) and build it in
this format: 'lexicon/[language]/[topic].inc.php'. MODx will automatically parse that directory and browse it in Lexicon Management for
you. You no longer need to 'buildLexicon' in your Extra's build scripts. However, this means that all packages using lexicons will need
to be rebuilt for 2.0.0 RC-2. All that needs to be changed is that they no longer need to call 'modPackageBuilder::buildLexicon' in their
build scripts, and their lexicon directories must be under the namespace path with the directory name 'lexicon' (similar to this component).
We apologize for the inconvience, but we promise that you'll find the change much, much easier to develop in.

This also means that we will be packaging in core translations into SVN. All core translations will be committed there, similar to Evolution.

Plugin Changes

Deprecated Plugin Events have been removed in RC-2, and a few new events have been introduced. Please view the in-progress 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

Moving Your Site to a New Server

MODx Revolution is a database-driven web application, so moving it to a new server involves the typical porting over of both the database and 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).

Packaging up your Files


Any time you pack up a site and move, it's best to package the files into "boxes" – when you move out of your apartment, you put everything into
boxes; the same concept is true with files: package them well. If you simply drag and drop files from one server to another using a GUI interface,
chances are good that the GUI will omit hidden files such as the vital .htaccess file. Besides, transferring hundreds of files via FTP can take a long
time because with many servers, each file must undergo some sort of authentication; in other words copying over a hundred 1 megabyte files
takes a lot longer than copying over a single 100 megabyte file.

On a UNIX style system, you can create a compressed file using the tar command:

tar -czf /path/to/backups/modx_revo_site.tar.gz /path/to/modx_doc_root/

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

Dumping your Database


In the future, MODx will be able to run on other databases, but for now it runs on MySQL. You can dump your MySQL database using a GUI tool
such as phpMyAdmin, or you can run the command-line mysqldump utility.

mysqldump -u username -p your_revo_db > /path/to/backups/my_revo_db.sql

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:

mysql -u username -p target_db < my_revo_db.sql


You can also use phpMyAdmin, but remember that web-based tools like this are subject to the same memory limits as PHP, so you're usually
better off using a command-line tool if possible.

Updating your Config File


Once you've deployed files to the new server, you need to update the main configuration file: core/config/config.inc.php. You have to update paths
to 6 different resources. Open the file and update the values for the following variables doing a find and replace:

/* PATHS */
$modx_core_path= '/path/to/modx_doc_root/core/';
$modx_processors_path= '/path/to/modx_doc_root/core/model/modx/processors/';
$modx_connectors_path= '/path/to/modx_doc_root/connectors/';
$modx_manager_path= '/path/to/modx_doc_root/manager/';
$modx_base_path= '/path/to/modx_doc_root/';
$modx_assets_path= '/path/to/modx_doc_root/assets/';

/* HOST (used for command-line PHP stuff) */


$http_host='yoursite.com';

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.

Update your Database


Sometimes developers structure their development and production servers to use the exact same path information, but for most of us, the file path
information will change when we move our MODx web site to a new server. Take a look inside your Revolution database and see for yourself that
path information is stored there. Type the following query into phpMyAdmin, a MySQL command line, or any other application that allows you to
execute queries on your database:

SELECT `path` FROM `your_revo_db`.`workspaces`;

Change "your_revo_db" to your database name, and add an appropriate prefix to the "workspaces" table if necessary.

On the new server you need to update this record. You can edit it using a GUI editor (like SQL-Yog or phpMyAdmin), or you can execute the
following command (again, you need to customize the query depending on your database, prefix, and the path to your data):

UPDATE `your_revo_db`.`workspaces` SET path='/path/to/modx_doc_root/core/' WHERE id='1';

Don't Forget the Database


MODx stores some path data in its database! When you move servers, you may have to update the workspaces table,
otherwise the manager page may show a white page.

Update .htaccess
When you change servers, you frequently wind up changing domain names. Make sure you update any references to the old domain to the new
one.

Log into the Manager: Clear your Cache and Sessions


By now you should be able to log into the manager, but when you first login you'll see an error message:

Could not find action file at: /path/to/manager/controllers/default/welcome.php

That's because the old path is still cached; MODx Revolution caches a lot of information in the database. Once you've gotten the files and
database transferred over to the new server, make sure you clear your site's cache of any lingering data that may contain the old file paths.

Log into the manager, then: Site --> Clear Cache

Finally, clear your sessions: Security --> Flush All Sessions

Your site should now be up and running in its new location!

Developing in MODx

This section contains information on starting development in MODx.

Code Standards

Code 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

Indentation and Line Breaks

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.

MODx does not advocate PHP compression.

HTML
HTML5 is a new version of HTML and XHTML. The HTML5 draft specification defines a single language that can be written in HTML and XML. 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)

MODx recommends following the HTML5 specs: http://whatwg.org/specs/web-apps/current-work/

Validation

All HTML must be HTML5-validated. MODx recommends using the W3C Validator.

Inline HTML in Snippets

MODx requires that 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"

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


"http://w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

Tags and Attributes

All tags and attributes must be written in lowercase. Additionally, we prefer that any attribute values also be lowercase, when the purpose of 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"

<meta http-equiv="content-type" content="text/html; charset=utf-8" />

"For humans"

<a href="http://example.com/" title="Description Goes Here">Example.com</a>

Quotes

In keeping with the strictness of XHTML code conventions, according to the W3C, all attributes must have a value, and must use double-quotes
(source). The following are examples of proper and improper usage of quotes and attribute/value pairs.

"Correct"

<input type="text" name="email" disabled="disabled" />

"Incorrect"

<input type=text name=email disabled>

CSS

Inline Styles

We strive to maintain proper separation of content and design, and therefore highly discourage the use of inline style="..." attributes. This not 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"

#selector_1 span, #selector_2 span, #selector_3 span {


background: #fff; color: #000;
}

"Also incorrect"

#selector { background: #fff; color: #000; }

Pixels vs. Ems

We use the px unit of measurement to define font size, because it offers absolute control over text. We realize that using the em unit for 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;
}

Internet Explorer Bugs

Inevitably, when all other browsers appear to be working correctly, any and all versions of Internet Explorer will introduce a few nonsensical bugs,
delaying time to deployment. While we encourage troubleshooting and building code that will work in all browsers without special modifications,
sometimes it is necessary to use conditional if IE comments to serve up specific fixes, which are ignored by other browsers.

"Fixing IE"

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

Margin & Padding

"Correct"

#selector {
margin: 0 0 10px;
padding: 0 0 10px;
}

"Incorrect - left attribute unnecessary"

#selector {
margin: 0 0 10px 0;
padding: 0 0 10px 0;
}

Hex Colors

We prefer hex values for all colors, written in lower-case. No upper-case or RGB, please! Additionally, all colors should be written as tersely 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;
}

"Incorrect - longhand unnecessary"

#selector {
background-color: #fff;
background-image: url(../images/file.png);
background-repeat: repeat-x;
background-attachment: fixed;
background-position: left bottom;
}

Border

In general, border should be a single line declaration, assuming that the values of the border are the same on all sides of the element. The order
in which values are declared are: width, style, and color.

"Shorthand - method 1"

#selector {
border: 1px solid #000;
}
If the values of each side differ, then there are two possible ways of using shorthand, and it is up to the discretion of the developer to decide which
to use. Note that method 2 follows the TRBL pattern.

"Shorthand - method 2"

#selector {
border-color: #fff #999 #666 #ccc;
border-style: solid dashed dotted double;
border-width: 1px 2px 3px 4px;
}

"Shorthand - method 3"

#selector {
border-top: 1px solid #fff;
border-right: 2px dashed #999;
border-bottom: 3px dotted #666;
border-left: 4px 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.

var test_1 = 'true';


var test_2 = 0;
if (test_1 == true) {
// Code here will run.
// But it shouldn't.
}
if (test_1 === true) {
// Code here won't run.
// Correct behavior.
}
if (test_2 != false) {
// Code here won't run.
// But it should.
}
if (test_2 !== false) {
// Code here will run.
// Correct behavior.
}

As you can see in the example above, simply using == and != is insufficient because it makes for potentially unpredictable results. Therefore, 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.

Consider the following examples of a JavaScript for-loop...

"Correct"

for (var i=0, j=arr.length; i<j; i++) {


// Do something.
}

"Incorrect"

for ( var i = 0, j = arr.length; i < j; i++ )


{
// Do something.
}

Variables, ID & Class

All JavaScript variables shall be written in completely lowercase letters, with underscores to separate words if need be. Likewise, all id and 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"

var my_html = '<img class="photo" src="/path/file.jpg" alt="Text" />';

"Incorrect"

var my_html = "<img class=\"photo\" src=\"/path/file.jpg\" alt=\"Text\" />";

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.

Closures & Scope

To maintain proper scope for variables, it is highly recommended that self-executing anonymous function be used as a closure. For the most part,
variables defined correctly using the var syntax, within the scope of a function will not add to global scope pollution. However, from time to time,
you may need to access variables via two or more functions. In such cases, multiple functions can be grouped together inside a closure.

"Closure"

(function() {
var first_variable = 'value 1';
var second_variable = 'value 2';

function first_func() {
// Do something.
}

function second_func() {
// Do something.
}
})();

Objects & Arrays

Objects can be thought of as tiered variables that contain multiple attributes. Similarly, an array could be described as a list of data that all 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.

"Object literal - preferred"

var john_doe = {
first_name: 'John'
,last_name: 'Doe'
,job: 'Everyman Respresentative'
,email: 'john.doe@example.com'
,married: true
,age: 30
};

"Object dot notation"

/*
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 = 'john.doe@example.com';
john_doe.married = true;
john_doe.age = 30;
"Array literal - preferred"

var doe_family = [
'John'
,'James'
,'Jane'
,'Jenny'
,'Jared'
,'Jerome'
];

"Array bracket notation"

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

Do not put parenthesis next to keywords. Put a space between.


Do put parenthesis next to function names.
Do not use parenthesis in return statements when it's not necessary. Example:

if ($test) {
}
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

Note these are not function arguments.

Use all lowercase letters.


Separate words with the underscore.

Function Arguments and Class Variables

The first letter is lowercase, rest are camelCase. Example:

class modFactor {
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

Constants must be in all UPPERCASE letters.


Use only if absolutely necessary.

File Structure

Always name PHP class files in name.class.php format.

Prefixing

Lexicon strings for Components need to be prefixed:

$_lang['mycomponent.welcome_message'] = 'Welcome!';

Always prefix class names; eg: 'finBank', 'finTransaction', etc.


Always prefix Chunk names; eg: 'finStatement', 'finDeposit'

SQL
All inline SQL must be capitalized, and table and column names must be enclosed with backticks.
"Correct"

UPDATE `mydatabase`.`mytable`
SET `name` = "Johnny"
WHERE `id` = 123;

"Incorrect"

update mydatabase.mytable set name='Johnny' where id=12

Overview of MODx Development

This section pertains to developing Extras for MODx Revolution that can help extend your MODx system.

Developer Introduction

What is MODx?
What is MVC?
What is MVC²?
Connector/Processor Relationships
What is xPDO?
What is an ORM?
A Brief Overview of Revolution
The 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.

A Brief Overview of Revolution

Revolution at its core is a Content Management Framework. It's not a PHP Application Framework like CodeIgnitor or Symfony, nor does 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.

In the front-end, they are Templates, Chunks and Resources.

Templates

Templates are what they sound like. They allow you to create templates that will encapsulate more page-specific data. Think of them 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.

The 2nd C: The Connectors

Connectors are a new idea to MODx Revolution; they are access points for processors. The manager system in MODx Revolution uses 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

xPDO, the database layer for Revolution


Explanation of Directory Structure
Glossary of Revolution Terms

Getting Started Developing

Programming in MODx Revolution


3rd-Party Components (3PCs)
core/components and assets/components
Snippets
Plugins
Properties and Property Sets
Custom Manager Pages (CMPs)
Using MODx Externally

Programming in MODx Revolution

MODx Revolution is an OOP Framework, built around the database ORM xPDO.

3rd-Party Components (3PCs)

3rd-Party Components (3PCs) are collections of any sort of MODx Objects. They can be a collection of Snippets, Plugins and Chunks, or a single
Snippet, or just a collection of files. They are usually transported and installed via Transport Packages.

core/components and assets/components

MODx doesn't necessarily limit where you can put your custom 3rd party component files, but we do have some recommendations. For files 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 and Property Sets

Properties are simply placeholders on Elements (Snippets/Plugins/Chunks/TVs/Templates), which can be parsed by each individual Element.
They allow customization and argument passing for each Element.

Property Sets are user-defined groupings of Properties that can be used to quickly centralize custom tag syntax calls.

More on Property Sets can be found here.

Custom Manager Pages (CMPs)

Custom Manager Pages, or CMPs, are custom pages in the manager built by 3rd Party developers to allow backend management 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 MODx Externally

Using the MODx object (and all of its respective classes) is quite simple. All you need is this code:

require_once '/absolute/path/to/modx/config.core.php';
require_once MODX_CORE_PATH.'config/'.MODX_CONFIG_KEY.'.inc.php';
require_once MODX_CORE_PATH.'model/modx/modx.class.php';
$modx = 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

Setting up a Development Environment

Recommended Development Tools and Environments for MODx Revolution

In developing MODx Revolution, the MODx Team has found the following environments invaluable:

Netbeans

Netbeans 6.8
Netbeans Subversion and JIRA plugins
Eclipse 3.6 Helios and EclipsePDT

Currently phpEclipse appears to have issues running under Helios

Install Eclipse IDE for Java EE Developers


This will give you the Web Tools Platform in a single step
Start up Eclipse
At this point it is not suggested to use a previous workspace with live projects
For this install we simply selected the default (and empty) workspace, which was located in the current user space
Go to the Help menu | Install New Software
Select "-All Additional Sites-" from the "Work with:" drop down
Wait for the site list to populate the display
Open the "Programming Languages" category
Select "PHP Development Tools"
Install all suggested software
-- Alternately you can directly install the PDT tools in a much smaller package
Restart Eclipse
At this point you can start importing sites into the workspace

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.

Eclipse versions before 3.6

Eclipse 3.2.+ (recommend latest 3.5.1)


Web Standard Tools Project (WST) 2.0.1 (http://download.eclipse.org/webtools/updates/)
Subclipse 1.6.5 (http://subclipse.tigris.org/update_1.6.x)
PHPEclipse 1.2.3 (http://update.phpeclipse.net/update/nightly)
Spket IDE 1.6.18 (http://spket.com/update/)

Installation

Simply install the latest Eclipse Classic


Start up eclipse / select a workspace
Use the Install Software option under the help menu
Right click and copy each of the links above (doing them in order doesn't hurt)
Click the "Add" button
Name the "repo" WST, Subclipse, PHPEclipse, or Spket, as it relates to the URL
Paste the URL
Click OK
Repeat for each of the links above as necessary
Individual notes:
WST - select the latest Web Tools Platform (takes quite a while)
Subclipse - simply install the Subclipse option
PHPEclipse - install everything offered
Spket - Install everything offered

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

Development Server Environments

We also MacPorts, XAMPP and MAMP, and the following tools/libraries in the development of MODx Revolution:

PHPUnit - this drives the PHP 5.1+ unit testing framework for xPDO, and we'll be adding a test harness to MODx 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.

How Do They Work?

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.

Passing Values Into a Snippet

Snippets can take input values using a modifed CGI web-form type notation. For example, if your Snippet looks something like this:

<?php
return 'My input was: ' . $input;
?>

You might call it using something like this:

[[!mySnippetName? &input=`Hello World`]]

Notice that the variable names in the calling bit need to match the variable names in the Snippet EXACTLY (case matters... i.e. 'Input' not 'input').
Secondly, don't forget the '&' in front of the would be variable names. And last but most certainly not least, take note that those are backticks, not
single quotes!

Database Interaction in Snippets

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.

Further Database Reading

For further reading on xPDO, read up on these:

xPDO at the xPDO space


Retrieving Objects in xPDO
The xPDOQuery Object

Recommended Methods and Tips

Write your Snippets outside of MODx.

This is pretty easy to do - just create an 'include' snippet, but make its content be this:

if (file_exists($file)) {
$o = include $file;
} else { $o = 'File not found at: '.$file; }
return $o;

You can use the include snippet on a page like such:

[[!include? &file=`/absolute/path/to/my/snippet.php`]]

And run your Snippets externally while you develop them!

Then you can test them to make sure they work (e.g. on the bash command line, you can use the command 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.

Don't try to mix PHP and HTML in a Snippet.

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:

<p>This is a horrible mixture of HTML and PHP</p>


<?php
return "<p>and PHP!&nbsp; Don't try it!&nbsp; It's bad architecture and it won't work!!</p>";
?>

You'll find that MODx will append PHP tags to beginning and end of the snippet, creating an invalid syntax, e.g.:

<?php <?php //something here ?> ?>

If you need to do something like this, 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;

Don't Work on Live Snippets

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.

Use Default Properties

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

Templating Snippets

One of the best practices in Snippet design is to make sure that you never write HTML directly in the Snippet, but template out the HTML 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

Our Initial Snippet

Let's take a case scenario; say you want to iterate across the published, non-deleted Resources that are children of the Resource with ID 390,
sorted by menuindex, and then output them as LI tags with the pagetitle and a link to click them.

Go ahead and create a snippet called 'ResourceLister', and put this inside:
/* first, build the query */
$c = $modx->newQuery('modResource');
/* we only want published and undeleted resources */
$c->where(array(
'published' => true,
'deleted' => false,
));
/* get all the children of ID 390 */
$children = $modx->getChildIds(390);
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.

Templating the Snippet

First off, let's create a chunk that we'll use for each item in the result set. Call it "ResourceItem", and make this its content:

<li><a href="[[~[[+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).

And then, change the foreach loop in the Snippet to this:

foreach ($resources as $resource) {


$resourceArray = $resource->toArray();
$output .= $modx->getChunk($tpl,$resourceArray);
}

The code first turns the modResource object into an array of field=name pairs (ie, $resourceArray['pagetitle'] is the pagetitle) via the toArray()
method. Then, we use $modx->getChunk() to pass our tpl Chunk and the resource array into it as properties. MODx parses the chunk, 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.

Adding A Row Class

What if we want the user to be able to specify a CSS class for each LI row, but not have to make their own custom chunk? Simple, we just add 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:

<li class="[[+rowCls]]"><a href="[[~[[+id]]]]">[[+pagetitle]]</a></li>

And finally, change our foreach loop to this:

foreach ($resources as $resource) {


$resourceArray = $resource->toArray();
$resourceArray['rowCls'] = $rowCls;
$output .= $modx->getChunk($tpl,$resourceArray);
}

Note how we're explicitly setting the 'rowCls' variable into our $resourceArray property array. We do this because we've already gotten the 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.

For reference, our final code looks like this:


$tpl = $modx->getOption('tpl',$scriptProperties,'ResourceItem');
$id = (int)$modx->getOption('id',$scriptProperties,390);
$rowCls = $modx->getOption('rowCls',$scriptProperties,'resource-item');

$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

Adding CSS and JS to Your Pages Through Snippets

Learning How to Register CSS and JS

So, you've got a Snippet that you've been writing and want to add CSS and/or JavaScript to your pages, but don't want to have to setup a 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.

Adding to the HEAD

There are a few methods that automatically add CSS and/or JavaScript to the HEAD of the current page. They will run in the order that they'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>');

Adding Before the BODY End

There are also methods that can be used to insert Javascript or HTML at the end of every page, right before the BODY tag closes. They are 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.

The Event Model


MODx invokes System Events across its code processes to allow you to modify core functionality without hacking the core. These System Events
can have any number of Plugins attached to them, and will execute each Plugin in rank according to it's priority.

Handling an Event

In your Plugin, how you handle the output depends on the System Event you are in. For some system events, you return a value from the Plugin.
For others, you access the output directly and modify it.

If you need to know which event triggered your plugin (say, for a plugin that listens to more than one event), you can access the Event's name 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

$words = array("snippet", "template"); // words to filter


$output = &$modx->resource->_output; // get a reference to the output
$output = str_replace($words,"<b>[filtered]</b>",$output);

Page-Not-Found Redirector:

Description: Redirects a user to selected document and sends a message


System Events: OnPageNotFound
System Settings:

pnf.page: Error Resource ID


pnf.mailto: Mail To Address
pnf.mailfrom: Mail From Address
if ($modx->event->name == 'OnPageNotFound') {
$errorPage = $modx->getOption('pnf.page');
if (empty($errorPage)) {
$modx->sendErrorPage();
} else {
$mailto = $modx->getOption('pnf.mailto');
if (!empty($mailto)) {
// send a message to a local account
$resourceId = $modx->resource->get('id');
$subject = 'Page not found';
$body = 'Someone tried to access document id '.$resourceId;
$modx->getService('mail', 'mail.modPHPMailer');
$modx->mail->set(modMail::MAIL_BODY, $body);
$modx->mail->set(modMail::MAIL_FROM, '$modx->getOption('pnf.mailfrom'));
$modx->mail->set(modMail::MAIL_FROM_NAME, 'MODx');
$modx->mail->set(modMail::MAIL_SENDER, 'MODx');
$modx->mail->set(modMail::MAIL_SUBJECT, $subject);
$modx->mail->address('to',$mailto);
$modx->mail->setHTML( true);
$modx->mail->send();
}
$url = $ this->makeUrl($scriptProperties['page']);
$modx->sendRedirect($url, 1);
exit;
}
}

See Also

System Events

What are System Events?


The Model of a System Event
Service Types
Available Events
See Also

What are 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 Model of a System Event

The system events table is found under {table_prefix}_system_eventnames, and has the following fields:

id - The unique ID of the event.


name - The name of the event. This is how they are referenced in code, via the modX.invokeEvent method.
service - What type of system event this event is.
groupname - Used for user interfaces, primarily for filtering, grouping and sorting of events. Not used explicitly in the modx model.

Service Types

The 'service' field in the System event is a number; the numbers reference different types of System Events. They are:

1 - Parser Service Events


2 - Manager Access Events
3 - Web Access Service Events
4 - Cache Service Events
5 - Template Service Events
6 - User Defined Events
3 is not fired in the 'mgr' context; 2 is not fired in any context but 'mgr'.

Available Events

This is not an exhaustive list as events are still being documented. Thank you for your patience. The TV, Template and 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

Fired before the entire site cache is cleared.

Service: 4 - Cache Service Events


Group: None

Event Parameters

None.

See Also

System Events
Plugins

OnBeforeChunkFormDelete

Event: OnBeforeChunkFormDelete

Fires before a chunk is deleted.

Service: 1 - Parser Service Events


Group: Chunks

Event Parameters

Name Description

chunk A reference to the modChunk object.

id The ID of the Chunk.

See Also

System Events
Plugins

OnBeforeChunkFormSave

Event: OnBeforeChunkFormSave
Fires after a form is submitted but before a Chunk is saved in the manager.

Service: 1 - Parser Service Events


Group: Chunks

Event Parameters

Name Description

mode Either 'upd' or 'new', depending on the circumstance.

chunk A reference to the modChunk object.

id The ID of the chunk. Will be 0 for new chunks.

See Also

System Events
Plugins

OnBeforeDocFormDelete

Event: OnBeforeDocFormDelete

Fires before a Resource is deleted via the manager.

Service: 1 - Parser Service Events


Group: Documents

Event Parameters

Name Description

resource A reference to the modResource object.

id The ID of the Resource.

children An array of IDs of children of this resource which will also be deleted.

See Also

System Events
Plugins

OnBeforeDocFormSave

Event: OnBeforeDocFormSave

Fires before a Resource is saved in the manager via the editing form.

Service: 1 - Parser Service Events


Group: Documents

Event Parameters

Name Description

mode Either 'new' or 'upd', depending on the circumstances.

resource A reference to the modResource object.

id The ID of the Resource. Will be 0 for new Resources.

See Also
System Events
Plugins

OnBeforeManagerLogout

Event: OnBeforeManagerLogout

Fires before a user is logged out of the manager.

Service: 2 - Manager Access Service Events


Group: None

Event Parameters

Name Description

user A reference to the modUser object of the user.

userid The user ID of the user. (deprecated)

username The username of the user. (deprecated)

See Also

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

Service: 4 - Cache Service Events


Group: None

Event Parameters

None. The resource can be referenced via $modx->resource.

See Also

System Events
Plugins

OnBeforeWebLogout

Event: OnBeforeWebLogout

Fires right before a user logs out of a non-mgr context.

Service: 3 - Web Access Service Events


Group: None

Event Parameters

Name Description

user A reference to the modUser object of the user.

userid The user ID of the user.

username The username of the user.


See Also

System Events
Plugins

OnCacheUpdate

Event: OnCacheUpdate

Fired after the cache is cleared at any time.

Service: 4 - Cache Service Events


Group: None

Event Parameters

Name Description

results The results of the clearing.

paths An array of paths that were to be cleared.

options An array of options passed to the cache clearing method.

See Also

System Events
Plugins

OnChunkFormDelete

Event: OnChunkFormDelete

Fires after a chunk is deleted.

Service: 1 - Parser Service Events


Group: Chunks

Event Parameters

Name Description

chunk A reference to the modChunk object.

id The ID of the Chunk.

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.

Service: 1 - Parser Service Events


Group: Chunks

Event Parameters
Name Description

mode Either 'new' or 'upd', depending on the circumstance.

id The ID of the Chunk. This will be 0 for new chunks.

chunk A reference to the modChunk object. Will be null in new chunks.

See Also

System Events
Plugins

OnChunkFormRender

Event: OnChunkFormRender

Fires during the rendering of a form. Useful for rendering HTML straight into the Chunk form.

Service: 1 - Parser Service Events


Group: Chunks

Event Parameters

Name Description

mode Either 'new' or 'upd', depending on the circumstance.

id The ID of the Chunk. This will be 0 for new chunks.

chunk A reference to the modChunk object. Will be null in new chunks.

See Also

System Events
Plugins

OnChunkFormSave

Event: OnChunkFormSave

Fires after a Chunk is saved in the manager.

Service: 1 - Parser Service Events


Group: Chunks

Event Parameters

Name Description

mode Either 'upd' or 'new', depending on the circumstance.

chunk A reference to the modChunk object.

id The ID of the chunk.

See Also

System Events
Plugins

OnDocFormDelete
Event: OnDocFormDelete

Fires after a Resource is deleted via the manager.

Service: 1 - Parser Service Events


Group: Documents

Event Parameters

Name Description

resource A reference to the modResource object.

id The ID of the Resource.

children An array of IDs of children of this resource which were deleted.

See Also

System Events
Plugins

OnDocFormPrerender

Event: OnDocFormPrerender

Fires before a Resource editing form is loaded in the manager.

Service: 1 - Parser Service Events


Group: Documents

Event Parameters

Name Description

mode Either 'new' or 'upd', depending on the circumstance.

resource A reference to the modResource object. Will be null for new Resources.

id The ID of the Resource. Will be 0 for new Resources.

See Also

System Events
Plugins

OnDocFormRender

Event: OnDocFormRender

Fires after a Resource editing form is loaded in the manager. Useful for inserting HTML into forms.

Service: 1 - Parser Service Events


Group: Documents

Event Parameters

Name Description

mode Either 'new' or 'upd', depending on the circumstance.

resource A reference to the modResource object. Will be null for new Resources.

id The ID of the Resource. Will be 0 for new Resources.


See Also

System Events
Plugins

OnDocFormSave

Event: OnDocFormSave

Fires after a Resource is saved in the manager via the editing form.

Service: 1 - Parser Service Events


Group: Documents

Event Parameters

Name Description

mode Either 'new' or 'upd', depending on the circumstances.

resource A reference to the modResource object.

id The ID of the Resource. Will be 0 for new Resources.

See Also

System Events
Plugins

OnDocPublished

Event: OnDocPublished

Called when a Resource is published via the Publish context menu.

Service: 5 - Template Service Events


Group: None

Event Parameters

Name Description

docid The ID of the resource being published. (deprecated)

id The ID of the resource being published.

resource A reference to the modResource object being published.

See Also

System Events
Plugins

OnDocUnPublished

Event: OnDocUnPublished

Called when a Resource is unpublished via the Unpublish context menu.

Service: 5 - Template Service Events


Group: None

Event Parameters
Name Description

docid The ID of the resource being unpublished. (deprecated)

id The ID of the resource being unpublished.

resource A reference to the modResource object being unpublished.

See Also

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

Service: 4 - Cache Service Events


Group: None

Event Parameters

None.

See Also

System Events
Plugins

OnManagerLogin

Event: OnManagerLogin

Fires anytime a user successfully logs into the manager.

Service: 2 - Manager Access Service Events


Group: None

Event Parameters

Name Description

user A reference to the modUser object.

attributes An array of:

rememberme - Boolean set if user wants password to be remembered.


lifetime - The session cookie lifetime for this login.
loginContext - The context key this login is occurring in.

See Also

System 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

user A reference to the modUser object of the user.

userid The user ID of the user. (deprecated)

username The username of the user. (deprecated)

See Also

System Events
Plugins

OnSiteRefresh

Event: OnSiteRefresh

Fires after the cache for the entire site is cleared.

Service: 1 - Parser Service Events


Group: None

Event Parameters

Name Description

results An array of results.

See Also

System Events
Plugins

OnUserChangePassword

Event: OnUserChangePassword

Fires anytime the user properly changes their password.

Service: 3 - Template Service Events


Group: None

Event Parameters

Name Description

user A reference to the modUser object of the user.

newpassword The new password being set.

oldpassword The old password being overridden.

userid The user ID of the user. (deprecated)

username The username of the user. (deprecated)

userpassword The new password being set. (deprecated)

See Also
System Events
Plugins

OnWebLogin

Event: OnWebLogin

Fired anytime a user logs into a non-mgr context.

Service: 3 - Web Access Service Events


Group: None

Event Parameters

Name Description

user A reference to the modUser object.

attributes An array of:

rememberme - Boolean set if user wants password to be remembered.


lifetime - The session cookie lifetime for this login.
loginContext - The context key this login is occurring in.

See Also

System Events
Plugins

OnWebLogout

Event: OnWebLogout

Fires right after the user logs out of a context and their context session is removed.

Service: 3 - Web Access Service Events


Group: None

Event Parameters

Name Description

user A reference to the modUser object of the user.

userid The user ID of the user.

username The username of the user.

See Also

System Events
Plugins

OnWebPagePrerender

Event: OnWebPagePrerender

Fired after a Resource is parsed, but before it is rendered.

Content Type headers have not yet been sent, nor has the output been flushed.

Service: 5 - Template Service Events


Group: None

Event Parameters

None.

See Also

System Events
Plugins

OnBeforeEmptyTrash

Event: OnBeforeEmptyTrash

Fires before the trash is emptied for the site.

Service: 1 - Parser Service Events


Group: Documents

Event Parameters

Name Description

ids An array of Resource IDs that will be permanently deleted.

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.

Service: 3 - Web Access Events


Group: None

Event Parameters

Name Description

username The provided username.

password The provided password.

attributes An array of:

rememberme - Boolean set if user wants password to be remembered.


lifetime - The session cookie lifetime for this login.
loginContext - The context key this login is occurring in.

See Also

System Events
Plugins

OnBeforeManagerPageInit

Event: OnBeforeManagerPageInit
Loaded right before a manager controller is run.

Service: 2 - Manager Access Events


Group: None

Event Parameters

Name Description

action The ID of the action being loaded.

filename The filename of the controller being loaded.

See Also

System Events
Plugins

OnBeforePluginFormDelete

Event: OnBeforePluginFormDelete

Fires before a plugin is deleted in the manager.

Service: 1 - Parser Service Events


Group: Plugins

Event Parameters

Name Description

plugin A reference to the modPlugin object.

id The ID of the Plugin.

See Also

System Events
Plugins

OnBeforePluginFormSave

Event: OnBeforePluginFormSave

Fires after a form is submitted but before a Plugin is saved in the manager.

Service: 1 - Parser Service Events


Group: Plugin

Event Parameters

Name Description

mode Either 'upd' or 'new', depending on the circumstance.

plugin A reference to the modPlugin object.

id The ID of the plugin. Will be 0 for new plugins.

See Also

System Events
Plugins
OnBeforeSnipFormDelete

Event: OnBeforeSnipFormDelete

Fires before a Snippet is deleted in the manager.

Service: 1 - Parser Service Events


Group: Snippets

Event Parameters

Name Description

snippet A reference to the modSnippet object.

id The ID of the Snippet.

See Also

System Events
Plugins

OnBeforeSnipFormSave

Event: OnBeforeSnipFormSave

Fires after a form is submitted but before a Snippet is saved in the manager.

Service: 1 - Parser Service Events


Group: Snippets

Event Parameters

Name Description

mode Either 'upd' or 'new', depending on the circumstance.

snippet A reference to the modSnippet object.

id The ID of the Snippet. Will be 0 for new Snippets.

See Also

System Events
Plugins

OnBeforeTempFormDelete

Event: OnBeforeTempFormDelete

Fires before a Template is deleted in the manager.

Service: 1 - Parser Service Events


Group: Templates

Event Parameters

Name Description

template A reference to the modTemplate object.

id The ID of the Template.


See Also

System Events
Plugins

OnBeforeTempFormSave

Event: OnBeforeTempFormSave

Fires after a form is submitted but before a Template is saved in the manager.

Service: 1 - Parser Service Events


Group: Templates

Event Parameters

Name Description

mode Either 'upd' or 'new', depending on the circumstance.

template A reference to the modTemplate object.

id The ID of the Template. Will be 0 for new Templates.

See Also

System Events
Plugins

OnBeforeTVFormDelete

Event: OnBeforeTVFormDelete

Fires before a TV is deleted in the manager.

Service: 1 - Parser Service Events


Group: Template Variables

Event Parameters

Name Description

tv A reference to the modTemplateVar object.

id The ID of the TV.

See Also

System Events
Plugins

OnBeforeTVFormSave

Event: OnBeforeTVFormSave

Fires after a form is submitted but before a Chunk is saved in the manager.

Service: 1 - Parser Service Events


Group: Template Variables

Event Parameters
Name Description

mode Either 'upd' or 'new', depending on the circumstance.

tv A reference to the modTemplateVar object.

id The ID of the TV. Will be 0 for new TVs.

See Also

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

Service: 1 - Parser Service Events


Group: modUser

Event Parameters

Depends on implementation.

See Also

System Events
Plugins

OnBeforeUserFormDelete

Event: OnBeforeUserFormDelete

Fires before a User is deleted in the manager.

Service: 1 - Parser Service Events


Group: Users

Event Parameters

Name Description

user A reference to the modUser object.

id The ID of the User.

See Also

System Events
Plugins

OnBeforeUserFormSave

Event: OnBeforeUserFormSave

Fires after a form is submitted but before a User is saved in the manager.

Service: 1 - Parser Service Events


Group: Users

Event Parameters
Name Description

mode Either 'upd' or 'new', depending on the circumstance.

user A reference to the modUser object.

id The ID of the User. Will be 0 for new Users.

See Also

System Events
Plugins

OnBeforeWebLogin

Event: OnBeforeWebLogin

Fires before the login process is started for a user when logging in via a non-manager context.

Service: 3 - Web Access Events


Group: None

Event Parameters

Name Description

username The provided username.

password The provided password.

attributes An array of:

rememberme - Boolean set if user wants password to be remembered.


lifetime - The session cookie lifetime for this login.
loginContext - The context key this login is occurring in.

See Also

System Events
Plugins

OnCategoryBeforeRemove

Event: OnCategoryBeforeRemove

Happens right before a category is removed.

Service: 2 - Manager Access Events


Group: Categories

Event Parameters

Name Description

category A reference to the modCategory object.

See Also

System Events
Plugins

OnCategoryBeforeSave
Event: OnCategoryBeforeSave

Happens right before a category is saved.

Service: 2 - Manager Access Events


Group: Categories

Event Parameters

Name Description

id The ID of the modCategory object.

category A reference to the modCategory object.

See Also

System Events
Plugins

OnCategoryRemove

Event: OnCategoryRemove

Happens after a category is removed.

Service: 2 - Manager Access Events


Group: modCategory

Event Parameters

Name Description

category A reference to the modCategory object.

See Also

System Events
Plugins

OnCategorySave

Event: OnCategorySave

Happens after a category is saved.

Service: 2 - Manager Access Events


Group: modCategory

Event Parameters

Name Description

category A reference to the modCategory object.

See Also

System Events
Plugins

OnChunkBeforeRemove
Event: OnChunkBeforeRemove

Fires right before a Chunk is removed.

Service: 1 - Parser Service Events


Group: modChunk

Event Parameters

Name Description

chunk A reference to the modChunk object.

See Also

System Events
Plugins

OnChunkBeforeSave

Event: OnChunkBeforeSave

Fires right before a Chunk is saved.

Service: 1 - Parser Service Events


Group: modChunk

Event Parameters

Name Description

chunk A reference to the modChunk object.

See Also

System Events
Plugins

OnChunkRemove

Event: OnChunkRemove

Happens after a chunk is removed.

Service: 2 - Manager Access Events


Group: modChunk

Event Parameters

Name Description

chunk A reference to the modChunk object.

See Also

System Events
Plugins

OnChunkSave
Event: OnChunkSave

Happens after a chunk is saved.

Service: 2 - Manager Access Events


Group: modChunk

Event Parameters

Name Description

chunk A reference to the modChunk object.

See Also

System Events
Plugins

OnContextBeforeRemove

Event: OnContextBeforeRemove

Happens right before a context is removed.

Service: 2 - Manager Access Events


Group: modContext

Event Parameters

Name Description

context A reference to the modContext object.

See Also

System Events
Plugins

OnContextBeforeSave

Event: OnContextBeforeSave

Happens right before a context is saved.

Service: 2 - Manager Access Events

Event Parameters

Name Description

context A reference to the modContext object.

mode Either 'new' (modSystemEvent::MODE_NEW) or 'upd' (modSystemEvent::MODE_UPD) depending on whether is a new object or an
existing one.

See Also

System Events
Plugins

OnContextFormPrerender
Event: OnContextFormPrerender

Fires prior to the context editing form loading. Useful for running custom javascript.

Service: 2 - Manager Access Events


Group: modContext

Event Parameters

Name Description

key The key of the context.

context A reference to the modContext object.

mode Either 'upd' or 'new', depending on the situation.

See Also

System Events
Plugins

OnContextFormRender

Event: OnContextFormRender

Fires after the context editing form has loaded.

Service: 2 - Manager Access Events


Group: modContext

Event Parameters

Name Description

key The key of the context.

context A reference to the modContext object.

mode Either 'upd' or 'new', depending on the situation.

See Also

System Events
Plugins

OnContextRemove

Event: OnContextRemove

Happens after a context is removed.

Service: 2 - Manager Access Events


Group: modContext

Event Parameters

Name Description

context A reference to the modContext object.

See Also

System Events
Plugins
OnContextSave

Event: OnContextSave

Happens whenever a context is saved.

Service: 2 - Manager Access Events

Event Parameters

Name Description

context A reference to the modContext object.

mode Either 'new' (modSystemEvent::MODE_NEW) or 'upd' (modSystemEvent::MODE_UPD) depending on whether is a new object or an
existing one.

See Also

System Events
Plugins

OnEmptyTrash

Event: OnEmptyTrash

Fires after the trash is emptied for the site.

Service: 1 - Parser Service Events


Group: Documents

Event Parameters

Name Description

ids An array of Resource IDs that were attempted to be permanently deleted.

num_deleted The number of Resources actually deleted.

See Also

System Events
Plugins

OnFileManagerUpload

Event: OnFileManagerUpload

Fires after any files are uploaded via the manager.

Service: 1 - Parser Service Events


Group: None

Event Parameters

Name Description

files An array of files from the PHP $_FILES array.

directory A reference to the modDirectory object that the files are being uploaded to.

See Also
System Events
Plugins

OnHandleRequest

Event: OnHandleRequest

Fires at the beginning of a request handler.

Service: 5 - Template Service Events


Group: None

Event Parameters

None.

See Also

System Events
Plugins

OnInitCulture

Event: OnInitCulture

Fires after a Culture and Lexicon has been initialized.

Service: 1 - Parser Service Events


Group: None

Event Parameters

None.

See Also

System Events
Plugins

OnLoadWebDocument

Event: OnLoadWebDocument

Fires directly before the Response is sent and after a Resource is loaded.

Service: 5 - Template Service Events


Group: None

Event Parameters

None. The resource can be referenced via $modx->resource.

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.

Service: 2 - Manager Access Events


Group: None

Event Parameters

Name Description

user A reference to the modUser object.

password The provided password.

rememberme Whether or not to remember the user via cookie.

lifetime The lifetime of the session cookie.

See Also

System Events
Plugins

OnManagerLoginFormPrerender

Event: OnManagerLoginFormPrerender

Fires before the login form is rendered for the MODx manager.

Service: 2 - Manager Access Events


Group: None

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.

Service: 2 - Manager Access Events


Group: None

Event Parameters

None.

See Also

System Events
Plugins

OnManagerPageInit
Event: OnManagerPageInit

Fired in the manager request handler, before the manager page response is loaded.

Service: 2 - Manager Access Events


Group: None

Event Parameters

Name Description

action The ID of the action currently being loaded.

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.

Service: 1 - Parser Service Events


Group: None

Event Parameters

Name Description

response_code The response code to send. Defaults to "HTTP/1.1 404 Not Found"

error_type The type. Defaults to 404.

error_header The header being sent: Defaults to "HTTP/1.1 404 Not Found"

error_pagetitle The pagetitle of the error page.

error_message The message being sent in the error page.

See Also

System Events
Plugins

OnPageUnauthorized

Event: OnPageUnauthorized

Fires immediately before the user is forwarded to the unauthorized page if attempting to view a non-accessible Resource.

Service: 1 - Parser Service Events


Group: None

Event Parameters

Name Description

response_code The response code to send. Defaults to "HTTP/1.1 401 Unauthorized"

error_type The type. Defaults to 401.

error_header The header being sent: Defaults to "HTTP/1.1 401 Unauthorized"


error_pagetitle The pagetitle of the unauthorized page.

error_message The message being sent in the unauthorized page.

See Also

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

Service: 1 - Template Service Events


Group: None

Event Parameters

None.

See Also

System Events
Plugins

OnPluginBeforeRemove

Event: OnPluginBeforeRemove

Happens right before a plugin is removed.

Service: 2 - Manager Access Events


Group: modPlugin

Event Parameters

Name Description

plugin A reference to the modPlugin object.

See Also

System Events
Plugins

OnPluginBeforeSave

Event: OnPluginBeforeSave

Happens right before a plugin is saved.

Service: 2 - Manager Access Events


Group: modPlugin

Event Parameters

Name Description

plugin A reference to the modPlugin object.


See Also

System Events
Plugins

OnPluginEventRemove

Event: OnPluginEventRemove

Fires after a plugin is disassociated directly from an event.

Service: 1 - Parser Service Events


Group: None

Event Parameters

Name Description

id The id of the modPluginEvent object

pluginEvent A reference to the modPluginEvent object

See Also

System Events
Plugins

OnPluginFormDelete

Event: OnPluginFormDelete

Fires after a plugin is deleted.

Service: 1 - Parser Service Events


Group: Plugins

Event Parameters

Name Description

chunk A reference to the modPlugin object.

id The ID of the Plugin.

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.

Service: 1 - Parser Service Events


Group: Plugins

Event Parameters

Name Description
mode Either 'new' or 'upd', depending on the circumstance.

id The ID of the Plugin. Will be 0 for new plugins.

plugin A reference to the modPlugin object. Will be null in new plugins.

See Also

System Events
Plugins

OnPluginFormRender

Event: OnPluginFormRender

Fires during the rendering of a form. Useful for rendering HTML straight into the Plugin form.

Service: 1 - Parser Service Events


Group: Plugins

Event Parameters

Name Description

mode Either 'new' or 'upd', depending on the circumstance.

id The ID of the Plugin. This will be 0 for new plugins.

chunk A reference to the modPlugin object. Will be null in new plugins.

See Also

System Events
Plugins

OnPluginFormSave

Event: OnPluginFormSave

Fires after a Plugin is saved in the manager.

Service: 1 - Parser Service Events


Group: Plugins

Event Parameters

Name Description

mode Either 'upd' or 'new', depending on the circumstance.

chunk A reference to the modPlugin object.

id The ID of the plugin.

See Also

System Events
Plugins

OnPluginRemove

Event: OnPluginRemove
Happens right after a plugin is removed.

Service: 2 - Manager Access Events


Group: modPlugin

Event Parameters

Name Description

plugin A reference to the modPlugin object.

See Also

System Events
Plugins

OnPluginSave

Event: OnPluginSave

Happens right after a plugin is removed.

Service: 2 - Manager Access Events


Group: modPlugin

Event Parameters

Name Description

plugin A reference to the modPlugin object.

See Also

System Events
Plugins

OnPropertySetBeforeRemove

Event: OnPropertySetBeforeRemove

Fires right before a Property Set is removed.

Service: 1 - Parser Service Events

Event Parameters

Name Description

propertySet A reference to the modPropertySet object.

See Also

System Events
Plugins

OnPropertySetBeforeSave

Event: OnPropertySetBeforeSave

Fires right before a Property Set is saved.

Service: 1 - Parser Service Events


Event Parameters

Name Description

propertySet A reference to the modPropertySet object.

See Also

System Events
Plugins

OnPropertySetRemove

Event: OnPropertySetRemove

Fires after a Property Set is removed.

Service: 1 - Parser Service Events

Event Parameters

Name Description

propertySet A reference to the modPropertySet object.

See Also

System Events
Plugins

OnPropertySetSave

Event: OnPropertySetSave

Fires right after a Property Set is saved.

Service: 1 - Parser Service Events

Event Parameters

Name Description

propertySet A reference to the modPropertySet object.

See Also

System Events
Plugins

OnResourceGroupBeforeRemove

Event: OnResourceGroupBeforeRemove

Fires right before a Resource Group is removed.

Service: 1 - Parser Service Events

Event Parameters

Name Description
group A reference to the modResourceGroup object.

See Also

System Events
Plugins

OnResourceGroupBeforeSave

Event: OnResourceGroupBeforeSave

Fires right before a Resource Group is saved.

Service: 1 - Parser Service Events


Group: modResourceGroup

Event Parameters

Name Description

group A reference to the modResourceGroup object.

See Also

System Events
Plugins

OnResourceGroupRemove

Event: OnResourceGroupRemove

Fires after a Resource Group is removed.

Service: 1 - Parser Service Events

Event Parameters

Name Description

group A reference to the modResourceGroup object.

See Also

System Events
Plugins

OnResourceGroupSave

Event: OnResourceGroupSave

Fires after a Resource Group is saved.

Service: 1 - Parser Service Events


Group: modResourceGroup

Event Parameters

Name Description

group A reference to the modResourceGroup object.


See Also

System Events
Plugins

OnRichTextBrowserInit

Event: OnRichTextBrowserInit

Used to handle MODx.Browser's filesystem implementation for custom RTEs.

Simply pass in a Javascript function that will handle the return url for the file selected from MODx.Browser to your RTE.

Service: 1 - Parser Service Events


Group: Richtext Editor

Event Parameters

None.

See Also

System Events
Plugins

OnRichTextEditorInit

Event: OnRichTextEditorInit

Renders anytime a Richtext Editor could be used.

Service: 1 - Parser Service Events


Group: Richtext Editor

Event Parameters

Name Description

editor The specified editor that the user wants to use.

elements An array of elements to transform into an RTE.

Other properties might be passed, such as:

forfrontend If passed, this will indicate to the plugin that this is to be loaded in a front-end context, not the manager.

width The requested width of the RTE.

height The requested height of the RTE.

See Also

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

Service: 1 - Parser Service Events


Group: System Settings

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.

Service: 1 - Parser Service Events


Group: modUser

Event Parameters

Depends on implementation.

See Also

System Events
Plugins

OnUserBeforeRemove

Event: OnUserBeforeRemove

Fires right before a User is removed.

Service: 1 - Parser Service Events


Group: modUser

Event Parameters

Name Description

user A reference to the modUser object.


See Also

System Events
Plugins

OnUserBeforeSave

Event: OnUserBeforeSave

Fires right before a User is saved.

Service: 1 - Parser Service Events


Group: modUser

Event Parameters

Name Description

user A reference to the modUser object.

mode Either 'new' (modSystemEvent::MODE_NEW) or 'upd' (modSystemEvent::MODE_UPD) depending on whether is a new object or an
existing one.

See Also

System Events
Plugins

OnUserFormDelete

Event: OnUserFormDelete

Fires after a User is deleted.

Service: 1 - Parser Service Events


Group: modUser

Event Parameters

Name Description

user A reference to the modUser object.

id The ID of the user.

See Also

System Events
Plugins

OnUserFormSave

Event: OnUserFormSave

Fires after a User is updated via the manager form.

Service: 1 - Parser Service Events


Group: modUser

Event Parameters
Name Description

user A reference to the modUser object.

id The ID of the user.

See Also

System Events
Plugins

OnUserNotFound

Event: OnUserNotFound

Fires when a user is not found during login.

Can be used to provide external authentication by returning an array, where one of the indexes in the array is an instance of (or extending) a
modUser object.

Service: 6 - User Defined Events


Group: modUser

Event Parameters

Name Description

username The specified username.

password The specified password.

attributes An array of:

rememberme - Boolean set if user wants password to be remembered.


lifetime - The session cookie lifetime for this login.
loginContext - The context key this login is occurring in.

See Also

System Events
Plugins

OnUserRemove

Event: OnUserRemove

Fires after a User is removed.

Service: 1 - Parser Service Events


Group: modUser

Event Parameters

Name Description

user A reference to the modUser object.

See Also

System Events
Plugins

OnUserSave
Event: OnUserSave

Fires when a User is saved.

Service: 1 - Parser Service Events


Group: modUser

Event Parameters

Name Description

user A reference to the modUser object.

mode Either 'new' (modSystemEvent::MODE_NEW) or 'upd' (modSystemEvent::MODE_UPD) depending on whether is a new object or 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.

Service: 3 - Web Access Events


Group: None

Event Parameters

Name Description

user A reference to the modUser object.

password The provided password.

rememberme Whether or not to remember the user via cookie.

lifetime The lifetime of the session cookie.

See Also

System Events
Plugins

OnWebPageComplete

Event: OnWebPageComplete

Fires after the Resource is loaded, response is sent, cache is stored (if applicable) and execution is completed.

Service: 2 - Template Service Events


Group: None

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.

Service: 5 - Template Service Events


Group: None

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

Documentation for xPDO

Documentation on xPDO can be found here:

xPDO 2.0 Documentation

See Also

More information about xPDO can be found at the xPDO website.

Advanced Development

This section is for advanced techniques and tools in MODx development.

Namespaces

What are Namespaces?

Namespaces are organizational elements for Components. They relate lexicon strings and packages to one another, as well as provide a 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.

How to get a Namespace

You can access a Namespace, and its specified path, via:

$namespace = $modx->getObject('modNamespace','mynamespace');
echo $namespace->get('name').' has a path of: '.$namespace->get('path');

See Also

Package Management
Internationalization
Settings

Caching

How is Caching Done in Revolution?


Configuration Cache
Context Configuration, Event Map, and Plugin Caching
Resource (Partial-Page) Caching
Element Caching
xPDO Object and Database Result Set Caching
Programmatic Caching
Using modCacheManager
Using modCacheManager::clearCache
See Also

There are many different caching features in Revolution. Different aspects of the caching can be extended with user-defined classes, so it 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.

How is Caching Done in Revolution?

First, lets take a look at all of the things in the core/cache/ directory of Revolution (or core/cache/MODX_CONFIG_KEY/ if you have that set 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.

Context Configuration, Event Map, and Plugin Caching

In a subdirectory for each context (i.e. web/ or mgr/ or connector/) you will find a context.cache.php file containing the context settings, a plugin
event map for the context, and the plugin elements that are registered in the event map.

Resource (Partial-Page) Caching

Within the web context (or another custom front-end context) there will be a subdirectory for resources/ containing files like 1.cache.php where 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.

xPDO Object and Database Result Set Caching

If you enable this, xPDO can cache query result sets representing any objects and collections in the objects subdirectory. This can be enabled 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

// write some data to a cache file


$colors = array('red','blue','green');
$modx->cacheManager->set('colors',$colors); /* writes to core/cache/colors.cache.php */

// get that data


$colors = $modx->cacheManager->get('colors');
foreach ($colors as $color) {
echo $color.'-';
} /* this echoes 'red-blue-green' */
?>

You can also set time expirations on cached files:

$str = 'My test cached data.';


$modx->cacheManager->set('testdata',$str,7200);
// this caches the data for 2 hours.

Using modCacheManager::clearCache

Now, here are some examples of 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);

// clear all cache files with extension .php in the cachePath


// + all objects + execute the timed publishing checks
$paths = array('');
$options = array('objects' => '*', 'publishing' => true, 'extensions' => array('.php'));
$modx->cacheManager->clearCache($paths, $options);

See Also

Caching in xPDO
xPDOCacheManager

Custom Manager Pages

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.

The following window is an example of what information makes up a Namespace:

In the path, you can also use placeholders for MODx paths:

{core_path} - Resolves to the 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/

Using modAction and modMenu

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.

Creating the modAction

To create a modAction, go to System -> Actions. Right-click your Namespace from the 'Actions' tree and select "Create Action Here. The 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.

Creating the modMenu

From there, you can create your menu item by right clicking on an already-existing menu item (MODx recommends placing custom CMP's under
'Components'), and clicking 'Place Action Here'. This will load a window where you can enter details for the new modMenu:
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.

Returning the Page

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:

return $modx->smarty->fetch( '/path/to/templates/list.tpl' );

One common use of Smarty is to assign MODx configuration settings and lexicons to a "placeholder" which can then be used in your templates.
For example, in your controller file you might place the following code:

$modx->smarty->assign( '_lang', $this->modx->lexicon->fetch() );


$modx->smarty->assign() takes two parameters:

1. $tplVar [string] - The name of the placeholder.


2. $value [string|array] - An string or associative array of data (key => value) to load into the placeholder.

Here's an example of using placeholders:

<h2 class="modx-page-header">{$_lang.mycomponent}</h2>

This would output a standard MODx Manager page header with the content of the lexicon matching the "mycomponent" key.

Plain-Old HTML

Of course, you don't need to use Smarty if you don't want to. One could simply return the HTML code in their controller, instead of calling
$modx->smarty->fetch():

$o = '<div class="test"><h2>My Component</h2></div>';

return $o;

Scripts and CSS

Since ExtJS plays an important part in the MODx Manager, you will probably need to include your own JavaScript files for your components.

The best way to include a JavaScript file on your page is to use $modx->regClientStartupScript(). This function takes two parameters:

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.

Brief Overview of MODExt

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

require_once $basePath . '/config.core.php';


require_once MODX_CORE_PATH . 'config/' . MODX_CONFIG_KEY . '.inc.php';
require_once MODX_CONNECTORS_PATH . 'index.php';

Next, we simply have to 'handle' the, or pass the request data on to the appropriate Processor file.
$modx->request->handleRequest( array(
'processors_path' => $modx->getOption( 'core_path' ) . 'components/mycomponent/processors/',
'location' => ''
) );
?>

As you can see, handleRequest() accepts an array of options:

processors_path: The base directory where your component's processor files are found.
location: The subdirectory of processors_path to find the processor.
action: This tells MODx the filename of the Processor file to load. This value will be taken from the HTTP $_REQUEST['action']
parameter.

That's all there is to it! Your AJAX requests simply need to call your Connector file (with an 'action' parameter referring to the 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

Actions and Menus

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.

You can then load the topic via:

$modx->lexicon->load('tinymce:buttons');

modAction's are also easily hooked to modMenu's, which are abstract representations of the top menubar in the manager. Again, this lets 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

Custom Manager Pages


Internationalization

Custom Manager Pages Tutorial

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.

Explanation and Mental Preparation

"What's the big deal?" you might ask. "Why can't I just add an anchor tag somewhere that links to my PHP file and be done with it?"

<a href="/path/to/my/file.php">My Custom Manager Page</a>

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.

What we'll need:

A PHP file on the webserver which generates the text for our CMP (a.k.a. the controller).
A Namespace (i.e. a path) which defines a dedicated folder for our script(s).
A clickable Menu object (modMenu) which associates the clickable link to the action.
An Action object (modAction) which is an abstract representation of your file.
And optionally, a Lexicon entry which would allow you to translate the label on your menu item.

Maybe you're baffled by the complexity here, and to be fair, for simple scenarios, this is a bit more complicated than is strictly required, but 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.

Create the Namespace

You can think of the Namespace as a dedicated directory for your PHP file(s) that pertain to this particular manager page. Keep in mind 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.

Create the Action

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)

Create the Menu Object

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!

Make your CMP Translatable (Optional)

If you never intend on internationalizing your site, then you probably have no need to create a Lexicon entry. But if you do want to provide
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.

Create a Lexicon Directory

Go to your Namespace path (usually in your Extra's core/components/mycmp/ directory) and place a "lexicon/" directory in there. From there, 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/

Identify your Lexicon key

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 the Topic File

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:

$_lang['lexicon_entry_key'] = 'Translation for Entry';

Create the Entries (Provide the Translations)

Go ahead and add these entries to core/components/mycmp/lexicon/en/default.inc.php:

$_lang['mycmp'] = 'My CMP';


$_lang['mycmp.menu_desc'] = 'My custom manager page.';

Now, clear the site cache to reload the lexicon cache, via Site -> Clear Cache.

Troubleshooting / Errors

Having problems? Here are a couple things that you may have run into.

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

Reserved GET Parameters Inside the MODx Manager

The following is a list (currently incomplete) of GET parameters used by the MODx manager. You should avoid using any of these parameters in
Custom Manager Pages:

a – used to define an action


context_key – specifies one of your contexts (e.g. "web" or "mgr")
class_key – specifies a class name, e.g. when creating a Weblink or static resource
id -- specifies a page_id

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

More MODExt Components

Of course, there are more MODExt components at your disposal. For a full list (and source code to examine), browse to the
manager/assets/modext/widgets/core/ directory of your MODx installation.

Extending a MODExt Class

Extending a MODExt component is actually quite simple. Let's extend the MODx.grid.Grid class to create our own custom grid, complete with a
custom toolbar.

First, create a new JavaScript file and place the following code:

MyComponent.grid.MyGrid = function( config ) {


/* Class parent constructor */
MyComponent.grid.MyGrid.superclass.constructor.call( this, config );
};
Ext.extend( MyComponent.grid.MyGrid, MODx.grid.Grid, {
/* Class members will go here */
} );
/* Register "mycomponent-grid-mygrid" as an xtype */
Ext.reg( "mycomponent-grid-mygrid", MyComponent.grid.MyGrid );

Here, we've created our own class (MyComponent.grid.MyGrid) which extends MODx.grid.Grid. We have also registered
"mycomponent-grid-mygrid" as an Ext xtype, which can be used to display this grid in a FormPanel or other component. It has no additional
functionality -- yet!

Now, let's add some configuration options:


MyComponent.grid.MyGrid = function( config ) {
config = config || {};

/* Grid configuration options */


Ext.applyIf( config, {
id : "mycomponent-grid-mygrid",
title : _( "my_grid" ),
url : MyComponent.config.connectors_url + "list.php",
baseParams : {
action : "getlist"
},
paging : true,
autosave : true,
remoteSort : true,
/* Store field list */
fields : [ {
name : "id",
type : "int"
}, {
name : "name",
type : "string"
}, {
name : "menu"
} ],
/* Grid ColumnModel */
columns : [ {
header : _( "id" ),
dataIndex : "id",
sortable : true
}, {
header : _( "name" ),
dataIndex : "name",
sortable : true
} ],
/* Top toolbar */
tbar : [ {
xtype : "button",
text : _( "create" ),
handler : {
xtype : "mycomponent-window-create",
blankValues : true
},
scope : this
} ]
} );

/* Class parent constructor */


MyComponent.grid.MyGrid.superclass.constructor.call( this, config );
};

Ext.extend( MyComponent.grid.MyGrid, MODx.grid.Grid, {


/* Class members will go here */
} );

/* Register "mycomponent-grid-mygrid" as an xtype */


Ext.reg( "mycomponent-grid-mygrid", MyComponent.grid.MyGrid );

Our basic configuration sets the grid up to work with a "list" connector, using the "getlist" action parameter. It also sets up paging, sorting, 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

ExtJS 3.0 API Documentation


MODx.combo.ComboBox

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:

foreach( $items as $item ) {


$data[] = array(
'id' => $obj->get( 'id' ),
'name' => $obj->get( 'name' ),
'menu' => array(
array(
'text' => $modx->lexicon( 'my_lexicon' ),
'handler' => ' this.myHandler'
)
)
);
}

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:

Languages (IANA format)


Topics
Entries

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.

Loading and Using Lexicons

Lexicons must first be loaded if they are to be used in the front-end; however, this is a trivial process.

Lexicons Via Tag

To use a Lexicon Entry in a tag, simply do:

[[%key? &topic=`topicname` &namespace=`namespace_name` &language=`en`]]

The 'language', 'topic', and 'namespace' properties are optional; if the tag has been run earlier on the page with the same 'topic' property value,
that topic will have already been loaded. If 'topic' is not specified, it will assume 'default'. If 'namespace' is not specified, it will assume 'core', or 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');

Lexicons with Placeholders

Say we wanted to load a Lexicon Entry with some dynamic values we have in our page. For example, say we want a greeting on a website, 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]]!

And then in tag form:

[[!%welcome_message? &name=`John`]]

Or in PHP:

$modx->lexicon('welcome_message',array('name' => 'John'));

You can have an infinite number of placeholders for each Tag.

Note our ! prefix for the Tag; this makes sure the Tag isn't cached, since our string might be changing before the page cache
does.

Lexicons for Settings

So say you're creating System Settings for your 3rd Party Component (3PC). The syntax for auto-loading them into the Revolution Settings grid 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:

$_lang['name-of-component.key-name'] = 'Your translation here.';

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:

$_lang['setting_gallery.display_thumbs'] = 'Display Thumbnails';


$_lang['setting_gallery.display_thumbs_desc'] = 'When set to true, this will display thumbnails for
the gallery.';

And we're done!

Conclusion

Lexicons provide MODx Revolution users with a plethora of avenues and options to do their i18n work. Lexicons are composed of multiple 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

Creating Lexicons for Your Components

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!');

What are the Default Included Services?

A list of the core-included MODx Services is as follows:

See Also

modX.getService

modMail

What is modMail?
modMail is an abstract class that can be extended to provide mail services for Revolution. It cannot be run by itself, but must be extended with 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 marketing@modxcms.com, with the From
address being 'me@xpdo.org'. 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,'me@xpdo.org');
$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','marketing@modxcms.com');
$modx->mail->address('reply-to','me@xpdo.org');
$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','marketing@modxcms.com');
$modx->mail->address('to','mom@home.com');

And finally, the example code above will send a message to our error.log if the mail isn't sent for some reason (usually a server misconfiguration).

What if I want to use another email class?

Simple - just extend modMail with that class, then load your class via 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.

The Official Provider of modxcms.com has a URL of:


http://rest.modxcms.com/extras/
and comes packaged in with MODx Revolution 2.0.0.

Installing Packages

You can easily install packages by right-clicking on the package and clicking "Install". A console will load showing you the details of the 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:

The package should then install on your MODx installation.

Updating Packages

You can easily update any package that has been downloaded from a provider. Simply click the 'Check for Updates' context menu item (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.

Note the three modes when you uninstall a package:

Each is self-explanatory.

See Also

Transport Packages

What is a Transport Package?


The Internals of a Transport Package
The manifest.php file
Okay, what are these Vehicles?
Inside a Vehicle's Source
Resolvers and Validators
A Validator
A Resolver
Usage
Related Pages

What is a Transport Package?

A Transport Package is a collection of objects and files that can be used to "transport" data from one MODx installation to another; or even 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.

The Internals of a Transport Package

MODx Revolution automatically "unpacks", or unzips, your transport packages for you. Once done, a subdirectory in your core/packages 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.

manifest-vehicles - These are the Vehicles metadata, in array format.

Okay, what are these Vehicles?

Transport Vehicles are the parts of a Transport Package. A package can contain as many Vehicles as it likes. Vehicles also come in different
types; the currently available ones are:

xPDOObjectVehicle - For transporting database data


xPDOFileVehicle - For transporting files

In the 'manifest-vehicles' array, you'll see these keys for each vehicle:

vehicle_package - This tells us what type of package is holding these vehicles. Currently the only type is 'transport'.
vehicle_class - The class name of the type of Vehicle this is.
class - The class name of the DB object being transported, or xPDOFileVehicle if it's a file vehicle.
guid - A randomly generated GUID for the vehicle.
native_key - If the vehicle is a database object, this will be its primary key by which it is identified.
filename - Where the vehicle's source file can be found within the transport package's folder.
namespace - Certain packages use the 'namespace' field to group vehicles and other objects to make them uniquely identifiable within 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.

Inside a Vehicle's Source

Vehicles can actually have a few different files grouped with them, but we'll first concern ourselves with the main vehicle file, which is specified 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:

class - Similar to the manifest, the class type of the vehicle.


object - An array that contains the object information. For DB objects this will most likely be a JSON array representation of the DB table.
For files, it will be a PHP array with the source, target and name of the vehicle.
vehicle_class - Similar to the manifest, the class name of the vehicle.
vehicle_package - Similar to the manifest, the transport type of the vehicle.
guid - Similar to the manifest, a unique identifier for the vehicle.
package - Only applicable to xPDOObjectVehicles, this will most likely be 'modx' or blank.
signature - The filename signature for this vehicle.
native_key - Similar to the manifest. If the vehicle is a database object, this will be its primary key by which it is identified.

The xPDOObjectVehicle, or database vehicles, often have these extra keys:

preserve_keys - If true, the vehicle will try and preserve the primary key of the database record on install.
update_object - If true, the vehicle will UPDATE the object if it's found already in the database during install. If false, it will be skipped.
unique_key - The column name by which the database object can be uniquely identified - often this is not the primary key, 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:

validate - An array of arrays which contain validators, explained later.


resolve - An array of arrays which contain resolvers, explained later.

In xPDOFileVehicles, you will also see a directory with the same filename as the vehicle, minus the ".vehicle". If you open it, there will be the 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

What is a Transport Provider?


Usage
Related Pages

What is a Transport Provider?

Transport Providers in MODx are remote sources that one can download Transport Packages from. Simply by specifying a service URL, you 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.

require_once dirname(__FILE__) . '/build.config.php';

In this file, we'll want to define the location of our MODx Revolution installation so that the build script can know where to get the modX class, 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.

require_once MODX_CORE_PATH . 'model/modx/modx.class.php';

$modx= new modX();


$modx->initialize('mgr');
$modx->setLogLevel(modX::LOG_LEVEL_INFO);
$modx->setLogTarget(XPDO_CLI_MODE ? 'ECHO' : 'HTML');

Okay, it's time for the meat. Let's first off use $modx->loadClass to load the modPackageBuilder class. Then we'll instantiate an instance of it, 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/');

The modPackageBuilder::createPackage function has 3 parameters:


name, version, and release. For us,
we'll be doing quip-0.1-alpha7, so let's go with that.

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'),
),
),
));

Okay, a bit more meat here. We're introducing 2 new parameters:

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.

So, back to our script. To recap, so far we have:


<?php
/**
* Quip build script
*
* @package quip
* @subpackage build
*/
$mtime = microtime();
$mtime = explode(" ", $mtime);
$mtime = $mtime[1] + $mtime[0];
$tstart = $mtime;
set_time_limit(0);

$root = dirname(dirname(__FILE__)).'/';
$sources= array (
'root' => $root,
'build' => $root .'_build/',
'lexicon' => $root . '_build/lexicon/',
'resolvers' => $root . '_build/resolvers/',
'data' => $root . '_build/data/',
'source_core' => $root.'core/components/quip',
'source_assets' => $root.'assets/components/quip',
'docs' => $root.'core/components/quip/docs/',
);
unset($root);

/* override with your own defines here (see build.config.sample.php) */


require_once dirname(__FILE__) . '/build.config.php';
require_once MODX_CORE_PATH . 'model/modx/modx.class.php';

$modx= new modX();


$modx->initialize('mgr');
$modx->setLogLevel(modX::LOG_LEVEL_INFO);
$modx->setLogTarget(XPDO_CLI_MODE ? 'ECHO' : 'HTML');

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

Let's do the same with our system settings:


/* load system settings */
$settings = include $sources['data'].'transport.settings.php';

$attributes= array(
xPDOTransport::UNIQUE_KEY => 'key',
xPDOTransport::PRESERVE_KEYS => true,
xPDOTransport::UPDATE_OBJECT => false,
);
foreach ($settings as $setting) {
$vehicle = $builder->createVehicle($setting,$attributes);
$builder->putVehicle($vehicle);
}
unset($settings,$setting,$attributes);

Great! We've got our actions, menus and settings packaged in. Now, using our newfound knowledge about related objects, let's create a 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:

/* create the snippet */


$snippet= $modx->newObject('modSnippet');
$snippet->set('id',0);
$snippet->set('name', 'Quip');
$snippet->set('description', 'A simple commenting component.');
$snippet->set('snippet',file_get_contents($sources['source_core'].'/snippet.quip.php');

Great! Note how here we're actually using the file_get_contents() function to grab the contents of the snippet from our dev environment and 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.

Now, we had some properties on that snippet...how do we put those in?

$properties = include $sources['data'].'properties.inc.php';


$snippet->setProperties($properties);
$category->addMany($snippet);

You'll use modSnippet's setProperties function to pass in an array of property arrays. So, let's take a look at that properties.inc.php file:
<?php
/**
* Default snippet properties
*
* @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;

Simple enough. And now on to the chunks:

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

/* create category vehicle */


$attr = array(
xPDOTransport::UNIQUE_KEY => 'category',
xPDOTransport::PRESERVE_KEYS => false,
xPDOTransport::UPDATE_OBJECT => true,
xPDOTransport::RELATED_OBJECTS => true,
xPDOTransport::RELATED_OBJECT_ATTRIBUTES => array (
'Snippets' => array(
xPDOTransport::PRESERVE_KEYS => false,
xPDOTransport::UPDATE_OBJECT => true,
xPDOTransport::UNIQUE_KEY => 'name',
),
'Chunks' => array (
xPDOTransport::PRESERVE_KEYS => false,
xPDOTransport::UPDATE_OBJECT => true,
xPDOTransport::UNIQUE_KEY => 'name',
),
)
);
$vehicle = $builder->createVehicle($category,$attr);

Great! We've got our category vehicle, complete with all the related chunks and snippet. They'll be installed in the right category when our users
install our package, too - so it'll look nice and sharp!

Validators and Resolvers

Validators and resolvers are basically scripts that run during the install process. Validators are run pre-install; meaning that they are run before 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);

Okay, first things first. File resolvers take two options:

source - This is the target directory or script. If it's a file resolver, it must not end with a trailing slash and must be a valid directory. If it's 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.

We have our lexicon structured nicely in our \core/components/quip/lexicon directory:


As you can see, we have a subdirectory as 'en', the IANA code for English. Then, we have a 'default.inc.php' - this represents the 'default' lexicon
topic. Should we want to create separate lexicon topics, we would name them 'topicname.inc.php'.

As of MODx Revolution RC-2, MODx will automatically find the lexicons in your lexicon directory, assuming that you put them in this structure 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.

Package Attributes: License, Readme and Setup Options

Each package has what are called 'package attributes', which can be passed to any resolver or validator. You could pass pretty much 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!

So let's use these in our build script:

/* now pack in the license file, readme and setup options */


$builder->setPackageAttributes(array(
'license' => file_get_contents($sources['docs'] . 'license.txt'),
'readme' => file_get_contents($sources['docs'] . 'readme.txt'),
'setup-options' => array(
'source' => $sources['build'] . 'setup.options.php'
),
));

Obviously our license and readme values are being passed the contents of our license and readme files. We're doing them via file_get_contents()
so that we can still store the actual files in the 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.

Our setup.options.php file looks like this:

<?php
/**
* Build the setup options form.
*
* @package quip
* @subpackage build
*/
/* set some default values */
$values = array(
'emailsTo' => 'my@emailhere.com',
'emailsFrom' => 'my@emailhere.com',
'emailsReplyTo' => 'my@emailhere.com',
);
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);

$setting = $modx->getObject('modSystemSetting',array('key' => 'quip.emailsFrom'));


if ($setting != null) { $values['emailsFrom'] = $setting->get('value'); }
unset($setting);

$setting = $modx->getObject('modSystemSetting',array('key' => 'quip.emailsReplyTo'));


if ($setting != null) { $values['emailsReplyTo'] = $setting->get('value'); }
unset($setting);
break;
case xPDOTransport::ACTION_UNINSTALL: break;
}

$output = '<label for="quip-emailsTo">Emails To:</label>


<input type="text" name="emailsTo" id="quip-emailsTo" width="300" value="'.$values['emailsTo'].'" />
<br /><br />

<label for="quip-emailsFrom">Emails From:</label>


<input type="text" name="emailsFrom" id="quip-emailsFrom" width="300" value=
"'.$values['emailsFrom'].'" />
<br /><br />

<label for="quip-emailsReplyTo">Emails Reply-To:</label>


<input type="text" name="emailsReplyTo" id="quip-emailsReplyTo" width="300" value=
"'.$values['emailsReplyTo'].'" />';

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

$modx->log(modX::LOG_LEVEL_INFO,"\nPackage Built.\nExecution time: {$totalTime}\n");


exit();

Great, we're done! You'll only need to run this script now, and viola! A fully zipped transport package file will appear in your core/packages
directory.

View the Source

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.

Steps to extending modUser

1. ) Create the schema and generate a model

The first thing we need to accomplish, is to create an extended user schema which extends modUser. Please note that there is no 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>

<!-- track all user phone numbers -->


<object table="phone_numbers" extends="xPDOSimpleObject">
<field key="user" dbtype="int" phptype="integer" null="false" default="0" index="index" />
<field key="areacode" dbtype="varchar" precision="3" phptype="string" null="false" default=""
/>
<field key="number" dbtype="varchar" precision="7" phptype="string" null="false" default="" />
<aggregate alias="extUser" local="user" foreign="id" cardinality="one" owner="foreign" />
</object>

<!-- user extension -->


<object table="table2" extends="xPDOSimpleObject">
<field key="user" dbtype="int" phptype="integer" null="false" default="0" index="index" />
<field key="myspaceurl" dbtype="varchar" precision="255" phptype="string" null="false" />

<aggregate alias="extUser" local="user" foreign="id" cardinality="one" owner="foreign" />


</object>
</model>

You will need to parse and create the model map associated with this schema. As this process is out of the scope of this topic, please refer to
Using Custom Database Tables in your 3rd Party Components for further information.

2.) Edit the extuser.class.php

To access the extended class, we have to inform modUser that the user in question has been extended. The 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');
}
}
?>

3.) Create (or edit) extension_packages in System Settings

Access the System settings found in the System menu of the manager, and search for: extension_packages.

If the key already exists, add

, extendeduser:{core_path}components/extendeduser/model/

Note the comma at the beginning of the Value.

If the key does not exists


Create a new system setting with name of extension_packages
Key of extension_packages
Fieldtype: Textfield
value

extendeduser:{core_path}components/extendeduser/model/

Note the absence of a comma at the beginning of the Value.

4.) Final Step Create a class to access and utilize your extended class
<?php
/**
* File sample.class.php (requires MODx Revolution 2.x)
* Created on Aug 18, 2010
* Project shawn_wilkerson
* @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 ()) {

/* Import modx as a reference */


$this->modx= & $modx;

/* Establish the environment */


$this->extPath= $modx->getOption('core_path',null,
MODX_CORE_PATH).'components/extendeduser/';
$this->modx->addPackage('extendeduser', $this->extPath .'model/', 'ut_');
$this->_config= array_merge(array (
'userID' => $this->modx->user->get('id'),
), $config);

/* Define the user */


$this->userObj = $this->setUser($this->_config['userID']);
$this->userID = $this->userObj->get('id');
}

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

5.) Accessing the class

In our example we will be accessing our extended user throughout our site, therefore we load it as a service as shown in the following example:
<?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):

$this->modx->getCount('extPhones', array('user' => $this->userID));


$this->modx->getCount('extPhones');

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.

Suggested additional considerations

The model files can be edited with methods and descriptions. Take a look at much of the MODx / xPDO models and you will see this 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

Extended modUser Classes currently Available

modActiveDirectory an application which provides interaction with a Microsoft Domain Controller

rpm extension allows people to login via Facebook and other social networking medium

Other Development Resources

This section covers alternative development options and information for MODx developers.

Loading MODx Externally


Loading the MODx Object

Using the MODx object (and all of its respective classes) is quite simple. All you need is this code:

require_once '/absolute/path/to/modx/config.core.php';
require_once MODX_CORE_PATH.'config/'.MODX_CONFIG_KEY.'.inc.php';
require_once MODX_CORE_PATH.'model/modx/modx.class.php';
$modx = 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

The MODx Revolution API Documentation can be found here:

http://api.modxcms.com

Class Reference

modX

The modX Class

This is the base class of MODx Revolution; it is used for many of the main processing methods of MODx. It extends the xPDO class.

Methods

See Also

modX.getChunk

modX::getChunk

Processes and returns the output from an HTML chunk by name.

Syntax

API Doc: http://api.modxcms.com/modx/modX.html#getChunk


string getChunk (string $chunkName, [array $properties = array ()])

Example

Lets process this chunk and output its value. We have this Chunk, called "WelcomeChunk":

<p>Welcome [[+name]]!</p>

We'll put this in our Snippet:

$output = $modx->getChunk('WelcomeChunk',array(
'name' => 'John',
));
return $output;

This code outputs this:

<p>Welcome John!</p>

See Also

Chunks

modX.getLoginUserName

modX::getLoginUserName

Returns the current user name, for the current or specified context.

Syntax

API Doc: http://api.modxcms.com/modx/modX.html#getLoginUserName

string getLoginUserName ([string $context = ''])

Example

Grab the user's username in the current Context.

$username = $modx->getLoginUserName();

See Also

Contexts

modX.getPlaceholder

modX::getPlaceholder

Get a placeholder value by key.

Syntax

API Doc: http://api.modxcms.com/modx/modX.html#getPlaceholder


mixed getPlaceholder (string $key)

Example

$value = $modx->getPlaceholder('MyPlaceholder');

See Also

modX.setPlaceholder
modX.setPlaceholders
modX.toPlaceholder
modX.toPlaceholders

modX.setPlaceholder

modX::setPlaceholder

Sets a Placeholder value.

Syntax

API Doc: http://api.modxcms.com/modx/modX.html#setPlaceholder

void setPlaceholder (string $key, mixed $value)

Example

$modx->setPlaceholder('name','Barry');

See Also

modX.getPlaceholder
modX.setPlaceholders
modX.toPlaceholder
modX.toPlaceholders

modX.setPlaceholders

modX::setPlaceholders

Sets a collection of placeholders stored in an array or as object vars.


An optional namespace parameter can be prepended to each placeholder key in the collection, to isolate the collection of placeholders.

Note that unlike 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

API Doc: http://api.modxcms.com/modx/modX.html#setPlaceholders

void setPlaceholders (array|object $placeholders, [string $namespace = ''])

Example

Add an array of placeholders, and prefix 'my.' to their key.


$modx->setPlaceholders(array(
'name' => 'John',
'email' => 'jdoe@gmail.com',
),'my.');

See Also

modX.toPlaceholder
modX.toPlaceholders
modX.setPlaceholder
modX.getPlaceholder

modX.addEventListener

modX::addEventListener

Add a plugin to the eventMap within the current execution cycle.

Syntax

API Doc: http://api.modxcms.com/modx/modX.html#addEventListener

boolean addEventListener (string $event, integer $pluginId)

Example

Add a Plugin with ID 2 to the Event 'OnChunkPrerender':

$modx->addEventListener('OnChunkPrerender',12);

See Also

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

API Doc: http://api.modxcms.com/modx/modX.html#checkForLocks

void checkForLocks (integer $id, string $action, string $type)

Example

Check for locks on the edit_chunk action.

if ($modx->checkForLocks($modx->getLoginUserID(),'edit_chunk','edit');

See Also

modX
modX.checkSession

modX::checkSession

Checks to see if the user has a session in the specified context.

Syntax

API Doc: http://api.modxcms.com/modx/modX.html#checkSession

boolean checkSession ([string $sessionContext = 'web'])

Example

Check to see if the user has a session in the 'sports' context.

$modx->checkSession('sports');

See Also

modX

modX.executeProcessor

modX::executeProcessor

Executes a specific processor. The only argument is an array, which can take the following values:

action - The action to take, similar to connector handling.


processors_path - If specified, will override the default MODx processors path.
location - A prefix to load processor files from, will prepend to the action parameter.

Syntax

API Doc: http://api.modxcms.com/modx/modX.html#executeProcessor

mixed executeProcessor (array $options)

Example

Execute the Context getList processor:

$modx->executeProcessor(array(
'location' => 'context',
'action' => 'getList',
));

See Also

modX

modX.getAuthenticatedUser

modX::getAuthenticatedUser
Gets the user authenticated in the specified context.

Syntax

API Doc: http://api.modxcms.com/modx/modX.html#getAuthenticatedUser

unknown getAuthenticatedUser ([string $contextKey = ''])

Example

Get the authenticated user for the 'sports' context:

$user = $modx->getAuthenticatedUser('sports');

See Also

modX

modX.getCacheManager

modX::getCacheManager

Get an extended xPDOCacheManager instance responsible for MODx caching.

Overrides xPDO::getCacheManager.

Syntax

API Doc: http://api.modxcms.com/modx/modX.html#getCacheManager

object getCacheManager()

Example

Get the Cache Manager to set a dummy cache file.

$cacheManager = $modx->getCacheManager();
$cacheManager->set('testcachefile','test123');

See Also

modX

modX.getChildIds

modX::getChildIds

Gets all of the child resource ids for a given resource.

Syntax

API Doc: http://api.modxcms.com/modx/modX.html#getChildIds

array getChildIds ([integer $id = null], [integer $depth = 10])

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

Get the configuration for the site.

Syntax

API Doc: http://api.modxcms.com/modx/modX.html#getConfig

array getConfig ()

Example

Get the site config into an array.

$config = $modx->getConfig();

See Also

modX

modX.getContext

modX::getContext

Retrieve a context by name without initializing it.

Within a request, contexts retrieved using this function will cache the context data into the modX::$contexts array to avoid loading the same
context multiple times.

Syntax

API Doc: http://api.modxcms.com/modx/modX.html#getContext

&$modContext getContext (string $contextKey)

Example

Get the 'sports' Context.

$ctx = $modx->getContext('sports');

See Also

Page: Contexts

Page: modX
Page: modX.getContext

Page: Contexts
Page: modX

Page: modX.getContext

modX.getEventMap

modX::getEventMap

Gets a map of events and registered plugins for the specified context.

Syntax

API Doc: http://api.modxcms.com/modx/modX.html#getEventMap

array getEventMap (string $contextKey)

Example

Get the event map for the current Context.

$map = $modx->getEventMap();

See Also

modX

modX.getLoginUserID

modX::getLoginUserID

Returns the current user ID, for the current or specified context.

Syntax

API Doc: http://api.modxcms.com/modx/modX.html#getLoginUserID

string getLoginUserID ([string $context = ''])

Example

Get the current login user ID for the 'sports' context.

$id = $modx->getLoginUserID('sports');

See Also

modX

modX.getParentIds

modX::getParentIds

Gets all of the parent resource ids for a given resource.


Syntax

API Doc: http://api.modxcms.com/modx/modX.html#getParentIds

array getParentIds ([integer $id = null], [integer $height = 10])

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

Gets the MODx parser.

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

API Doc: http://api.modxcms.com/modx/modX.html#getParser

object getParser()

Example

Get the MODx Parser object.

$parser = $modx->getParser();

See Also

modX

modX.getRegisteredClientScripts

modX::getRegisteredClientScripts

Returns all registered JavaScript and HTML blocks.

Syntax

API Doc: http://api.modxcms.com/modx/modX.html#getRegisteredClientScripts

string getRegisteredClientScripts ()

Example
Get all registered scripts into an array.

$scripts = $modx->getRegisteredClientScripts();

See Also

modX

modX.getRegisteredClientStartupScripts

modX::getRegisteredClientStartupScripts

Returns all registered startup CSS, JavaScript, or HTML blocks.

Syntax

API Doc: http://api.modxcms.com/modx/modX.html#getRegisteredClientStartupScripts

string getRegisteredClientStartupScripts ()

Example

Get all registered startup scripts into an array.

$startupScripts = $modx->getRegisteredClientStartupScripts();

See Also

modX

modX.getRequest

modX::getRequest

Attempt to load the request handler class, if not already loaded. Defaults to modRequest.

Syntax

API Doc: http://api.modxcms.com/modx/modX.html#getRequest

boolean getRequest ([$string $class = 'modRequest'], [$path $path = ''])

Example

Load a custom Request handler class called 'myRequest' from '/path/to/myrequest.class.php':

$modx->getRequest('myRequest','/path/to/');

See Also

modX

modX.getResponse
modX::getResponse

Attempt to load the response handler class, if not already loaded. Defaults to modResponse.

Syntax

API Doc: http://api.modxcms.com/modx/modX.html#getResponse

boolean getResponse ([$string $class = 'modResponse'], [$path $path = ''])

Example

Load a custom Response handler class called 'myResponse' from '/path/to/myresponse.class.php':

$modx->getRequest('myResponse','/path/to/');

See Also

modX

modX.getService

modX::getService

Load and return a named service class instance.

Syntax

API Doc: http://api.modxcms.com/modx/modX.html#getService

object getService (string $name, [string $class = ''], [string $path = ''], [array $params = array
()])

Examples

Get the modSmarty service.

$modx->getService('smarty','smarty.modSmarty');

Get a custom, user-defined service called 'modTwitter' from a custom path ('/path/to/modtwitter.class.php'), and pass in some custom parameters.

$modx->getService('twitter','modTwitter','/path/to/',array(
'api_key' => 3212423,
));
$modx->twitter->tweet('Success!');

See Also

modX
MODx Services

modX.getSessionState

modX::getSessionState
Returns the state of the SESSION being used by modX.

The possible values for session state are:

modX::SESSION_STATE_UNINITIALIZED
modX::SESSION_STATE_UNAVAILABLE
modX::SESSION_STATE_EXTERNAL
modX::SESSION_STATE_INITIALIZED

Syntax

API Doc: http://api.modxcms.com/modx/modX.html#getSessionState

integer getSessionState ()

Example

$state = $modx->getSessionState();

See Also

modX

modX.getTree

modX::getTree

Get a site tree from a single or multiple modResource instances.

Syntax

API Doc: http://api.modxcms.com/modx/modX.html#getTree

array getTree ([int|array $id = null], [int $depth = 10])

Example

Get a tree for the Resource with ID 12. Only go 5 levels deep.

$treeArray = $modx->getTree(12,5);

See Also

modX

modX.getUser

modX::getUser

Get the current authenticated User and assigns it to the modX instance.

Syntax

API Doc: http://api.modxcms.com/modx/modX.html#getUser

modUser getUser ([string $contextKey = ''])


Example

Get the current auth'ed user and print out its username.

$user = $modx->getUser();
echo $user->get('username');

See Also

modX

modX.getVersionData

modX::getVersionData

Gets the modX core version data. The array contains the following keys (examples for version "MODx Revolution 2.0.0-beta-3"):

version - The current version number, eg: 2


major_version - The current major version number, eg: 0
minor_version - The current minor version number, eg: 0
patch_level - The current release level, eg: 'beta-3'
code_name - The code name for the product, eg: 'Revolution'
full_version - A compiled full version name, eg: '2.0.0-beta-3'
full_appname - The entire version name, eg: 'MODx Revolution 2.0.0-beta-3'

Syntax

API Doc: http://api.modxcms.com/modx/modX.html#getVersionData

array getVersionData ()

Example

Print out the current full version:

$vers = $modx->getVersionData();
echo $vers['full_version'];

See Also

modX

modX.handleRequest

modX::handleRequest

Initialize, cleanse, and process a request made to a modX site.

Syntax

API Doc: http://api.modxcms.com/modx/modX.html#handleRequest

mixed handleRequest ()

Example

Handle the current request.


$modx->handleRequest();

See Also

modX

modX.hasPermission

modX::hasPermission

Returns true if user has the specified policy permission.

Syntax

API Doc: http://api.modxcms.com/modx/modX.html#hasPermission

boolean hasPermission (string $pm)

Example

Deny the user access if they don't have the permission 'edit_chunk' in their loaded Policies.

if (!$modx->hasPermission('edit_chunk')) die('Access Denied!');

See Also

Policies

modX.initialize

modX::initialize

Initializes the modX engine into a Context.

This includes preparing the session, pre-loading some common classes and objects, the current site and context settings, extension packages
used to override session handling, error handling, or other initialization classes.

Syntax

API Doc: http://api.modxcms.com/modx/modX.html#initialize

void initialize ([string $contextKey = 'web'])

Example

Initialize the 'sports' Context.

$modx->initialize('sports');

See Also

Contexts

modX.invokeEvent
modX::invokeEvent

Invokes a specified Event with an optional array of parameters.

Syntax

API Doc: http://api.modxcms.com/modx/modX.html#invokeEvent

void invokeEvent (string $eventName, [array $params = array ()])

Example

Invoke the OnChunkRender event:

$modx->invokeEvent('OnChunkRender',array(
'id' => $chunk->get('id'),
));

See Also

modX

modX.lexicon

modX::lexicon

Grabs a processed Lexicon Entry.

This may also be a modLexicon object as well, if the Lexicon has been loaded. PHP supports having objects and methods with
the same name.

Syntax

API Doc: http://api.modxcms.com/modx/modX.html#lexicon

void lexicon (string $key, [array $params = array()])

Example

Output the translation of the 'welcome_message' Entry, and sets the 'name' Placeholder in it.

echo $modx->lexicon('welcome_message',array('name' => 'John'));

See Also

Internationalization

modX.makeUrl

modX::makeUrl

Generates a URL representing a specified resource.

The scheme indicates in what format the URL is generated.

-1 : (default value) URL is relative to site_url


0 : see http
1 : see https
full : URL is absolute, prepended with site_url from config
abs : URL is absolute, prepended with base_url from config
http : URL is absolute, forced to http scheme
https : URL is absolute, forced to https scheme

Syntax

API Doc: http://api.modxcms.com/modx/modX.html#makeUrl

string makeUrl (integer $id, [string $context = ''], [string $args = ''], [mixed $scheme = -1])

Examples

Make a URL for the Resource with ID 4.

$url = $modx->makeUrl(4);

Make a URL for the Resource with ID 12, but make sure it's in HTTPS.

$url = $modx->makeUrl(12,'','','https');

modX.parseChunk

modX::parseChunk

Parse a chunk using an associative array of replacement variables.

Syntax

API Doc: http://api.modxcms.com/modx/modX.html#parseChunk

string parseChunk (string $chunkName, array $chunkArr, [string $prefix = '[[+'], [string $suffix =
']]'])

Example

$output = $modx->parseChunk('myChunk',array('name' => 'John'));

See Also

modX
modX.getChunk

modX.regClientCSS

modX::regClientCSS

Register CSS to be injected inside the HEAD tag of a resource.

Syntax

API Doc: http://api.modxcms.com/modx/modX.html#regClientCSS

void regClientCSS (string $src)


Example

Register a CSS file to the HEAD tag:

$modx->regClientCSS('assets/css/style.css');

See Also

modX
modX.regClientHTMLBlock
modX.regClientScript
modX.regClientStartupHTMLBlock
modX.regClientStartupScript
modX.getRegisteredClientScripts
modX.getRegisteredClientStartupScripts

modX.regClientHTMLBlock

modX::regClientHTMLBlock

Register HTML to be injected before the closing BODY tag.

Syntax

API Doc: http://api.modxcms.com/modx/modX.html#regClientHTMLBlock

void regClientHTMLBlock (string $html)

Example

Inject a footer into the page.

$modx->regClientHTMLBlock('<div id="footer">(c) 2009 MODx</div>');

See Also

modX
modX.regClientCSS
modX.regClientScript
modX.regClientStartupHTMLBlock
modX.regClientStartupScript
modX.getRegisteredClientScripts
modX.getRegisteredClientStartupScripts

modX.regClientScript

modX::regClientScript

Register JavaScript to be injected before the closing BODY tag.

Syntax

API Doc: http://api.modxcms.com/modx/modX.html#regClientScript

void regClientScript (string $src, [boolean $plaintext = false])

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

Register HTML to be injected before the closing HEAD tag.

Syntax

API Doc: http://api.modxcms.com/modx/modX.html#regClientStartupHTMLBlock

void regClientStartupHTMLBlock (string $html)

Example

Render a faux tag element before the end of the HEAD.

$modx->regClientStartupHTMLBlock('<tag></tag>');

See Also

modX
modX.regClientCSS
modX.regClientHTMLBlock
modX.regClientScript
modX.regClientStartupScript
modX.getRegisteredClientScripts
modX.getRegisteredClientStartupScripts

modX.regClientStartupScript

modX::regClientStartupScript

Register JavaScript to be injected inside the HEAD tag of a resource.

Syntax

API Doc: http://api.modxcms.com/modx/modX.html#regClientStartupScript

void regClientStartupScript (string $src, [boolean $plaintext = false])

Example

Register some JS to the start of the page:


$modx->regClientStartupScript('assets/js/onload.js');

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

API Doc: http://api.modxcms.com/modx/modX.html#reloadConfig

array reloadConfig ()

Example

$modx->reloadConfig();

See Also

modX
Settings
System Settings

modX.removeAllEventListener

modX::removeAllEventListener

Remove all registered events for the current request.

Syntax

API Doc: http://api.modxcms.com/modx/modX.html#removeAllEventListener

void removeAllEventListener ()

Example

Eliminate any other events from firing:

$modx->removeAllEventListener();

See Also

modX
modX.removeEventListener

modX::removeEventListener

Remove an event from the eventMap so it will not be invoked.

Syntax

API Doc: http://api.modxcms.com/modx/modX.html#removeEventListener

boolean removeEventListener (string $event)

Example

Prevent any Events from firing on 'OnChunkRender':

$modx->removeEventListener('OnChunkRender');

See Also

modX

modX.runSnippet

modX::runSnippet

Process and return the output from a PHP snippet by name.

Syntax

API Doc: http://api.modxcms.com/modx/modX.html#runSnippet

string runSnippet (string $snippetName, [array $params = array ()])

Example

Run the 'Welcome' snippet with some custom parameters:

$output = $modx->runSnippet('Welcome',array(
'name' => 'John'
));
echo $output; // prints 'Welcome John!'

See Also

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

API Doc: http://api.modxcms.com/modx/modX.html#sendError

void sendError ([string $type = ''], [array $options = array()])

Examples

Send an Unavailable 503 error page.

$modx->sendError('unavailable');

Send a Fatal 500 error page.

$modx->sendError('fatal');

See Also

modX

modX.sendErrorPage

modX::sendErrorPage

Send the user to a MODx virtual error page.

Syntax

API Doc: http://api.modxcms.com/modx/modX.html#sendErrorPage

void sendErrorPage ([array $options = null])

Example

Send the user to the default Error page for the site.

$modx->sendErrorPage();

See Also

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

API Doc: http://api.modxcms.com/modx/modX.html#sendForward


void sendForward (integer $id, [string $options = null])

Example

Send the user to Resource ID 234 without actually changing the URL.

$modx->sendForward(234);

See Also

modX
modX.sendRedirect
modX.sendErrorPage

modX.sendRedirect

modX::sendRedirect

Sends a redirect to the specified URL using the specified method.

Valid $type values include:

REDIRECT_REFRESH - Uses the header refresh method


REDIRECT_META - Sends a a META HTTP-EQUIV="Refresh" tag to the output
REDIRECT_HEADER - Uses the header location method

REDIRECT_HEADER is the default.

Syntax

API Doc: http://api.modxcms.com/modx/modX.html#sendRedirect

void sendRedirect (string $url, [integer $count_attempts = 0], [string $type = ''])

Examples

Send a redirection request to the Resource with ID 54.

$url = $modx->makeUrl(54);
$modx->sendRedirect($url);

Send a redirection request to modxcms.com. Do so via the META HTTP-EQUIV refresh tag.

$modx->sendRedirect('http://modxcms.com',0,REDIRECT_META);

See Also

modX
modX.sendForward
modX.sendErrorPage

modX.sendUnauthorizedPage

modX::sendUnauthorizedPage

Send the user to the MODx unauthorized page.


Syntax

API Doc: http://api.modxcms.com/modx/modX.html#sendUnauthorizedPage

void sendUnauthorizedPage ([array $options = null])

Example

Send the user to the unauth page.

$modx->sendUnauthorizedPage();

See Also

modX
modX.sendErrorPage
modX.sendForward
modX.sendRedirect

modX.setDebug

modX::setDebug

Sets the debugging features of the modX instance.

Syntax

API Doc: http://api.modxcms.com/modx/modX.html#setDebug

boolean|int setDebug ([boolean|int $debug = true], [boolean $stopOnNotice = false])

Example

Turn debug mode on, and tell the process to stop if Notices occur:

$modx->setDebug(true);

See Also

Page: modX.setDebug
Page: modX.setDebug

modX.switchContext

modX::switchContext

Switches the primary Context for the modX instance.

Be aware that switching contexts does not allow custom session handling classes to be loaded. The gateway defines the session handling that 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

API Doc: http://api.modxcms.com/modx/modX.html#switchContext

boolean switchContext (string $contextKey)


Example

Switch to the 'sports' Context.

$modx->switchContext('sports');

See Also

Contexts

modX.toPlaceholder

modX::toPlaceholder

Recursively validates and sets placeholders appropriate to the value type passed.

Syntax

API Doc: http://api.modxcms.com/modx/modX.html#toPlaceholder

void toPlaceholder (string $key, mixed $value, [string $prefix = ''], [string $separator = '.'])

Example

Set a placeholder and prefix its key with 'my.'

$modx->toPlaceholder('name','John','my');

See Also

modX.toPlaceholders
modX.setPlaceholder
modX.setPlaceholders
modX.getPlaceholder

modX.toPlaceholders

modX::toPlaceholders

Sets placeholders from values stored in arrays and objects.

Each recursive level adds to the prefix, building an access path using an optional separator.

Syntax

API Doc: http://api.modxcms.com/modx/modX.html#toPlaceholders

void toPlaceholders (array|object $subject, [string $prefix = ''], [string $separator = '.'])

Example

Set an array of placeholders and prefix with 'my.'


$modx->toPlaceholders(array(
'name' => 'John',
'email' => 'jdoe@gmail.com',
),'my');

See Also

modX.toPlaceholder
modX.setPlaceholder
modX.setPlaceholders
modX.getPlaceholder

modX.unsetPlaceholder

modX::unsetPlaceholder

Unsets a placeholder value by key.

Syntax

API Doc: http://api.modxcms.com/modx/modX.html#unsetPlaceholder

void unsetPlaceholder (string $key)

Example

$modx->unsetPlaceholder('myPlaceholder');

See Also

modX.unsetPlaceholders
modX.toPlaceholder
modX.toPlaceholders
modX.setPlaceholder
modX.setPlaceholders
modX.getPlaceholder

modX.unsetPlaceholders

modX::unsetPlaceholders

Unset multiple placeholders, either by prefix or an array of keys.

Syntax

API Doc: http://api.modxcms.com/modx/modX.html#unsetPlaceholders

void unsetPlaceholders (string|array $keys)

Example

Unset the 'my.name' and 'my.email' Placeholders.

$modx->unsetPlaceholders(array('my.name','my.email'));

Unset all Placeholders that are prefixed with 'my.'


$modx->unsetPlaceholders('my.');

See Also

modX.unsetPlaceholder
modX.setPlaceholder
modX.setPlaceholders
modX.toPlaceholder
modX.toPlaceholders

modChunk

The modChunk Class

This is the Chunk base class for MODx Revolution.

Methods

See Also
Page: (at)CHUNK
Page: modChunk.getContent

Page: modChunk.setContent
Page: modChunk
Page: (at)CHUNK

Page: modChunk

Page: modChunk.getContent
Page: modChunk.setContent

modChunk.getContent

modChunk::getContent

Get the source content of this chunk.

Syntax

API Doc: http://api.modxcms.com/modx/modChunk.html#getContent

void getContent ([ $options = array()])

Example

$chunk = $modx->getObject('modChunk',array('name' => 'MyChunk'));


if ($chunk) {
$content = $chunk->getContent();
}

See Also

Page: (at)CHUNK
Page: modChunk.getContent

Page: modChunk.setContent
Page: modChunk

Page: (at)CHUNK
Page: modChunk
Page: modChunk.getContent

Page: modChunk.setContent

modChunk.setContent

modChunk::setContent

Sets the content of this Chunk.

Syntax

API Doc: http://api.modxcms.com/modx/modChunk.html#

void setContent ( $content, [ $options = array()])

Example

$chunk->setContent('<h2>Hello!</h2>');

See Also

Page: (at)CHUNK

Page: modChunk.getContent
Page: modChunk.setContent

Page: modChunk
Page: (at)CHUNK

Page: modChunk
Page: modChunk.getContent
Page: modChunk.setContent

modUser

The modUser Class

This is the base User class for MODx Revolution.

Methods

See Also
Page: Users

Page: modUser
Page: Users
Page: modUser

modUser.isAuthenticated

modUser::isAuthenticated

Determines if this user is authenticated in a specific context.

Separate session contexts can allow users to login/out of specific sub-sites individually (or in collections).

Syntax

API Doc: http://api.modxcms.com/modx/modUser.html#isAuthenticated

boolean isAuthenticated ([string $sessionContext = 'web'])

Example

See if the User is logged into the 'web' context. If not, deny access and send to Unauthorized Page.

if (!$modx->isAuthenticated('web')) {
$modx->sendUnauthorizedPage();
}

See Also

Page: Users

Page: modUser
Page: Users
Page: modUser

modUser.addSessionContext

modUser::addSessionContext

Adds a new context to the user session context array.

Syntax

API Doc: http://api.modxcms.com/modx/modUser.html#addSessionContext

void addSessionContext (string $context)

Example

Add a 'sports' Context session to the user.

$modx->addSessionContext('sports');

See Also

Page: Users

Page: modUser
Page: Users

Page: modUser
modUser.changePassword

modUser::changePassword

Change the User password.

Syntax

API Doc: http://api.modxcms.com/modx/modUser.html#changePassword

boolean changePassword (string $newPassword, string $oldPassword)

Example

Change the password from 'boo123' to 'b33r4me'

$modx->changePassword('b33r4me', 'boo123');

See Also

Page: Users

Page: modUser
Page: Users
Page: modUser

modUser.endSession

modUser::endSession

Ends a user session completely, including all contexts.

Syntax

API Doc: http://api.modxcms.com/modx/modUser.html#endSession

void endSession ()

Example

End the user's session.

$user->endSession();

See Also

modUser

modUser.getSessionContexts

modUser::getSessionContexts

Returns an array of user session context keys.


Syntax

API Doc: http://api.modxcms.com/modx/modUser.html#getSessionContexts

array getSessionContexts ()

Example

Get all user seesion contexts for this user that is logged into the web and mgr contexts:

$keys = $user->getSessionContexts();
print_r($keys); // prints Array ( 'web', 'mgr' );

See Also

modUser

modUser.getSettings

modUser::getSettings

Gets all user settings in array format.

Syntax

API Doc: http://api.modxcms.com/modx/modUser.html#getSettings

array getSettings ()

Example

Get all the User Settings for this User.

$settings = $user->getSettings();

See Also

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

API Doc: http://api.modxcms.com/modx/modUser.html#hasSessionContext

boolean hasSessionContext (mixed $context)

Example

See if the User has a Session for the 'sports' Context:


if ($user->hasSessionContext('sports')) {
// do code here
}

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

API Doc: http://api.modxcms.com/modx/modUser.html#isMember

boolean isMember (mixed $groups)

Example

See if the User is a member of the 'Staff' group:

$user->isMember('Staff');

See if the User is a member of either the 'Staff' or 'Investors' groups.

$user->isMember(array('Staff','Investors'));

See Also

modUser

modUser.loadAttributes

modUser::loadAttributes

Loads the principal attributes that define a modUser security profile.

Syntax

API Doc: http://api.modxcms.com/modx/modUser.html#loadAttributes

void loadAttributes ( $target, [ $context = ''], [ $reload = false])

Example

Load attributes for the 'sports' context and the modResource target.

$user->loadAttributes('modResource','sports',true);

See Also
modUser

modUser.removeSessionContext

modUser::removeSessionContext

Removes a user session context.

Syntax

API Doc: http://api.modxcms.com/modx/modUser.html#removeSessionContext

void removeSessionContext (string|array $context)

Example

Remove the session for the User in the 'sports' Context.

$user->removeSessionContext('sports');

See Also

modUser
Contexts

modUser.removeSessionContextVars

modUser::removeSessionContextVars

Removes the session vars associated with a specific context.

Syntax

API Doc: http://api.modxcms.com/modx/modUser.html#removeSessionContextVars

void removeSessionContextVars (string $context)

Example

Remove all session vars for the User in the 'sports' Context.

$user->removeSessionContextVars('sports');

See Also

modUser
Contexts

modUser.removeSessionCookie

modUser::removeSessionCookie

Removes a session cookie for a user.

Syntax
API Doc: http://api.modxcms.com/modx/modUser.html#removeSessionCookie

void removeSessionCookie (string $context)

Example

Remove the Session Cookie for the User in the 'sports' Context.

$user->removeSessionCookie('sports');

See Also

Page: Users
Page: modUser

Page: Users
Page: modUser

Case Studies and Tutorials

Case Studies and Tutorials


This page contains a list of case studies and tutorials for various MODx Revolution scenarios.

Using Custom Database Tables in your 3rd Party Components

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

<?xml version="1.0" encoding="UTF-8"?>


<model package="storefinder" baseClass="xPDOObject" platform="mysql" defaultEngine="MyISAM"
phpdoc-package="storefinder" phpdoc-subpackage="model">

First we'll tell the browser and parser that this is XML code with a standard XML header. Next, we're going to create a "model" tag, and put 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 class="sfStore" table="sfinder_stores" extends="xPDOSimpleObject">

"Object" is our representation of a table, which will generate into an xPDOObject class when we're through. There are some attributes to 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:

<field key="name" dbtype="varchar" precision="100" phptype="string" null="false" default="" index="


index" />
<field key="address" dbtype="varchar" precision="255" phptype="string" null="false" default="" />
<field key="city" dbtype="varchar" precision="255" phptype="string" null="false" default="" />
<field key="state" dbtype="varchar" precision="255" phptype="string" null="false" default="" />
<field key="zip" dbtype="varchar" precision="10" phptype="string" null="false" default="0" index=
"index" />
<field key="country" dbtype="varchar" precision="20" phptype="string" null="false" default="" />
<field key="phone" dbtype="varchar" precision="20" phptype="string" null="false" default="" />
<field key="fax" dbtype="varchar" precision="20" phptype="string" null="false" default="" />
<field key="active" dbtype="int" precision="1" attributes="unsigned" phptype="integer" null="false"
default="0" />

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:

key - The key name of the column.


dbtype - The DB type - such as varchar, int, text, tinyint, etc.
precision - The precision of the field. Usually this is the max number of characters.
attributes - Only applies to some DB types; in integers you can set to "unsigned" to make sure that the value is always positive.
phptype - The corresponding PHP type of the DB field type.
null - If the field can be NULL or not.
default - The default starting value of the field should none be set.
index - An optional field, when set, will add a type of index to the field. Some of the values are "pk", "index", and "fk".

And we'll finish by closing the object and model tags:

</object>
</model>

So now we have a completed XML schema for our model! You can view the full version
here. Let's move on to the schema build script.

Building the Schema


Go ahead and create a 'build.config.php' file in your _build directory. It should contain this:

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

require_once dirname(__FILE__) . '/build.config.php';


include_once MODX_CORE_PATH . 'model/modx/modx.class.php';
$modx= new modX();
$modx->initialize('mgr');
$modx->loadClass('transport.modPackageBuilder','',false, true);
$modx->setLogLevel(modX::LOG_LEVEL_INFO);
$modx->setLogTarget(XPDO_CLI_MODE ? 'ECHO' : 'HTML');

$root = dirname(dirname(__FILE__)).'/';
$sources = array(
'root' => $root,
'core' => $root.'core/components/storefinder/',
'model' => $root.'core/components/storefinder/model/',
'assets' => $root.'assets/components/storefinder/',
'schema' => $root.'_build/schema/',
);

This will do a few things. First off, it starts up a nice execution time script for us, so we can see how long it takes to build the schema. Secondly, 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.

And finally, we want to actually parse this into a file:

$generator->parseSchema($sources['schema'].'storefinder.mysql.schema.xml', $sources['model']);

$mtime= microtime();
$mtime= explode(" ", $mtime);
$mtime= $mtime[1] + $mtime[0];
$tend= $mtime;
$totalTime= ($tend - $tstart);
$totalTime= sprintf("%2.4f s", $totalTime);

echo "\nExecution time: {$totalTime}\n";

exit ();

This block of code executes the schema parsing method, and then outputs the total time the script took to execute. Run it, and viola! Our
storefinder/core/model/storefinder/ directory is now filled with all of our map and class files!

Using our New Model


You may be asking, "Okay, that's great. Now how do I use these?" Well, xPDO makes it incredibly simple. Let's first create our snippet file in our
core/components/storefinder/ directory, and call it 'snippet.storefinder.php' -- we're going to tie into a file on the file system because it's easier to
edit it using a text editor, and we want a file on the file system for our build package.
Before we proceed, let's enable testing of this snippet directly from MODx. Since we're developing this in a separate directory from our MODx
install, let's create a snippet called 'StoreFinder' in our MODx Revolution instance, and put this inside of it (you'll need to change the first line to
the correct path):

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

Part 2 Coming Soon...

See Also
Generating the xPDO Model Code
http://svn.modxcms.com/docs/display/revolution/PHP+Coding+in+MODx+Revolution%2C+Pt.+I

Creating a Blog in MODx Revolution

Creating a Blog in MODx Revolution


Getting the Needed Extras
Needed Extras
Optional Extras
Creating your Blog Post Template
Header and Footer
The Post Info
The Post Content
Adding Comments to Posts
Setting up Tagging
Creating the Sections
Setting up the blogPost Chunk
Setting up Your Blog Home
Adding Posts
Page Structure Within the Sections
Adding a New Blog Post
Setting up Your Archives
Creating the Archives Resource
Setting up the Archivist Widget
Advanced Options
Adding a Moderator Group
Adding a "Latest Posts" widget
Adding a "Latest Comments" widget
Adding a "Most Used Tags" widget
Conclusion

Creating a Blog in MODx Revolution


This tutorial is here to help you setup a flexible, powerful blogging solution in MODx Revolution. Since MODx Revolution is not blogging software,
but rather a full-blown Content Application Platform, it doesn't come pre-packaged with a cookie-cutter blogging solution. You'll need to setup your
blog how you want it.

Fortunately, the tools to do so are already there for your taking. This tutorial will walk you through how to set them up. It's recommended 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.

Getting the Needed Extras


First off, you'll want to go ahead and download and install some Extras that we'll be using in our Blog. The following is a list of most
commonly-used Extras:

Needed Extras

getResources - For listing posts, pages and other Resources.


getPage - For pagination of listings.
Quip - For anything and everything in commenting.
tagLister - For managing tags and doing tag-based navigation.
Archivist - For managing your Archives section.

Optional Extras

Breadcrumbs - For displaying a breadcrumb navigation trail.


Gallery - For managing photo Galleries.
SimpleSearch - For adding a simple search box to your site.
getFeed - If you want to grab other feeds in your site, such as a Twitter feed.
Login - If you want to restrict commenting to logged in users only, you'll need this.

Creating your Blog Post Template


First off, you'll want to have a Template that's geared just for Blog Posts. Why? Well, if you want comments and special formatting or page
displays for your blog, you'll probably not want to have to do that for each Blog Post. So, the best route is to setup your own blog post template.
This tutorial already assumes you have a base Template for your normal pages on the site - we'll reference that later on as 'BaseTemplate'.

We'll create one called 'BlogPostTemplate'. Our content looks something like this:
[[$pageHeader]]
<div id="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]]`
&notifyEmails=`my@email.com`
&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.

Header and Footer

First off, you'll notice that I have two chunks: "pageHeader" and "pageFooter". These chunks contain my common HTML tags that I would put 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.

The Post Info

Next we get into the "info" of the post - basically the author and tags for the post. Let's look in detail:

<p class="post-info">
Posted on [[*publishedon:strtotime:date=`%b %d, %Y`]] |
Tags: [[*tags:notempty=`[[!tolinks? &items=`[[*tags]]` &key=`tag` &target=`1`]]`]] |
<a href="[[~[[*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!

The Post Content

Okay, back to our Template. We're in the content section now - note how we start with [[*introtext]]. This is a useful MODx Resource field - think 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.

Adding Comments to 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:

<div class="post-comments" id="comments">[[!Quip?


&thread=`blog-post-[[*id]]`
&replyResourceId=`19`
&closeAfter=`30`
]]
<br /><br />
[[!QuipReply?
&thread=`blog-post-[[*id]]`
&moderate=`1`
&moderatorGroup=`Moderators`
&closeAfter=`30`
]]
</div>

Okay, cool. Note we have two Snippet calls here - one for displaying the comments for this thread ( 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.

Creating the Sections


If you want your blog to have 'Sections' (also called Categories), you'll first need to create those Resources.

For this tutorial's purpose, we'll create 2 sections: "Personal" and "Technology". Go ahead and create 2 Resources in the root of your site, 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.

Setting up the blogPost Chunk

In that call, we also have a property called 'tpl' which we set to 'blogPost'. This is our Chunk that shows each result of our blog post listings. 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!

Setting up Your Blog Home


In our home page for our blog, which we've got in Resource ID 1 - our site start - we've got this:
[[!getResourcesTag?
&elementClass=`modSnippet`
&element=`getResources`
&tpl=`blogPost`
&parents=`34,35`
&limit=`5`
&includeContent=`1`
&includeTVs=`1`
&showHidden=`0`
&hideContainers=`1`
&cache=`0`
&pageVarKey=`page`
]]
[[!+page.nav:notempty=`
<div class="paging">
<ul class="pageList">
[[!+page.nav]]
</ul>
</div>
`]]

This allows us to show all posts from the two sections we've made, in Resources 34 and 35. It also allows us to filter by tag (since all our 'tolinks'
and 'tagLister' calls have a target of 1 (this Resource's ID). In other words, by putting our getResourcesTag call here, we have automatic tagging!

You could easily make this another page than your site_start (or ID 1) - just make sure to change the 'target' properties in your tagLister 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.

Page Structure Within the Sections

Before we start, though, it's important to note that how you structure your posts within the section is totally up to you. You can add year and 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.

Adding a New Blog Post

Okay - go ahead and create a new Resource, and set it's Template to 'BlogPostTemplate'. Then you can start writing your post. You can 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!

Setting up Your Archives


Great - you have your first blog post! And, you've got it so you can browse it in Sections as well. Now, you're going to want to set up some way of
browsing old blog posts. This is where 'Archvist' comes into play.

Creating the Archives Resource

Go ahead and place a Resource in your root called 'Archives', and give it an alias of 'archives'. Then inside the content, place this:
[[!getPage?
&element=`getArchives`
&elementClass=`modSnippet`
&tpl=`blogPost`
&hideContainers=`1`
&pageVarKey=`page`
&parents=`34,35`
&includeTVs=`1`
&toPlaceholder=`archives`
&limit=`10`
&cache=`0`
]]

<h3>[[+arc_month_name]] [[+arc_year]] Archives</h3>

[[+archives]]

[[!+page.nav:notempty=`
<div class="paging">
<ul class="pageList">
[[!+page.nav]]
</ul>
</div>
`]]

Look familiar? It's very similar to getResourcesTag, described above in our Section page. This time, getPage is wrapping the 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.

Setting up the Archivist Widget

Okay, so now you've got a Resource to browse archives in, but you need some way of generating the months that lists posts. That's 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

Adding a Moderator Group

So earlier, in our QuipReply call, we specified a moderatorGroup of 'Moderators'. Let's go ahead and create that User Group now.

Go to Security -> Access Controls, and create a new User Group called 'Moderators'. Add any users you want in the group (including yourself!)
and give them whatever role you want.

Then, go to the Context Access tab. Add an ACL (a row, basically) that gives this user group access in the 'mgr' context, with a minimum role 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.

Adding a "Latest Posts" widget

You're probably going to want a "Latest Posts" somewhere on the site, and no fear - adding it is quite easy.

First off, you'll want to place this call wherever you want the list to appear:

[[!getResources?
&parents=`34,35`
&hideContainers=`1`
&tpl=`latestPostsTpl`
&limit=`5`
&sortby=`publishedon`
]]

So we're telling 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>

And boom! Latest blog posts displaying on your site:

Adding a "Latest Comments" widget

What about a widget that shows a few of the latest comments across your posts? Simple - Quip packages a nice little snippet called
QuipLatestComments that can handle this easily.

Place the call wherever you want the comment list to show:
[[!QuipLatestComments? &tpl=`latestCommentTpl`]]

Now create a chunk called 'latestCommentTpl':

<li class="[[+cls]][[+alt]]">
<a href="[[+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.

Adding a "Most Used Tags" widget

This part is ridiculously easy; tagLister does this for you. Just place this wherever you want:

[[!tagLister? &tv=`tags` &target=`1`]]

And tagLister will check the TV 'tags', and create links that go to the target (here, Resource ID 1) with the top 10 tags being used. There's a 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.

PHP Coding in MODx Revolution, Pt. I

The Simple How


The Model
See Also

So, a lot of people have been asking about the new codebase. Is it coder-friendly? Will it be a big deviation from 0.9.6/Evolution? Does it support
OOP projects? Is it faster? Will it be easy to learn?

In these tutorials, we plan to answer those questions with a resounding, "yes."

The codebase in Revolution has switched to 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.

The Simple How


So, how does one actually get an object in the new modx? Well, used to, you had to do and remember a myriad of different functions:

// The old way of doing things in MODx 1.x and earlier


$doc = $modx->getDocument(23);
$doc = $modx->getDocument(45,'pagetitle,introtext');
$chunk = $modx->getChunk('chunkName');

// or even more convoluted


$res = $modx->db->select('id,username',$table_prefix.'.modx_manager_users');
$users = array();
if ($modx->db->getRecordCount($res))
{
while ($row = $modx->db->getRow($res)) {
array_push($users,$row);
}
}
return $users;

Not anymore. Things are much simpler, and there's really only a few functions you'll need. Lets look at some examples:
// getting a chunk with ID 43
$chunk = $modx->getObject('modChunk',43);

// getting a chunk with name 'TestChunk'


$chunk = $modx->getObject('modChunk',array(
'name' => 'TestChunk'
));

// getting a collection of chunk objects, then outputting their names


$chunks = $modx->getCollection('modChunk');
foreach ($chunks as $chunk) {
echo $chunk->get('name')."<br />\n";
}

// getting a resource (i.e. a page) that is published, with a alias of 'test'


$document = $modx->getObject('modResource',array(
'published' => 1,
'alias' => 'test',
));

The Model
So, you're probably asking, Where is the list of table names to object names map? It can be found in
"core/model/schema/modx.mysql.schema.xml". (You'll note the 'mysql' - yes, this means that MODx will in the near future support other
databases) From there you can view an XML representation of all the MODx DB tables.

For example, modChunk:

<object class="modChunk" table="site_htmlsnippets" extends="modElement">


<field key="name" dbtype="varchar" precision="50" phptype="string" null="false" default="" index="
unique" />
<field key="description" dbtype="varchar" precision="255" phptype="string" null="false" default=
"Chunk" />
<field key="editor_type" dbtype="int" precision="11" phptype="integer" null="false" default="0" />
<field key="category" dbtype="int" precision="11" phptype="integer" null="false" default="0" />
<field key="cache_type" dbtype="tinyint" precision="1" phptype="integer" null="false" default="0"
/>
<field key="snippet" dbtype="mediumtext" phptype="string" />
<field key="locked" dbtype="tinyint" precision="1" attributes="unsigned" phptype="boolean" null="
false" default="0" />
<aggregate alias="Category" class="modCategory" key="id" local="category" foreign="id"
cardinality="one" owner="foreign" />
</object>

You can also define your own schemas for your own components and add them as packages - more on that in a future article. Lets go into the
schema:

<object class="modChunk" table="site_htmlsnippets" extends="modElement">

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.

<field key="name" dbtype="varchar" precision="50" phptype="string" null="false" default="" index="


unique" />

This tag represents a column in the database. Most of these attributes are pretty straightforward.

<aggregate alias="modCategory" class="modCategory" key="id" local="category" foreign="id" cardinality=


"one" owner="foreign" />

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

PHP Coding in MODx Revolution, Pt. II

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:

// let's create a Template


$template = $modx->newObject('modTemplate');

// now, lets save some data into the fields


$template->set('templatename','TestTemplate');
$template->set('description','A test template.');

// we could have also done it like this:


$data = array(
'templatename' => 'TestTemplate',
'description' => 'A test template.',
);
$template->fromArray($data);

// okay, now we're ready. let's save.


if ($template->save() === false) {
die('An error occurred while saving!');
}

It's that simple.

Removing an Object
Okay, so assuming we have the same previous object and now want to remove it (you could also grab another object, of course), the code 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

PHP Coding in MODx Revolution, Pt. III

In MODx, form processing is handled by 'Processors', which are isolated files located in the MODx core directory. They are accessed through
'Connectors', which handle AJAX requests from the User Interface (UI), which require a REQUEST variable named 'action' that specifies 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

And now on to the processor:


<?php
/**
* @package modx
* @subpackage processors.element.chunk
*/
$modx->lexicon->load('chunk');

First off, we include the root index.php file for the processors, which does some slight variable checking and includes licensing. Then, we load 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');

// get rid of invalid chars


$_POST['name'] = str_replace('>','',$_POST['name']);
$_POST['name'] = str_replace('<','',$_POST['name']);

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

// invoke OnBeforeChunkFormSave event


$modx->invokeEvent('OnBeforeChunkFormSave',array(
'mode' => modSystemEvent::MODE_NEW,
'id' => $_POST['id'],
));

Events are pretty much the same invoke-wise in Revolution as they were in 096 - however they are more optimized in their loading.

$chunk = $modx->newObject('modChunk', $_POST);


$chunk->set('locked',isset($_POST['locked']));
$chunk->set('snippet',$_POST['chunk']);
$chunk->set('category',$category->get('id'));
if ($chunk->save() === false) {
return $modx->error->failure($modx->lexicon('chunk_err_save'));
}
Important: note the 2nd parameter of the newObject() method. This is basically the same as $obj->fromArray() - it allows you to specify an array of
key-value pairs to assign to the new object.

// invoke OnChunkFormSave event


$modx->invokeEvent('OnChunkFormSave',array(
'mode' => modSystemEvent::MODE_NEW,
'id' => $chunk->get('id'),
));

Again, more event invoking.

// log manager action


$modx->logManagerAction('chunk_create','modChunk',$chunk->get('id'));

Now, how manager actions work in Revolution is a little different. This stores a lexicon string key ('chunk_create'), the class key of the 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?

return $modx->error->success('',$chunk->get(array('id', 'name', 'description', 'locked',


'category')));

Now, send a success response back to the browser. The parameters of $modx->error->success() are as follows:

1: $message - A string message to send back. Used to report details about a success (or failure).
2: $object - An xPDOObject or array of data fields to convert into JSON and send back to the browser.

So basically, here, we're sending back the Chunk information - minus the content, which could be big and unnecessary and complicated to send.
This will allow the UI to handle the creation properly.

Next, we'll talk about how to create your own schemas and add them dynamically into the MODx framework, without having to modify the core.

Loading Pages in the Front-End via AJAX and jQuery Tabs

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

Creating the Resources


In the Resources you want to load via the tabs, you'll need to just create all your Resources with the Template being blank (or a minimal template
with only the things you want inside the tabs). This will make sure that we're not loading anything besides the wanted material - you wouldn't want
to load your whole page header and footer into each tab!

Doing the Front-End Loading


Now we'll use jQuery's fun tabs() command to create the front-end loading system. The code would look something like this (pulled from jquery
UI's docs):
<script type="text/javascript">
$(function() { $("#tabs").tabs(); });
</script>

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

Great! So this loads the pages via Ajax.

Wait, I want the Page Titles as the tab headers!


There are a few ways you can do this; one, you can use getResources, Wayfinder, or use a getField snippet.

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:

<li id="[[+id]"><a href="[[~[[+id]]]]" title="[[+longtitle]]">[[+pagetitle]]</a></li>

and in our tabs page:

<script type="text/javascript">
$(function() { $("#tabs").tabs(); });
</script>

<div id="tabs">
<ul>
[[getResources? &parents=`123` &depth=`1` &tpl=`myRowTpl` &includeContent=`1` &includeTVs=`1`]]
</ul>
</div>

Using Wayfinder

For Wayfinder, make sure your rowTpl template, which you can create as a Chunk named 'myRowTpl' (or whatever you want), looks like this:

<li[[+wf.id]][[+wf.classes]]><a href="[[+wf.link]]" title="[[+wf.title]]">[[+wf.linktext]]</a></li>

and in our tabs page:

<script type="text/javascript">
$(function() { $("#tabs").tabs(); });
</script>

<div id="tabs">
<ul>
[[Wayfinder? &startId=`123` &level=`1` &rowTpl=`myRowTpl`]]
</ul>
</div>

Using a getField Snippet

Or, you can use a Snippet such as this one to grab the pagetitle:
<?php
/**
* Grabs a field for a specified Resource
*/
/* setup some default properties */
$id = $modx->getOption('id',$scriptProperties,false);
$field = $modx->getOption('field',$scriptProperties,'pagetitle');

if ($id) { /* grab the resource object */


$resource = $modx->getObject('modResource',$id);
if ($resource == null) return '';
} else { /* if no id specified, use current doc */
$resource =& $modx->resource;
}

/* return the field value */


return $resource->get($field);
?>

Call this Snippet getField like so in our tabs page:

<script type="text/javascript">
$(function() { $("#tabs").tabs(); });
</script>

<div id="tabs">
<ul>
<li><a href="[[~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.

Managing Resources and Elements via SVN

The Problem
When working in collaboration, teams of developers and designers often collaborate via Subversion (SVN) to make development easier 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.

However, the solution in MODx Revolution is quite simple.

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:

if (!file_exists($file)) return '';


$o = include $file;
return $o;
You can then call it like so in your Static Resources:

[[include? &file=`/path/to/my/svn/checkout/snippet.php`]]

And you're done. You can also use tags within the 'file' parameter, such as this:

[[include? &file=`[[++assets_path]]/js/myscript.js`]]

Conclusion
This allows you to easily manage content via SVN. It can be achieved with Templates and TVs as well; just plop the include snippet wherever you
need filesystem-based files.

xPDO XML Schema File vs. Table Structure Examples


Before you Start
A simple table: session
The xPDO XML:
The MySQL CREATE TABLE statement:
Things to Note
system_settings
XML
MySQL
user_group_roles
XML

This is a work in progress

The XML files used by xPDO to define database tables are meant to be created in a certain format. One of the easiest ways to get familiar 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.

Before you Start


Note that the core/model/schema/modx.mysql.schema.xml schema file contains definitions of many tables, and we're using it as a reference
for all the tables mentioned on this page.

In this schema file, each table is defined as an object, and all objects are wrapped in the following model tag:

<model package="modx" baseClass="xPDOObject" platform="mysql" defaultEngine="MyISAM" phpdoc-package=


"modx" phpdoc-subpackage="">
<!-- all table objects are defined in here -->
</model>

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.

A simple table: session


The session table is a good table for a simple example because it contains no foreign keys; in other words, it has no composite or aggregate
relations to worry about.

The xPDO XML:


<object class="modSession" table="session" extends="xPDOObject">
<field key="id" dbtype="varchar" precision="40" phptype="string" null="false" index="pk"
default="" />
<field key="access" dbtype="int" precision="20" phptype="timestamp" null="true" attributes=
"unsigned" />
<field key="data" dbtype="text" phptype="string" />
</object>

The MySQL CREATE TABLE statement:

CREATE TABLE `modx_session` (


`id` varchar(40) NOT NULL default '',
`access` int(20) unsigned default NULL,
`data` text,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1

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

<object class="modSystemSetting" table="system_settings" extends="xPDOObject">


<field key="key" dbtype="varchar" precision="50" phptype="string" null="false" default=""
index="pk" />
<field key="value" dbtype="text" phptype="string" null="false" default="" />
<field key="xtype" dbtype="varchar" precision="75" phptype="string" null="false" default=
"textfield" />
<field key="namespace" dbtype="varchar" precision="40" phptype="string" null="false" default=
"core" />
<field key="area" dbtype="varchar" precision="255" phptype="string" null="false" default="" />
<field key="editedon" dbtype="timestamp" phptype="timestamp" null="false" attributes="ON
UPDATE CURRENT_TIMESTAMP" />

<aggregate alias="ContextSetting" class="modContextSetting" local="key" foreign="key"


cardinality="one" owner="local" />
<aggregate alias="Namespace" class="modNamespace" local="namespace" foreign="name"
cardinality="one" owner="foreign" />
</object>

MySQL

CREATE TABLE `modx_system_settings` (


`key` varchar(50) NOT NULL default '',
`value` text NOT NULL,
`xtype` varchar(75) NOT NULL default 'textfield',
`namespace` varchar(40) NOT NULL default 'core',
`area` varchar(255) NOT NULL default '',
`editedon` timestamp NOT NULL default '0000-00-00 00:00:00' on update CURRENT_TIMESTAMP,
PRIMARY KEY (`key`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1

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

CREATE TABLE `modx_user_group_roles` (


`id` int(10) unsigned NOT NULL auto_increment,
`name` varchar(255) NOT NULL,
`description` mediumtext,
`authority` int(10) unsigned NOT NULL default '9999',
PRIMARY KEY (`id`),
UNIQUE KEY `name` (`name`),
KEY `authority` (`authority`)
) ENGINE=MyISAM AUTO_INCREMENT=3 DEFAULT CHARSET=latin1

Adding Custom Fields to Manager Forms

Adding a Custom Field


Adding custom fields to manager forms - such as the Create Chunk, Update Resource, etc - in MODx Revolution is fairly straightforward. You just
use the On*FormRender Plugin events.

We want to add a field called 'Home' that puts an address field into the manager interface, and then stores it into the longtitle value (this is not the

best place to store it, but let's go along with it for tutorial purposes ).

To do so, we'd create a 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.

MODx Community Information

This section is currently under construction.

Getting a MODx Account

This section is under construction.

Simply signup here: http://modxcms.com/community/register/

Filing Bug Reports


This section is under construction.

Bug reports can be filed in JIRA at http://bugs.modx.com

Becoming a Core Contributor

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

But how do I get a sign-on?


Simple. Just register for a modxcms.com SSO (single-sign on) login and you're there! You'll instantly have access to Jira and Fisheye, and be
able to submit bugs and review recent commits. MODx team administrators will then grant you access according to team needs, and you'll be able
to quickly start submitting Jira patches and more!

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.

I've submitted my CLA, now what? I want to commit!


We applaud your eagerness. Honestly, no joke, we do. MODx needs eager developers. However, there is a level of relationship that we like to
develop with people before they become core devs. You've got access to Jira now, and probably have noticed some bugs or have some feature
requests. So use it! Submit those bugs and ideas to Jira, and include in Jira patches for them. Please, no political jokes hidden in the comments.
Really.

You can submit a patch by using 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).

My patch was rejected! What?!


Every so once in a while, a patch submitted doesn't make it into the core. It's not because we don't like you. In all honesty, Jason loves darts, and
before each patch approval he staples them to the wall and we choose the one he hits while blindfolded. (Okay, so that's not true. He's not
blindfolded.)

Sometimes a patch you submit wont make it in. That may be for a myriad of reasons:

The patch wasn't a part of the Core Design Philosophy


We decided to move the feature request to a component rather than the core
Someone else provided a more elegant patch
The patch caused too many other issues to arise
The patch was submitted in lolcode, which although we had a hoot reading, was pretty useless in the end

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

Recommended Development Tools and Environments for MODx Revolution

In developing MODx Revolution, the MODx Team has found the following environments invaluable:

Netbeans

Netbeans 6.8
Netbeans Subversion and JIRA plugins

Eclipse

Eclipse 3.2.+ (recommend latest 3.5.1)


Web Standard Tools Project (WST) 2.0.1 (http://download.eclipse.org/webtools/updates/)
Subclipse 1.6.5 (http://subclipse.tigris.org/update_1.6.x)
PHPEclipse 1.2.3 (http://update.phpeclipse.net/update/nightly)
Spket IDE 1.6.18 (http://spket.com/update/)

Installation

Simply install the latest Eclipse Classic


Start up eclipse / select a workspace
Use the Install Software option under the help menu
Right click and copy each of the links above (doing them in order doesn't hurt)
Click the "Add" button
Name the "repo" WST, Subclipse, PHPEclipse, or Spket, as it relates to the URL
Paste the URL
Click OK
Repeat for each of the links above as necessary
Individual notes:
WST - select the latest Web Tools Platform (takes quite a while)
Subclipse - simply install the Subclipse option
PHPEclipse - install everything offered
Spket - Install everything offered

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

Development Server Environments

We also MacPorts, XAMPP and MAMP, and the following tools/libraries in the development of MODx Revolution:
PHPUnit - this drives the PHP 5.1+ unit testing framework for xPDO, and we'll be adding a test harness to MODx 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

MODx PHP Coding Standards

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

Do not put parenthesis next to keywords. Put a space between.


Do put parenthesis next to function names.
Do not use parenthesis in return statements when it's not necessary. Example:

if ($test) {
}
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

Note these are not function arguments.

Use all lowercase letters.


Separate words with the underscore.

Function Arguments and Class Variables


The first letter is lowercase, rest are camelCase. Example:

class modFactor {
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

Constants must be in all UPPERCASE letters.


Use only if absolutely necessary.

File Structure

Always name PHP class files in name.class.php format.

Prefixing

Lexicon strings for Components need to be prefixed:

$_lang['mycomponent.welcome_message'] = 'Welcome!';

Always prefix class names; eg: 'finBank', 'finTransaction', etc.


Always prefix Chunk names; eg: 'finStatement', 'finDeposit'

Using GitHub

This section is under construction.

See Git Installation.

S-ar putea să vă placă și