ModX Revolution Docs 20101007



Comments



Description

MODx Official Documentation 1. Home . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.1 Getting Started . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.1.1 Server Requirements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.1.1.1 MySQL 5.0.51 Issues . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.1.2 Installation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.1.2.1 Basic Installation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.1.2.1.1 MODx Revolution on Debian . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.1.2.1.2 Lighttpd Guide . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.1.2.1.3 Problems with WAMPServer 2.0i . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.1.2.2 Advanced Installation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.1.2.3 Git Installation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.1.2.4 Troubleshooting Installation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.1.2.5 Successful Installation, Now What Do I Do? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.1.2.6 Using MODx Revolution from SVN . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.1.3 An Overview of MODx . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.1.3.1 Glossary of Revolution Terms . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.1.3.1.1 Explanation of Directory Structure . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.1.3.2 Roadmap . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.2 Making Sites with MODx . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.2.1 Structuring Your Site . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.2.1.1 Resources . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.2.1.1.1 Content Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.2.1.1.2 Named Anchor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.2.1.1.3 Static Resource . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.2.1.1.4 Symlink . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.2.1.1.5 Weblink . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.2.1.2 Templates . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.2.1.3 Chunks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.2.1.4 Using Snippets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.2.2 Tag Syntax . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.2.3 Customizing Content . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.2.3.1 Template Variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.2.3.1.1 Creating a Template Variable . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.2.3.1.2 Bindings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.2.3.1.3 Template Variable Output Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.2.3.1.4 Adding a Custom TV Input Type . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.2.3.1.5 Adding a Custom TV Output Type . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.2.3.2 Properties and Property Sets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.2.3.3 Input and Output Filters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3 Administering Your Site . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.1 Settings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.1.1 System Settings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.1.1.1 allow_duplicate_alias . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.1.1.2 allow_multiple_emails . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.1.1.3 allow_tags_in_post . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.1.1.4 auto_check_pkg_updates . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.1.1.5 auto_check_pkg_updates_cache_expire . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.1.1.6 auto_menuindex . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.1.1.7 blocked_minutes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.1.1.8 cache_action_map . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.1.1.9 cache_context_settings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.1.1.10 cache_db . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.1.1.11 cache_db_expires . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.1.1.12 cache_default . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.1.1.13 cache_disabled . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.1.1.14 cache_handler . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.1.1.15 cache_json . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.1.1.16 cache_json_expires . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.1.1.17 cache_lexicon_topics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.1.1.18 cache_noncore_lexicon_topics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.1.1.19 cache_resource . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.1.1.20 cache_resource_expires . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.1.1.21 cache_scripts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.1.1.22 compress_css . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.1.1.23 compress_js . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.1.1.24 concat_js . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.1.1.25 container_suffix . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.1.1.26 cultureKey . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.1.1.27 custom_resource_classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.1.1.28 default_template . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.1.1.29 editor_css_path . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.1.1.30 editor_css_selectors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.1.1.31 emailsender . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.1.1.32 emailsubject . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 6 6 7 7 8 11 12 13 13 17 19 21 23 24 26 30 34 35 35 35 38 39 39 40 41 42 43 45 46 47 47 48 50 56 58 59 62 65 67 68 69 69 69 69 70 70 70 70 70 70 71 71 71 71 71 72 72 72 72 72 72 73 73 73 73 73 74 74 74 74 74 75 75 1.3.1.1.33 error_page . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.1.1.34 failed_login_attempts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.1.1.35 fe_editor_lang . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.1.1.36 feed_modx_news . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.1.1.37 feed_modx_news_enabled . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.1.1.38 feed_modx_security . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.1.1.39 feed_modx_security_enabled . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.1.1.40 filemanager_path . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.1.1.41 friendly_alias_urls . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.1.1.42 friendly_url_prefix . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.1.1.43 friendly_url_suffix . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.1.1.44 friendly_urls . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.1.1.45 mail_charset . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.1.1.46 mail_encoding . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.1.1.47 mail_smtp_auth . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.1.1.48 mail_smtp_helo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.1.1.49 mail_smtp_hosts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.1.1.50 mail_smtp_keepalive . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.1.1.51 mail_smtp_pass . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.1.1.52 mail_smtp_port . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.1.1.53 mail_smtp_prefix . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.1.1.54 mail_smtp_single_to . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.1.1.55 mail_smtp_timeout . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.1.1.56 mail_smtp_user . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.1.1.57 mail_use_smtp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.1.1.58 manager_date_format . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.1.1.59 manager_direction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.1.1.60 manager_lang_attribute . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.1.1.61 manager_language . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.1.1.62 manager_theme . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.1.1.63 manager_time_format . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.1.1.64 modx_charset . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.1.1.65 new_file_permissions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.1.1.66 new_folder_permissions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.1.1.67 password_generated_length . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.1.1.68 password_min_length . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.1.1.69 phpthumb_cache_maxage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.1.1.70 phpthumb_cache_maxfiles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.1.1.71 phpthumb_cache_maxsize . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.1.1.72 phpthumb_cache_source_enabled . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.1.1.73 phpthumb_far . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.1.1.74 phpthumb_zoomcrop . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.1.1.75 proxy_auth_type . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.1.1.76 proxy_host . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.1.1.77 proxy_password . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.1.1.78 proxy_port . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.1.1.79 proxy_username . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.1.1.80 publish_default . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.1.1.81 rb_base_dir . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.1.1.82 rb_base_url . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.1.1.83 request_controller . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.1.1.84 request_param_alias . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.1.1.85 request_param_id . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.1.1.86 search_default . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.1.1.87 server_offset_time . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.1.1.88 server_protocol . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.1.1.89 session_cookie_domain . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.1.1.90 session_cookie_lifetime . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.1.1.91 session_cookie_path . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.1.1.92 session_cookie_secure . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.1.1.93 session_handler_class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.1.1.94 session_name . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.1.1.95 settings_version . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.1.1.96 signupemail_message . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.1.1.97 site_name . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.1.1.98 site_start . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.1.1.99 site_status . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.1.1.100 site_unavailable_message . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.1.1.101 site_unavailable_page . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.1.1.102 strip_image_paths . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.1.1.103 tree_root_id . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.1.1.104 udperms_allowroot . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.1.1.105 unauthorized_page . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.1.1.106 upload_maxsize . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.1.1.107 use_alias_path . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75 75 75 75 76 76 76 76 76 77 77 77 77 77 78 78 78 78 78 78 78 79 79 79 79 79 79 80 80 80 80 80 80 80 81 81 81 81 81 81 82 82 82 82 82 82 82 83 83 83 83 83 84 84 84 84 84 84 85 85 85 85 85 86 86 86 87 87 87 87 88 88 88 88 88 1.3.1.1.108 use_browser . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.1.1.109 use_editor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.1.1.110 use_multibyte . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.1.1.111 welcome_screen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.1.1.112 which_editor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.1.1.113 which_element_editor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.2 Using Friendly URLs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.3 Contexts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.3.1 Creating a Subdomain from a Folder using Virtual Hosts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.3.2 Using One Gateway Plugin to Manage Multiple Domains . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.4 Customizing the Manager . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.4.1 Form Customization Pages . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.4.1.1 FC-Resource . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.4.1.2 FC-Template . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.4.1.3 FC-Chunk . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.4.1.4 FC-Snippet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.4.1.5 FC-Plugin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.4.2 Form Customization Rules . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.4.2.1 Field Default . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.4.2.2 Field Label . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.4.2.3 Field Visible . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.4.2.4 Move TV to Tab . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.4.2.5 New Tab . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.4.2.6 Tab Title . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.4.2.7 Tab Visible . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.4.2.8 TV Default . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.4.2.9 TV Title . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.4.2.10 TV Visible . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.5 Security . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.5.1 Users . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.5.2 User Groups . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.5.3 Resource Groups . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.5.4 Roles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.5.5 Policies . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.5.5.1 Permissions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.5.5.2 ACLs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.5.6 Security Tutorials . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.5.6.1 Giving a User Manager Access . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.5.6.2 Making Member-Only Pages . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.6 Installing a Package . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.6.1 Troubleshooting Package Management . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.7 Upgrading MODx . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.7.1 Upgrading from MODx Evolution . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.7.1.1 Functional Changes from Evolution . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.7.2 Upgrading to Revolution 2.0.0-rc-2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.8 Moving Your Site to a New Server . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.4 Developing in MODx . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.4.1 Code Standards . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.4.2 Overview of MODx Development . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.4.2.1 Developer Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.4.2.1.1 Getting Started Developing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.4.2.2 Extras Directories . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.4.2.3 Setting up a Development Environment . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.4.3 Basic Development . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.4.3.1 Snippets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.4.3.1.1 Templating Your Snippets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.4.3.1.2 Adding CSS and JS to Your Pages Through Snippets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.4.3.2 Plugins . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.4.3.2.1 System Events . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.4.3.3 xPDO . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.4.4 Advanced Development . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.4.4.1 Namespaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.4.4.2 Caching . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.4.4.3 Custom Manager Pages . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.4.4.3.1 Actions and Menus . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.4.4.3.2 Custom Manager Pages Tutorial . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.4.4.3.3 MODExt . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.4.4.4 Internationalization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.4.4.4.1 Creating Lexicons for Your Components . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.4.4.5 MODx Services . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.4.4.5.1 modMail . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.4.4.6 Package Management . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.4.4.6.1 Transport Packages . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.4.4.6.2 Providers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.4.4.6.3 Creating a 3rd Party Component Build Script . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89 89 89 89 89 90 90 91 92 93 94 95 95 96 97 97 97 97 99 100 101 102 103 105 106 106 107 108 109 110 112 112 113 115 117 120 121 121 122 124 126 126 127 128 129 130 132 132 143 143 145 146 146 148 148 151 154 155 157 193 193 193 194 195 200 201 203 208 211 211 211 212 214 216 218 1.4.4.7 Extending modUser . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.4.5 Other Development Resources . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.4.5.1 Loading MODx Externally . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.4.5.2 API Reference . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.4.5.3 Class Reference . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.4.5.3.1 modX . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.4.5.3.2 modChunk . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.4.5.3.3 modUser . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.5 Case Studies and Tutorials . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.5.1 Using Custom Database Tables in your 3rd Party Components . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.5.2 Creating a Blog in MODx Revolution . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.5.3 PHP Coding in MODx Revolution, Pt. I . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.5.4 PHP Coding in MODx Revolution, Pt. II . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.5.5 PHP Coding in MODx Revolution, Pt. III . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.5.6 Loading Pages in the Front-End via AJAX and jQuery Tabs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.5.7 Managing Resources and Elements via SVN . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.5.8 xPDO XML Schema File vs. Table Structure Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.5.9 Adding Custom Fields to Manager Forms . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.6 MODx Community Information . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.6.1 Getting a MODx Account . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.6.2 Filing Bug Reports . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.6.3 Becoming a Core Contributor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.6.3.1 Development Environments . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.6.3.2 MODx PHP Coding Standards . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.6.4 Using GitHub . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 231 235 235 236 236 236 261 262 268 268 273 283 285 286 288 290 291 293 294 294 294 295 296 297 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. 20 or newer. depending on your server PHP Configuration Options . and general information about MODx to get you started. but might experience bugs here and there. beginning concepts around MODx. INSERT.51?) InnoDB storage engine MyISAM storage engine Supported Browsers (for Backend Manager Interface) Mozilla Firefox 3. This section provides installation tutorials.51 (Why not 5. Server Supported Web Servers Apache 1.6 and 5. with the following permissions: SELECT. UPDATE.Getting Started Welcome to MODx Revolution. but may be in a feature release.1.2.1 and above (excluding 5.2 and above Microsoft Internet Explorer 8 and above IE7 is not fully supported at this time. specifically pdo_mysql (for xPDO) SimpleXML Safe_mode off Register_globals off PHP memory_limit 24MB or more.2.0./configure --with-apxs2=/usr/local/bin/apxs --with-mysql --prefix=/usr/local --with-pdo-mysql --with-zlib MySQL Database Requirements 4.0 and above Apple Safari 3.0) Required extensions: zlib JSON (or PECL library) mod_rewrite (for friendly URLs/. 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.1.1.0. You are free to use it. x86-64 Mac OS X Windows XP. DELETE are required for normal operation CREATE.3.0 lighttpd (Setup and Friendly URL Guide) Zeus PHP Compatibility 5. Server Requirements Supported Operating Systems Linux x86.x (uses htaccess for Friendly URLs by default) IIS 6.2. ALTER.x . INDEX.1.htaccess) GD lib (required for captcha and file browser) PDO. . our version control software.MySQL 5. It's recommended you only use this if you plan to move the core. 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.php?id=36406 Installation This page is for New Installations only. you'll want to download MODx Revolution 2.0. at the time they were packaged. A lot may have changed since then. ordering and prepare statements. Please upgrade your MySQL installation.0." others are just a plain old "modx-2. You can simply extract the files to your server and follow the Basic Installation guide to install MODx.51 installed.0-xxxx-#.51a. Most users should choose this version. including 5.0. since the "core" contents are compressed. see Upgrading MODx. and you have SSH access and are familiar with making folders writable. manager or connectors directories.0. Installation steps will differ in each distribution.mysql. "Normal" vs.com/bug. Some are labeled as "Advanced. as well as other open source applications.zip". specifically grouping.mysql.php?id=47655 http://bugs. including bugfixes and the addition of new features.0.0.51.51? MySQL 5. MySQL 5.51 Issues Why does MODx not support MySQL server version 5.0.0 is managed on GitHub.com/bug. From Git MODx Revolution 2.net/bug. has serious bugs with PDO. There you will find downloads for MODx Revolution. Installing MODx MODx comes with multiple distributions for download. Downloading MODx First off. It's worth noting that these packages are basically snapshots from Git.0. If you're looking to upgrade.51 Server Buglist Here are just some of the bugs that occur: http://bugs. and therefore MODx does not support installations with MySQL 5. It will cause uncorrectable errors in normal queries in MODx. MODx Setup will try to unpack or "build" this package during install. "Advanced" You have probably noticed that there are a few different types of packages to choose from. so please select the distribution's installation guide below: Normal Distribution: Basic Installation Advanced Distribution: Advanced Installation Building from Git: Git Installation .These packages are slightly less than half the size of the "Regular" downloads. Please read the Git Installation document to learn how to use MODx Revolution from Git. Git will always have the latest up-to-date snapshot of Revolution. Currently. Note the release date for each package.These packages are pre-built snapshots from Git. Advanced . So what do these labels mean? Normal .0.php. Please follow the Advanced Installation document for this distribution.php?id=32202 http://bugs. please read the Troubleshooting Installation page. start the install process by loading your web browser and running the setup script by navigating to the setup/ folder. Install Options After this. From there you will be asked to choose a language. and be presented with a welcome screen. If you're still having issues installing.After finishing installation. make sure your core/cache/ and core/config/ directories are writable by PHP. 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. you'll be presented with a screen with some Install Options: . You might want to check the Server Requirements page first.0i MAMP on MacOSX Debian Vista and XAMPP Installing Packages See Also Beginning Setup After you've downloaded MODx Revolution. Click Next when you're ready. please read the Troubleshooting Installation page. if you are still having issues. Before running setup. click the 'Test database server connection and view collations' link.The New Installation option should be the only available option for you to choose. check to make sure your database username and password are correct. they will show below. If you do have errors.this is useful should you want to make multiple MODx installations on one database.com. you can do so in the textfields below. you might need to do that manually. if you want.port= appending the IP/hostname.database. If you have your MySQL server on a different port. this will be 'localhost'. For most users. if your user does not have access to create a database.port=3307". Collations and Charsets This will then popup another form for setting your database charset and collation: . Should you have any errors. with the . Database Options From here. This tells MODx to prefix the tables with this value . you can specify a different table prefix here. Also. Most servers will be fine with the default values. If you need to adjust the file permissions for your webserver. click Next to proceed. When you're finished. which is the URL at which your database is located. Also. When finished. specify it like so: "my. you will get a form asking you for your database information: Add in your database hostname. (root will be the actual directory you are installing to. and "/core/export" are writable. click 'Next' to proceed. Specify a username that you want to be the administrator username.) 2. as this is a common administrator username and is often the first username hackers check. "/core/packages". if you need to change them. put in your email (or the email of your administrator) and specify a password. If any of these fail. make sure the collation matches the charset.For most users you can leave these values at what they are.ini setting sets memory_limit to 128M. DO NOT COPY config.inc. From there. Make sure the directories "/[root]". State your server setup and installation info. Click next when you're finished. However. Pre-Installation Checks MODx will then proceed with a list of checks to verify that your system is ready for installing. and all the checks pass. and we'll try and help you find a solution. If you get a blank screen or cannot proceed after clicking 'Install'. MODx recommends not using 'admin'. When install is successful. Post a message in the Revolution forum regarding your issue.inc. and max_execution_time to 120 3. Creating an Administrator User This form will now present you with a few fields for setting up your administrator user. and prompt you to attempt reinstallation should any of those errors have occurred.' after you've finished. Post-Installation Summary MODx will then let you know if any errors occurred during install."/core/cache". Once you're ready. 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. verify these steps: 1.php" and make it writable. and you'll be presented with one final option: . Click the 'Create or test selection of your database. Make sure your php. Create a blank file "/core/config/config. click 'Install' to proceed.tpl! Just make it a blank file! 4. Select the 'xCache' caching drivers to remedy this. Some users have reported that applying a fix found here: http://www.MODx recommends that you make sure to remove the setup/ directory after installing.8. as the drivers compiled with MAMP are faulty with regards to PDO and will cause Apache kernel errors.0i MAMP on MacOSX MAMP (including latest 1. You're finished! Additional Info Some other special cases: WAMPServer 2. When ready. See Also MODx Revolution on Debian MODx Revolution on Debian Debian packages in old versions of MySQL drivers in. see the How to Install Packages article. Installing Packages For information on installing 3rd-party packages. with one exception. click 'Login' to be presented with the Login form for the manager interface. please see the MODx Revolution on Debian article for more information.apachefriends.0i Please see this article: Problems with WAMPServer 2.org/f/viewtopic.php?f=16&t=32617 will fix Apache crashing errors with PDO support in XAMPP. Vista and XAMPP There have been reported problems with installing Revolution on 64-bit Vista with XAMPP. You cannot use eAccelerator as the caching system. You can do this by clicking the 'Check this to DELETE the setup directory from the filesystem. so to get it up-to-date and working with the PDO drivers in MODx Revolution.' checkbox.4) works fine with MODx Revolution. Debian Debian uses outdated MySQL drivers for its PHP build that will need to be updated. to safeguard your site from anyone else trying to run setup on your site. We cannot guarantee a working solution on that OS and setup at this time. you'll have to . The easiest way to do this is by updating PHP.org stable all deb-src http://packages.timezone = Europe/Amsterdam /etc/init. mysql. look for an entry named "mod_rewrite". 2.php?q=$1&$2".php?q=$1" ) .do the following: 1. url. You can do this manually on debian.org stable all deb-src http://php53.*)$" => "/index..modules. Please remove the # from the line and save the file. "^/(?!index(?:-ajax)?\. This is a comment symbol. Under this directive. So open your lighttpd.dotdeb. This is still a work in progress. So lets search for something that looks like this: $SERVER["socket"] == ":80" { $HTTP["host"] =~ "yourdomainname. or even same idea as Apache does for URL rewriting. Next we need to find the location in which to put the friendly URL code.dotdeb. like so: vi /etc/apt/sources.dotdeb. "^/(?!index(?:-ajax)?\.*)$" => "/$1/$2". Update the server MySQL drivers.conf) Look for the directive server.d/apache2 reload Lighttpd Guide Lighttpd Guide for Setup and Friendly URLs.*)$" => "/index. and PHP installation.org stable all apt-get update apt-get upgrade php5 vi /etc/php5/apache2/php.document-root = "/path/to/your/doc/root" server. This guide assumes you already have a working lighttpd.org stable all deb http://php53.rewrite-once = ( "^/(assets|manager|core|connectors)(. All URL rewriting is done in the lighttpd. Friendly URL Setup Lighttpd Guide for Setup and Friendly URLs.php)(. Friendly URL Setup lighttpd does not use the same system.list (you can choose a different mirror): deb http://packages.dotdeb. and currently only covers the URL rewriting aspect. This guide only covers proper settings and the use of friendly URL Rewriting.ini date. or use apt-get.com" { server. Update the client MySQL drivers.php)(.*)\?(.conf config file (In Linux it is usually located in /etc/lighttpd/lighttpd.name = "yourservername" Directly under this you should add the following code.conf file First we need to make sure that the URL rewriting module is enabled. By default it has a # in front of it. After this is done.0i's PHP 5. simple add another | followed by the folder or filename you wish to omit from url rewriting.5-dev.0i will set the server to 5. Excluded dirs/files in the example above are (assets|manager|core|connectors). From here. If after reading this.1.3.0.0. if you plan on moving the core/ directory. browse to setup/ in your browser and skip to the Advanced Options section of this document. It sets its server version at 5.0i How to get WAMPServer 2.36. you're still having issues installing. The problem child comes in WAMPServer 2. WAMPServer allows you to start your stack with different versions of PHP/MySQL combinations.1. You should be left with two directories core/ and setup/. upload and extract it to your server.com/2010/01/11/modx-revolution-and-wamp/ A short summary and explanation is below. Problems with WAMPServer 2. http://codingpad. simply start WAMPServer with the PHP 5.51a. and some problems you might encounter. and therefore will not install with this configuration. You HAVE to exclude any files or folders you do not want rewritten in the config file.36. but its client version at 5. you will have working friendly URLs again in lighttpd. or move the core/ directory You have SSH access or can easily move/make writable directories on your server. The Solution To fix it. Installation Pre-Steps After you've downloaded MODx Revolution's advanced distribution. If you're not going to do so. While still not optimal.This does not mean you are done! Lighttpd handles url-rewrites a bit differently. or rename the config key. 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. this will allow Revolution to run smoothly without MySQL hiccups. MODx does not support 5.0.0 build.maryspad.2. and the client to 5.0i working on MODx Revolution Mary (einsteinsboi) has a great blog post about using WAMPServer 2. If you wish to add more to these. WAMPServer 2. proceed to the next section. Renaming or Moving the Core .11 version. You might want to check the Server Requirements page first. It is recommended to only install this distribution if: You plan on renaming the manager/ or connectors/ directories. please read the Troubleshooting Installation page.0i with MODx Revolution. 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.51a. This allows you to run multiple sites with a shared core.at the beginning . but with two extra options at the bottom. Should you choose to rename or move the core. To change it. you can change the folder/file perms to 0775/0664. Simply rename or move the core. If MODx still cannot find the directory from the path you specified. This is required to change the core path. you can choose to adjust the permissions for creating new files or folders in your MODx installation. The defaults should work fine. MODx will then prompt you with a welcome page. Below that. that it is an absolute path. Advanced Options You will now be presented with some options for install. 'New Installation' will be your only radio option available to check. unique config key and click next. as each individual site will need its own unique configuration key.php file writable. respectively. which is what you want.MODx Revolution allows you to rename and/or move the core/ directory to enhance your level of security. and that you've made the directory readable (and the core/cache/ file writable). and you'll be presented with a textfield: Specify a custom. MODx will ask you to choose a language. MODx might also ask you to make the setup/includes/core. and setup/ . If MODx is able to find the core from there. simply click the link the install tells you to change the config key. You can also move the core/ directory outside of the webroot to further secure your MODx installation. Below that. check if you have typed it correctly.config.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. and below will ask if you want to change the MODx Configuration Key. Changing the Configuration Key From here. similar to the Basic Installation screen. Do so at this time. but if on a more restrictive server. and you should do so before proceeding. you will be presented with two checkbox options: . MODx recommends doing so before installing. you will proceed normally with the installation. If you do have errors.) Click 'Next' to proceed to the next step.com. they will show below. you can specify a different table prefix here. For most users.port= appending the IP/hostname. if you want. Should you have any errors. When finished. Also. If you have your MySQL server on a different port. if your user does not have access to create a database. if you need to change them.port=3307". you might need to do that manually. Database Options From here. Click the 'Create or test selection of your database. click the 'Test database server connection and view collations' link.database.These will be grayed out during new installations. However. This tells MODx to prefix the tables with this value .' after you've finished. this will be 'localhost'. which is the URL at which your database is located. check to make sure your database username and password are correct. (During upgrades. make sure the collation matches the charset. Also. Creating an Administrator User .this is useful should you want to make multiple MODx installations on one database. with the . 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. you will get a form asking you for your database information: Add in your database hostname. it is recommended that you uncheck these as well. specify it like so: "my. Note: If you do change the directories. and we'll try and help you find a solution. however. If you get a blank screen or cannot proceed after clicking 'Install'. Ensure that MODx can create the manager and connectors directories. unless you have a special reason not to. Make sure you change both the path and URL! When done. (root will be the actual directory you are installing to. Once you're ready. State your server setup and installation info. click 'Next' to proceed.ini setting sets memory_limit to 128M. and all the checks pass. and prompt you to attempt reinstallation should any of those errors have occurred. Context Configuration MODx will now present you with a detailed context installation screen. Make sure the directories "/[root]". Click next when you're finished."/core/cache". MODx recommends leaving the web/ context paths as they are. Make sure your php. click 'Install' to proceed. and max_execution_time to 120 3. Post-Installation Summary MODx will then let you know if any errors occurred during install. 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. verify these steps: 1. as this is a common administrator username and is often the first username hackers check. can add an extra level of security to your site. Specify a username that you want to be the administrator username. "/core/config". and "/core/export" are writable. "/core/packages". This is where you can configure the paths to your web context (the main context).This form will now present you with a few fields for setting up your administrator user. click 'Next' to proceed. this is done by making the parents of those directories writable (since you can change where they are installed) 4. as well as the directories for your connectors/ and manager/ folders. MODx recommends not using 'admin'.) 2. and you'll be presented with one final option: . If any of these fail. Renaming your manager/ and connectors/ directories. Pre-Installation Checks MODx will then proceed with a list of checks to verify that your system is ready for installing. From there. Simply change the paths and URLs in the textfields provided. put in your email (or the email of your administrator) and specify a password. Post a message in the Revolution forum regarding your issue. When install is successful. the directories above any of those paths must be writable to allow MODx to write the manager/ and/or connectors/ directories to them. Because of the nature of the new packaging and installation system. When ready. . make sure you are working on the 2. 2. if you'd like to contribute back. Git Location Git clone the revolution repository on GitHub at: http://github. Unlike previous versions of MODx.com/modxcms/revolution. There are three current branches in the modxcms/revolution GitHub repository: Stable Branches master . Revolution will not install directly from Git.This will usually match the latest release.MODx recommends that you make sure to remove the setup/ directory after installing. 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. you must first create the core installation package using a PHP build script before running the setup. (You'll need to submit a CLA before we can accept your code. and is only changed during releases. though.com:yourgitusernamehere/revolution. to safeguard your site from anyone else trying to run setup on your site. if you're wanting the latest bugfix release. You can do this by clicking the 'Check this to DELETE the setup directory from the filesystem.' checkbox.git Or.com/modxcms/revolution/ using this syntax: git clone http://github.0 branch. From there. click 'Login' to be presented with the Login form for the manager interface. fork it in your GitHub repository and clone that repository with: git clone git@github. please read the excellent tutorial from GitHub and view the GitHub help pages.0-pl.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.0. It is the stable branch. ie.) If you're not familiar with Git. New branches will be feature-specific and merged into the 2.1' or 'master' if you're running from another branch. if you get a blank page when you click "install". If you change any paths on the Context Paths setup step. change your working directory to _build/ and execute the command "php transport.If your updates require a core transport rebuild (such as anything modified in the _build directory. Run Setup Now you are ready to execute the new setup script at the setup/ URL (e. 2. where the installer will place the files in the specified locations (assuming the locations allow the PHP process to write to them).The latest development branch for 2. so be patient.0. The actual install process requires more than the default 8M of memory allocated to PHP in many default php.66Ghz Intel Core Duo with 2GB RAM) running the Leopard development environment as outlined below. try increasing the memory_limit configuration to 32M or more (16M may work.php directory. this message might show up in the commit: [ReUp] .ini files.php and many other files/directories. Once that script is finished executing. but why not give php a little space.0 And Git will update your install.core. Upgrading Your Local Git Repository After Commits Simply run these two commands: git fetch origin git rebase origin/2. you will need to either edit the path or give the full path to the PHP executable in the command line.config. after cloning. (Note: on Mac Mini (1. Patches from the 2. confirm that you now have a file named core/packages/core.0' for '2.transport. From the command line. (Substitute '2.core.) When a commit is made. just type: git checkout -b 2.sample. Make sure you check both the "Core package has been manually unpacked" and "Files are already in-place" options when installing from Git.0 branch are merged to here from time to time.For new features for 2. To create a local tracking branch from one in the origin remote.) Note that you can also do this from the browser by browsing to the _build/transport. Run the Build If this is the first time you are building from Git.0 And git will handle the rest.0 . the build script just needs to be able to make a connection to a MySQL database.x releases. database model changes. eh?).Development Branches 2.g. If the PHP executable is not in your path. if that directory is accessible in your web server setup.zip and a directory core/packages/core/ containing a manifest.1 branch when they are complete and stable. copy the file _build/build. centered around new features.0 origin/2. NOTE that this database does not have to contain anything.php and edit the properties to point at a valid database with proper credentials.1 . or .1.php to _build/build. All feature branches eventually integrate into this branch. There will be other branches in the future. this is intended for installs from the core package with files not already in-place.php". http://localhost/modxrevo/setup/ if installed in a subdirectory of the web root named modxrevo/). all patches (not new features) are committed to here. this only takes 5-10 seconds. make sure and move the corresponding directories as appropriate.config. The build process may take an extended period of time (10 to 30 seconds likely). If this message does not show up. Additional Information Using MAMP on Mac OS X If you use MAMP on Mac OS X.core. the page flakes out. then prefix your commit message with this. replacing 2.0 for patches or 2. To adjust the dynamic linker library path to include the MAMP PHP libraries.0. Make sure you are sending pull requests to the development branches. since database changes don't always necessarily 'backport'.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.1 for new features. and you're working on a fork of the revolution repository.1 with the name of the branch you want to switch to. simply rebuild the core transport and run setup/ again. Sending Pull Requests If you've fixed a bug or added an improvement. be careful when doing this. etc (eAccelerator) Resource / Elements / File tree not appearing I can't login to the manager after installing! Still Having Issues? Common Problems .1 to 2." The login page keeps redirecting me back to the login screen with no error Things sometimes don't load.core. You'll need to submit a CLA before we can accept your code. you can send a pull request to MODx and one of the Integration Managers will review your patch. you're done after you fetch and rebase. run the build and run setup/ again. After you've done so. you may get problems (errors about DYLD libraries not being included) when trying to execute ''transport. ie. since different branches might have different databases. Switching backwards is not always recommended.--construct]: [2002] Argument invalid (trying to connect via unix://) OR "Checking database:Could not connect to the mysql server. ie 2. Pull requests to master will be ignored. Switching Branches If you want to switch to a different branch.php'' from the terminal. switching from 2. run the following command via the terminal: export DYLD_LIBRARY_PATH=/Applications/MAMP/Library/lib:$\{DYLD_LIBRARY_PATH\} You can then execute ''transport. While no major issues should occur.1 Of course.default data changes).core. If you see this message. This is because the MAMP PHP libraries won't be on the dynamic linker path by default. simply type these commands: git fetch origin git checkout 2.php'' by using the absolute path to the MAMP PHP executable: /Applications/MAMP/bin/php5/bin/php transport. You are using at least PHP 5.1.com.tpl to config. If you renamed the config. then your PDO setup is not configured correctly.default_socket=/path/to/my/mysql.inc." This means your MySQL socket is incorrectly configured.php file an empty.php. eAccelerator can cause problems when doing the heavy lifting during the install process. "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.php that is writable.default_socket=/path/to/my/mysql.--construct]: [2002] Argument invalid (trying to connect via unix://) OR "Checking database:Could not connect to the mysql server.ini: mysql. $e->getMessage().0 You are using MySQL later than 4.51a). } catch (PDOException $e) { echo 'Connection failed: ' . try { $dbh = new PDO($dsn. 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. Make the config.database.0. You followed all the directions here for your distribution.1.sock mysqli. To fix it.20. } ?> If this fails.6 or 5. "I clicked install and got a blank white screen!" Make sure your 'memory_limit' setting in php. $user = 'dbuser'.sock The login page keeps redirecting me back to the login screen with no error This can happen with older Revolution beta installs.2.inc. but not any iteration of MySQL 5. 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 .host=localhost'. Usually this can be remedied by adding to (or updating) your php.inc. but not 5.0.php.tpl to config.inc. $password = 'dbpass'. which is incorrect. Try putting this syntax into the hostname field (replacing the data with your mysql server's host and port): my. rename it back to config. For slower servers. PDO Error Messages If you are getting PDO-related error messages during install. writable file. you might need to up it to 64M.default_socket=/path/to/my/mysql. make sure: You have eAccelerator disabled during install.1.inc.inc.inc.51 (including 5.ini is set to at least 32M.port=3307 Warning: PDO::__construct() [pdo.1+.First off.sock pdo_mysql.tpl and create a blank file named config. $password). $user. 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. eaccelerator. Things sometimes don't load. and we'll try and address your issue as soon as possible. You might need to disable it.cache.enable = 0.. post your error and your server environment information in our forums here.. page "flake outs" may stem from items stored within your own browser's cache. Successful Installation.htaccess file in the root of your MODx install: php_value session. Under System Clear Cache Under Security Flush Permissions and then Flush Sessions This will dump everything and log you out 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. Still Having Issues? If you're still having problems. Now What Do I Do? Creating the first page Creating a Template After a successful installation. if your server supports php_flag server directives: php_flag eaccelerator. 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.ini: eaccelerator. you'll be presented with the Manager login page. 2.optimizer 0 php_flag eaccelerator. this can cause problems.htaccess in the modx root directory. eaccelerator. 3. you've changed these explicitly for some purpose of your own.enable 0 php_flag eaccelerator.debug 0 Resource / Elements / File tree not appearing Additional. You can do so via your php. log in.auto_start 0 More common issues to come. and log back into the manager. This can be verified by accessing the manager with a browser not previously utilized in doing so. the page flakes out.Then delete the core/cache/config. etc (eAccelerator) Are you running eAccelerator? In some server configurations. Unless. Use the login and password you specified during the installation.php file. of course.optimizer = 0. A more complete solution: 1. try setting this in your . The simple fix: clear your browser's cache. 4. or in your . You will be presented with something like this .debug = 0. open (if it's not open. And now. . which is why this list of error messages. My Template. Click on the floating Save button to save your new document. let's just create a really simple template. click the Preview menu item. MODx templates are basically just HTML pages. will be the document's fields. give the template a name. with the content parts replaced with MODx tags. Home. and some content. or you can right-click on the web context icon. We need to create a template to give it some structure and style. if you view the tree on the left. On the right. In either case. and view your page in all its glory! Creating a Template Obviously. First Document. So to begin with. Creating the first page This being MODx. the yellow folder with the green plus sign on it). To begin with. the page alone is missing something. First Revolution Template. the first thing to do is create a page to make these errors go away. New Template and Quick Create Template. and you'll see two choices for creating a new template. just give the document a title. a description. there are several ways to create a new resource (document). Therefore. a Menu Title. choose Create. taking up most of the page. Click on the Tree View's 'Elements' tab. Make sure to check the Published check box. and then the HTML code for the template. you'll see your new document listed. This will open a selection of elements you can create and manage to add dynamic content to your page. Go to the main Site menu. The New Template changes the right panel to a more complex form for creating a new template. then Create a Document Here. The New Document window will appear.Since Revolution RC1 doesn't come with any default content. Right-click on the Template element. the Tree View block on the left will have the first section. 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. there aren't any pages. 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. Web Resources. The main Site menu has a "New Document" menu item. w3.0-ui/ . confirm that you now have a file named core/packages/core. if you click the main Home menu.dtd"> <html xmlns="http://www. SVN Locations Checkout or export the latest Revolution code from SVN at the URL: http://svn. you must first create the core installation package using a PHP build script before running the setup.padding:10px 20px. change your working directory to _build/ and execute the command "php transport.php to build. charset=utf-8" /> <style type="text/css" media="screen"> #content{width:80%. Unlike previous versions of MODx.com/svn/tattoo/tattoo/branches/2.w3.margin:auto.border:5px groove #a484ce.<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1. Go back to the site and refresh the page.config. (Note: on Mac Mini (1. From the command line.text-align:center.0/ . this only takes 5-10 seconds.1//EN" "http://www.php". The build process may take an extended period of time (10 to 30 seconds likely). 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.modxcms.org/1999/xhtml" xml:lang="en"> <head> <title>My First Revolutionary Page</title> <meta http-equiv="Content-Type" content="text/html.Latest 2. you'll see that it's been assigned the template (since it's the only one. Now if you open your first document for editing. the build script just needs to be able to make a connection to a MySQL database.) Note that you can also do this from the browser by browsing to the _build/transport. NOTE that this database does not have to contain anything.transport.php and edit the properties to point at a valid database with proper credentials.0 dev http://svn. Because of the nature of the new packaging and installation system.core.For default mgr UI/design work Run the Build If this is the first time you are building from SVN.config.org/TR/xhtml11/DTD/xhtml11. you will need to either edit the path or give the full path to the PHP executable in the command line.modxcms.php and many other files/directories.} #content h1{color:#a484ce. if that directory is accessible in your web server setup.} </style> </head> <body> <div id="content"> [[*content]] </div> </body> </html> Save the new template. Revolution will not install directly from SVN. Once that script is finished executing.66Ghz Intel Core Duo with 2GB RAM) running the Leopard development environment as outlined below.} #content p{padding:20px.php directory.com/svn/tattoo/tattoo/branches/2. and the document didn't have one). And now.text-align:center. copy the file build.zip and a directory core/packages/core/ containing a manifest. If the PHP executable is not in your path.core. so be patient. Run Setup .sample. core. where the installer will place the files in the specified locations (assuming the locations allow the PHP process to write to them). simply rebuild the core transport and run setup/ again. Give it a try at your earliest convenience and record any issues or problems you encounter. database model changes. you simply need to svn update. shape or presence you want. See Development Environments for recommended tools and setups for contributing directly to MODx core code. There is a lot of work going on still.g. Upgrading After Commits When a commit is made to an SVN branch. fixes and refactorings. Only the absolute required minimal data is included. If you see this message. eh?). or default data changes). to a blog.If your updates require a core transport rebuild (such as anything modified in the _build directory. if you get a blank page when you click "install".core. and plenty left to go. then prefix your commit message with this.php An Overview of MODx What is MODx? MODx is a Content Application Platform.php'' by using the absolute path to the MAMP PHP executable: /Applications/MAMP/bin/php5/bin/php transport. and you're done. You can setup everything from a simple site.Now you are ready to execute the new setup script at the setup/ URL (e. make sure and move the corresponding directories as appropriate. and keep your admin interface . If you change any paths on the Context Paths setup step. one of two messages might show up in the commit. Make sure you check both the "Core package has been manually unpacked" and "Files are already in-place" options when installing from SVN. but why not give php a little space. etc.ini files. you may get problems (errors about DYLD libraries not being included) when trying to execute ''transport. If neither of these messages show up. This is because the MAMP PHP libraries won't be on the dynamic linker path by default.core. try increasing the memory_limit configuration to 32M or more (16M may work. 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. The actual install process requires more than the default 8M of memory allocated to PHP in many default php. as of this time. http://localhost/modxrevo/setup/ if installed in a subdirectory of the web root named modxrevo/). to a full-scale web presence with MODx. this is intended for installs from the core package with files not already in-place. What does this mean? Well. [REBUILD/UPGRADE REQUIRED] .php'' from the terminal. It also offers a completely customizable backend interface that you can make as simple (or as complex) as you like. Using MAMP on Mac OS X If you use MAMP on Mac OS X. there are no documents or add-ons installed by default. 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. so expect the unexpected as we work through changes. A completely flexible. forwarding link. Package distribution built-in so you can pack up your code. Put your CSS and images where you want them. adding dictionary links to words. as MODx offers completely flexible templating and no-holds-barred content delivery. processing forms. has a ton of moving parts. grabbing custom data. or anything else. in essence. tagging elements. They usually contain the footer and header for a page. fetches that Resource's Template. with the UI and content management of a CMS. Simple. They can do anything you can code. managing redirects for old pages. Snippets Snippets are dynamic bits of PHP code that run when the page is loaded. and have found they have the power. including any tags that might be in it. including building custom menus. And once you're done designing. etc). TV. 100% Friendly URL compatible. You've looked at PHP frameworks.simple and usable. They can contain Snippets. or point-and-click install Extras straight from within the manager. powerful and robust API. You want the power and flexibility of a framework. but have found developing in them to be either a mishmash of too many unconnected code pieces. be it whatever you want inside it. Add custom manager pages to run entire applications within MODx. etc. Internationalization support. Basic Concepts MODx. and as easy as structuring your site that way. built on OOP principles and using a PDO-powered Object Relational Model (ORM) called xPDO. But the basics parts are: Resources Resources are basically a webpage location. Add in a rich.html is incredibly simple. MODx then parses the resulting combined content. A great example would be a 'tags' TV that allows you to specify tags for a Resource. Schedule Resources to publish at certain times. either hand off the development duties to your developer. Get a full WYSIWYG view of your Resources. Developers (Badass Billy) You've looked at different CMSes. 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 outputs the response to the user's browser. Leave Resources unpublished before you finish them. Chunks Chunks are simply small blocks of content. Template Variables Template Variables (TVs) are custom fields for a Template that allow the user to assign dynamic values to a Resource. They are usually used for extending the Revolution core to do something during a part of the loading process .com/my/own/custom/url. Templates Templates are the house a Resource lives in. It can be actual HTML content. . etc. Chunk. You can have an unlimited number of TVs per page. and then places the Resource's content in that Template. MODx is fully. Plugins Plugins are event hooks that run whenever an event is fired. MODx helps you organize your content the way you want it. and distribute it across any Revolution install. and get stellar built-in SEO results. or a file. So What Happens on a Request? MODx loads the requested Resource. or any other Element type (Snippet. From there. or simply not powerful or elegant enough.it leaves the front-end design up to you. Drag and drop your webpages around to reorder and move them. but don't do Content Management nor have a good enough UI for your clients. Custom properties and sets. that's fully customizable. or a symlink.such as stripping out bad words in content. grabbing tweets. Enter MODx Revolution. Sencha-powered UI for your clients. You can use MODx as your Content Management System (CMS) of choice. so getting mysite. in the order they are reached. 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. Asset Any type of file resource that is usually located in the /assets directory. Back-end A synonym for the MODx manager interface. libraries. image files. . css files. can include Third-party Components. etc. JavaScript files. as defined by the constant MODX_ASSETS_PATH. but yet still provides extra functionality to the MODx instance. class files. Context Setting A single setting for that Context that either creates a new setting or overrides a System Setting. usually used to create multiple-context sites. Category. usually pertaining to a normal website page. or context manipulation classes. such as a custom User or authentication class. a single Template. Content Element Also called just "Element". mime-type and binary setting for any Resource. or Property Set visible in the Manager Elements tree. This will make it easy to isolate upgrades to the MODx Core. Context A delineator of resources and settings that can be used for a variety of reasons. or quickly revert to a previous Core Workspace you know works. Template Variable. such as subdomains. multi-language sites. Chunk. Component Also called "Third-party Component". Plugin. there will be an ability to manage multiple Workspaces from a single database. caching mechanisms. or Template. Chunk Tags Tags in the form [[$ChunkName]] that can be used in reference to Chunks. This will be especially important for multi-site configurations running on shared MODx Core installations. or 3PC. etc. Snippet. Extension Also called "Core Extension".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. Content Type Sets the extension. Document Identifier See Resource Identifier. Document A specific type of Resource. directly from the manager application. the MODx Core used by the setup application is recorded into the MODx database as the Default MODx Workspace. Core Workspace Each unique MODx Core is represented by a named Workspace. usually in the form of an Add-on. you'll be able to apply an entire new MODx Core installation to production sites after testing on a staging site. by quickly adding a new Core Workspace and selecting a menu option. A MODx Third-party Component that modifies the MODx Core. File Resolver . When you install Revolution initially. a Component usually provides extra functionality to MODx. In future MODx Revolution releases. Core Extension. Link Tags Tags in the form [[~ResourceId]] that reference the URL of a particular Resource. used to set a specific parameter for the Element. Can have any number of derivative classes.e.'value') in a Snippet or Plugin. i. and other objects related to the Component in a Revolution site. Settings. menuindex. Resource A type of container that is interpreted by the Parser to fetch content. Placeholder Tags Tags in the form [[+PlaceholderName]] that reference MODx Placeholders. Property A single variable for an Element. longtitle. 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. Property Set A collection of variables used for a particular purpose with an Element. overriding the default behavior. such as title. Language Tags Tags in the form [[%LanguageStringKey]] that reference MODx Lexicon entries.A type of xPDOVehicle Resolver that copies files from the source location to the target location in a Transport Package. by their Topic. to reduce load times. the most common is a Document. . An example is a custom Property Set for a Snippet that passes in specific parameters to the Element. introtext. etc. Manager The back-end of the MODx interface. Revolution only loads Lexicon Entries as it needs them. Lexicon A Lexicon is a dictionary of words and phrases organized by Culture (more specific than language. usually set with $modx->setPlaceholder('placeholderName'. Property Sets are attached to Elements and pass in the Properties that they carry as parameters to the Element. Some fields are available on the Document Create/Edit screen and via Resource Tags. alias. Others can only be accessed via the documentObject. Also specifies an absolute path in which the Component may be found. 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. Resource Field Any of the fields of the site_content table. Namespace An organizational tag for Components to use to identify Lexicon Entries. or Document Identifier. Resolver (for xPDOVehicles) Post-processor: a script or predefined action that is evaluated after a Vehicle is installed or uninstalled. or Template Variables. Transport Package A packaged and zipped collection of Transport Vehicles. the number in parenthesis in the MODx Resource Tree in the Manager that uniquely identifies the Resource specified. that can be easily distributed ("transported") from one Core Workspace to another. and can then perform actions on MODx before anything else happens in the install/uninstall process. Static Resource A specific type of Resource that is a direct reference to a specific file on the MODx site. System Setting A site-wide variable accessible to the MODx site. and User Settings. An example of a PHP Resolver is one that attaches Plugin Events to a newly-installed Plugin. Template Variables Custom Resource Fields created by the user on the Document Create/Edit Screen and referenced using Content Tags. Transport Vehicles An intelligent container that encapsulates any artifact that can be distributed in a Transport Package. The content is replaced with the contents of that file. the Resource's content will replace the Symlink's content. also referred to as Snippet Calls. Resource Tags Tags in the form [[*ResourceField/TV]]. Context Settings. Can be overridden by Context Settings and User Settings. Symlink A type of Resource that references a single. Transport Vehicles store their payloads in a . Resource ID. An example of a file Resolver is one that copies the assets/ditto directory in the xPDOVehicle path to /modx/assets/ditto. Snippet Tags Tags in the form [[SnippetName]]. Setting Tags Tags in the form [[++SettingName]] that reference MODx System Settings.Resource Identifier Also called a Document ID. which can be used to refer to Resource Fields. local MODx Resource. Resolvers always occur after the vehicle's object is save()'d. Transport Provider (formerly Provisioner) A web service that enables remote installation of Transport Packages directly from the MODx manager application. MODx will not uninstall or install the package. Validator (for xPDOVehicles) Pre-processor: a script or predefined action that executes prior to the vehicle being installed or uninstalled.portable format. redirecting the visitor to that URL or Resource. 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/ . If the validator returns false. xPDOVehicle The base Transport Vehicle class. Some of these directories can be renamed and moved. A Validator could be used to determine if a directory exists and is writable. each with its own set of responsibilities and tasks. along with various attributes that control how the object is installed/uninstalled in a Core Workspace (see xPDOVehicle Validators and xPDOVehicle Resolvers). User Setting A user-specific setting that either creates a new setting or overrides the similar Context Setting and System Setting. the install/uninstall action will proceed as normal. stores xPDOObject instances (which represent a row of data in a table) in it's payload. to see if other modx elements are already installed. and their locations can be configured during setup. Weblink A type of Resource that references a specific URL or MODx Resource. Used to provide unique settings to that user. xPDOVehicle. or to determine if a certain version of MySQL and PHP are used on a server. If the validator returns true. Explanation of Directory Structure The root directory of MODx is split into several subdirectories. which we will discuss later.cache. we request connectors/resource/index. Simply include this file in your connectors. model/schema data. sanitize any request data. core/ The Core is what makes MODx. MODx. This file is like the config. Like every context cache. You can customize those pages here. core/cache/web/ Unlike the cache in MODx Evolution. should be stored here. processors and class files. core/error/ This contains default templating for error response messages in Revolution's front-end.This file is particularly useful in creating your own connectors.txt file. Any context can override a system setting. time. which contains the date. the web context cache will contain separate directories for resources and elements. any files needed to run in the Manager.php) which instantiates the main MODx object. It sets up database credentials and a number of MODX_ constants for the proper operation of your site. and error which was logged by MODx. meaning that they are only cached after being accessed for the first time. and then handle the request using $modx->request->handleRequest(). Additionally. RSS. and Smarty data are generated on-demand by MODx.php file.php . You will find the error. and the limit on the number of cacheable resources will disappear. with the exception of the manager files and the setup files. as well as files you don't want web-accessible. core/export/ . the GPL license. The connectors/resource/index. Lexicons. Every context (ie. They don't do any database manipulation on their own. you can use the $modx->log() method. core/cache/mgr/ This directory contains cache data for the mgr (Manager) context.php will then "handle" the request and call the correct Processor file. web and mgr) has a context.php. This new caching mechanism means that loading times will decrease.log file here.php file. when we create a resource.cache. and then handle the request by pointing to the appropriate Processor file. and sanitize the GET or POST request. except that it only caches settings that have been overridden from their default System Setting. For example.cache.connectors/ Connectors are essentially entry points for AJAX requests in MODx.php?action=create. it will cache any context settings that have been overridden from their default System Settings. the MODx Revolution cache is split up into several parts. Typically. core/docs/ This directory contains the changelog. Notable Files connectors/index.php file will include the base connector file (connectors/index. file. core/cache/logs/ All file logging in MODx is done here. It is the base for all the libraries for Revolution. The index. core/cache/ The cache directory contains all of the cache files generated by MODx. A resource with ID 12 will be found at cache/web/resources/12. resources. such as controllers. they simply load up the main MODx class. Most everything you need. core/cache/rss/ A cache of every RSS feed in MODx. handle any custom Context switching. elements. and any tutorials that have been created for Revolution. are in this directory. core/config/ This directory contains the configuration file for MODx Revolution. 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. To log an entry to this file. core/model/smarty/ .php".are subcategories of classes. and the Controllers. core/model/ This is the model. one would use a format such as this: $modx->lexicon->load( 'lang:namespace:topic' ). topic .class. core/lexicon/ Lexicons in Revolution are different from language files in Evolution for two main reasons. # lang . They are never accessed directly. A "topic" is simply a single lexicon file. Package creators will also be able to create a custom namespace. These are the classes that are either xPDOObjects .inc. but is never actually read or parsed when MODx is running. Well. This makes it possible to manage lexicons directly from the Manager. mssql. core/model/modx/mysql/ This directory contains the class and map files for each xPDO object. which contains the structure of the database and the hooks into it. inside the Lexicon Management area. modsnippet. including the processors that handle specific functions ." being the separation of directories. it can be inferred that the core/model/modx directory is referring to the "modx" package. such as modcachemanager. all lexicons are stored in the MODx database. The tutorials on creating 3rd party components teach more about schemas. and you'll see a ton of classes. in which we add a Connector access point and Processors to the model.class. and others would also appear here. So. Splitting lexicons into topics means that only the required language strings are loaded. core/model/modx/ "Wait! I thought we were already in a modx dir? Why another modx subdirectory?" Good question. with the ". 1. To load a lexicon. This is used in building new maps and classes. We'll explain those as we come to them. you need to move your HTML files into this directory. What's a model. the exported HTML files for your site will be located here. English lexicons are stored in /core/lexicon/en/). First. it's the M in MVC (model-view-controller). I'd create a new xPDO package. in the format "topic. removing chunks. that are loaded like: $modx->loadClass('transport. 2. This allows you to lock them down to prevent unauthorized access. as it is mainly used for development work. which is the GUI part of the application that contains no logic . core/model/schema/ The schema is the XML representation of the MODx database. The built-in namespace for MODx is "core". This way I could use the maps and classes created without having to modify the MODx core. that said.php.After running the Export function in MODx Revolution. So. the View. Second. MODx does model sort-of similar. Inside these subdirectories are multiple files. Other database platforms such as pgsql. For the most part. you say? Well.php is a PHP class that is an object of modx_site_snippets). MODx Revolution uses xPDO for its database management.the 2-digit IANA code. and Manager users can also create their own namespaces as well. in Revolution. So.Each lexicon has its own Namespace. or they are functional classes. This is optional. you can ignore this directory. and instead are accessed through connectors. which is an OO paradigm that states that there should be at least three parts to an application.which are PHP classes that represent tables in the DB (ie. core/model/modx/processors/ This directory contains the individual processor files used in database manipulation.such as saving snippets.just presentation. and add it in at runtime. Maps are simply PHP arrays containing the structure of the database table they reference. etc. which connect the model to the view. if I wanted to create my custom tables.The specific topic/file you want to load. depending on their two-digit IANA code (for example. The Model. and defaults to 'en'. and later cached on-demand.not including mysql or processors . We actually do a MVC/C model. The subdirectories in this folder . What you need to know is that the model contains all the PHP classes that run Revolution. lexicon files are split up into separate directories. This is shown in the Creating a 3rd Party Component tutorial. saving memory and loading time.modPackageBuilder'). core/import/ To run the Import function in MODx Revolution. xPDO uses the idea of 'packages' for different connections to different models. Let's go inside it. namespace . that happens elsewhere. Ditto.idx. This allows for easy installation and removal. When you build a package (for example. Nothing in this folder is customized for MODx . Most pages seen in the Manager and during Setup are Smarty template (.This is the cache file for all of the System Settings in MODx.php . as well as remote updating of installed packages. if you're doing a lot of JavaScript work. Notable Files manager/assets/ext2/ext-all. manager/assets/templates/ This directory contains the template files for each manager page. This directory contains all of the class files needed by xPDO to do everything from query caching. you're looking at a page generated by the controller at manager/controllers/resource/staticresource/update. you can edit this file and set use_captcha to '0' to disable CAPTCHA. this code renders the page: $modx->smarty->assign('resource'. and makes it possible for MODx to support various database platforms besides MySQL. chunks. Smarty is an intelligent.tpl file for a particular manager page. an extension to PDO.php . core/cache/mgr/actions. this file contained the cache data for all documents. core/packages/ Here you will find any transport packages you've downloaded via the Package Management section of Revolution. If you are looking for the Smarty . These classes are used by MODx internally. decrease download time. and performing overall site maintenance tasks. The core package is also found here as well. They simply fetch data and return or output it to the browser for rendering and display. manager/ The Manager is the MODx backend or administration area for creating resources. after checking out from SVN).js . Tip . Then you can log in and disable CAPTCHA in System Settings. and their xPDO equivalents are modSystemSetting objects.php . and snippets. ModExt extends the original ExtJS library. which must be included on all Manager pages (or any page using Ext). It's compressed to save space.This contains the Smarty libraries. and .tpl'). The best way to deal with this is to simply rename this file. managing users. However. this is no longer the case.In MODx Evolution. object-oriented templating engine that uses dynamic.This is the main Ext library file. you're bound to run into some cryptic errors because of the compression. and developers should never need to deal with them directly.tpl) files that MODx interacts with. The Smarty placeholders in update.net. but rather are used to organize HTML. to make development more convenient for users.$resource). It provides a uniform interface for manipulating databases. return $modx->smarty->fetch('resource/staticresource/update.cache.cache. and speed up page loads. Their database equivalents are found in the _system_settings table. It's simply an extraction of the Smarty files you can get from http://smarty.tpl are filled in with the data held in the $resource array. In Revolution.php. to building transport packages and outputting data as a convenient JSON object. Whenever you load a page in the Manager.If you ever get locked out by the CAPTCHA component. When you edit a resource (often a document) in the Manager. manager/assets/controllers/ Controllers are the PHP files tied to modActions. check in the manager/templates/default/ directory. modifiable placeholders. which simply loads a Smarty template and outputs any necessary JavaScript to the browser. as well as the custom ModExt implementation. the transport package will be stored here. core/cache/sitePublishing. for example.a map of all modAction objects. core/xpdo/ MODx Revolution was designed to use OpenExpedio (xPDO). and this file now keeps track of cache refresh intervals. etc. After setting the characteristics of the resource in the $resource array. such as TinyMCE.php. They do not contain PHP code. Notable Files core/cache/config. manager/assets/ This directory contains the ExtJS libraries. you are in effect telling MODx to load a particular Controller. 0-pl implementations.0. such as JavaScript or images. and other media in here.1 Fix any major issues in JIRA Revolution 2.then rename the ext-all. Revolution 2.2 Transport Package dependencies Add Static Elements Add Heirarchical Elements Revolution 2. and prior to running Setup. It contains the necessary files needed to run Setup and perform a Fresh Installation or an Upgrade. assets/ This directory is not present in MODx Revolution by default. _build/ This directory is only present in version of MODx Revolution downloaded from the subversion server (as well as the "SDK" distribution). Migration of the Revolution codebase to Git will also occur at this time.core.js to use the uncompressed version during development.js file to ext-all-debug. which will contain all of the necessary Vehicles for installing MODx Revolution. It contains the packaged MODx core data files necessary to install MODx to a database.1 Add Custom Dashboards Simplify setup/ process More API methods Revolution 2. Roadmap This is a work-in-progress roadmap for MODx Revolution. the core team will concentrate on creating distributions for different 2. an assets/components/<component_name>/ directory will be created to hold any necessary component files. Tasks in purple are already finished in Git.0.3 Permissions/ACLs for specific Users User Group Settings Revolution 3. you should notice a "core" directory inside your core/packages/ directory.php . After completion. but like in MODx Evolution. JavaScript.This file must be executed after downloading MODx Revolution. assets/components/ When you install a package using the Package Manager. Revolution 2. CSS.0 . it is common to place images. Notable Files _build/transport.0 After PL release. Just be sure to switch them back afterwards! setup/ This directory is the equivalent of the "install" directory in MODx Evolution. This lets MODx know what Resource to fetch when you are loading a webpage. rename. You can move. There are different types of Resources. such as documents. Managing Resources Resources are shown in the Resources tree in the left-hand navigation of the manager. To edit one.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. There are 4 total types of Resources. simply click on the page you would like to edit. weblinks. You can alternatively right-click the Resource and click 'Edit Resource'. symlinks. actual files. and Static Resources. Also. when you're wanting to link between Rsources. or "Resource Identifier". Weblinks. and the ID will stay the same . The default Resource type is a Document. Each Resource also has a unique ID. This will load the Resource Edit page: . Symlinks. you will use this ID to do so. or many other things. 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. Structuring Your Site This section contains information about MODx objects that are used in the structure of your website. alter or even change the type of a Resource. and simply represents a webpage on your site. which allows MODx to not have to worry about the resulting URL. and they are Documents.meaning that any changes you make to the Resource wont affect your links to it. Also called 'Hide from Menus'. If the Resource is deleted or not. Higher order means later. If the Resource is cacheable. A Resource with alias 'home' and Content Type 'html' would render 'home. Also called 'Summary'.The content of the Resource can then be edited in the large content field in the bottom area. if set. The ID of the user who created the Resource. Useful for blogs or searching. Resource Fields Resources come packaged with a list of predetermined fields by default. The ID of the last user to edit the Resource. The Parent Resource's ID. The ID of the user who last published the Resource. A reference to the Template that this Resource is using If the Resource is Published. The title to show for the Resource when displayed in a menu. an introductory excerpt of the Resource's content.html' if it isn't a Container. if your site is using Friendly URLs. . An extended description of the Resource. The ID of the user who deleted the Resource. this Resource will not show in most Menu or Navigation snippets. The date the Resource was created. If the Resource is searchable. They are: General Resource Fields Name id template published pagetitle longtitle description introtext alias parent menutitle menuindex hidemenu content Description The ID of the Resource. this specifies whether or not the Resource renders with a / in Friendly URLs instead of its suffix. The URL alias to use. The order index of the Resource in a menu. Other fields related to each Resource can also be edited via the tabs on the top of the page. A longer title of the Resource. or viewable on the front-end. Settings Resource Fields Name isfolder searchable cacheable createdby editedby deleted deletedby publishedby createdon Description Labeled as 'Container'. The actual content of the Resource. The title of the Resource. . URL Parameters for Resource Tags Adding URL parameters in your Resource Tag is quite simple in Revolution. The date the Resource was last edited. via the $modx->resource reference. links to Resources are dynamically managed via "Resource Tags". with a value of 'Snacks' and a 'sort' parameter of 'Taste'. You can put these tags anywhere.publishedon editedon The date the Resource was published. For example. You can also get the Resource Tag by dragging a Resource from the left tree into the content panel.html?tag=Snacks&sort=Taste Note that those are backticks instead of apostrophes. and MODx will dynamically render the URL for the Resource. this example Snippet will return the current page's pagetitle reversed: /* output the current Resource's pagetitle */ $output = $modx->resource->get('pagetitle'). MODx provides you with the Resource object in any Snippet. Let's say we have Resource ID 42 that resolves to a URL of 'store/items. They look like this: [[~123]] where '123' is the ID of the Resource to link to. return strrev($output). [[*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. adds an . Here's how you'd do it: [[~42? &tag=`Snacks` &sort=`Taste`]] This would render as: store/items. ie: [[*pagetitle]] // renders the pagetitle. Linking to a Resource In MODx. Also see Named Anchor. [[*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.html'.. // If it is longer than 100 chars. Using Resource Fields Resource fields can be accessed from anywhere by using the Template Variable syntax. We want to add a 'tag' parameter to the URL. URL Schemes in Resource Tags . They are associated with file extensions and tell the MODx Parser what type of extension to render the page with.css" will render as: test. This allows you to create any type of file from Resources. You'll see a grid populated with all the current Content Types. a Resources with an alias of 'test' and Content Type "CSS" that has a file extension of ". The available schemes are: Name https full Description Renders the link with https instead of http 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.html. Creating New Content Types First. This will automatically associate that Resources with the selected Content Type. simply select the Content Type that you'd like to use: Then save the Resources. Click on 'New Content Type'. and a window will appear: .css instead of test. go to System -> Content Types. Usage When editing a Resource. For example.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. You can point a Static Resource to any file on your webserver. It is mainly for organizational and labeling purposes.The fields that appear are as follows: Name . Binary .Is the file type text/ascii or binary? Description . this . File Extensions . which will tell the browser what type of file the Resources is.This is the file extension to render the Resource as. From there. click "save" and the Content Type will appear in the grid. and does not affect the function of the type. MIME Type .Here you can set the MIME Type for the extension. 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.This is the name of the Content Type. 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.An optional field for your own descriptive purposes. A list of available MIME Types can be found here or here. See Also Resources Named Anchor What is a Named Anchor? A named anchor is a link to content within the current resource. 2) If you do not see it -. an alias and a reference to the "master" document itself.so you can specify custom paths to set as System Settings. You can add a new template or what ever changes you may want to the symlink. See Also Using Resource Symlinks Understanding a Symlink A MODx Revolution symlink simply takes the content of one resource and displays it in another. Content within a Static Resource is parsed and displayed in the exact same way as a Document. They behave exactly like a Document (standard Resource). but you can not add additional content to it. however. and you can place it anywhere within the site. See Also Symlink What is a Symlink? Symlinks are similar to Weblinks in that they redirect to another Resources or URL. How do I use this feature? 1) Create the symlink with a name. Static Resources can also have tags inside their content fields to determine the path of the file .try clearing the site cache / document cache. Example: . symlinks will persist the current URL. or use Snippets to dynamically find the path.allows you to manage these Resources externally. a Privacy Policy. It has no template. You would put those Resources in one "utility . Weblink What is a Weblink? A weblink is a document of type "reference". By creating a subdomain in the control panel and then a symlink in the root of the website to content burried further down. 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. as soon as it sees that it is a "reference". You can use an external URL for the content. it just uses the content as the argument for sendRedirect($url). or you can use a Resource ID to link to a Resource in your MODx Resource tree. and is in the top-level of your tree to be displayed in your main menu. and others. Contact Us is one of your main pages. 4square. The content of the weblink is just an URL.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. But you also want a link to "Contact Us" there. It simply serves as a link that will be part of a generated menu. Example Say you want a footer menu with links to a Terms of Use. you could create a "doorway" page or a fast means to getting to a group of related content. The parser doesn't even parse it. and/or a sidebar.pages" folder. you could do it the other way around. Usage To create a Template -. thus causing the page to be loaded by the parser. A Resources can only be using one Template . but only one house. just like a house. Select "Create a New Template" then paste your HTML into the "Template Code" textarea. The parser will detect that the Weblink's content is a number. The Template. it can switch Templates at any time. When a document is requested. have the Contact Us Resource in your unpublished "utility pages" folder. A Template usually contains the header and footer of a page . then.Expand the "Elements" part of the tree and right click on Templates. Or. 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. This way the menu will include a link to the contact page. etc. also changes the main way a page is displayed. just as a person can move from house to house at any time. 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> . probably unpublished so it won't show up in your main menu. In that folder you would also put a Weblink to your site's contact page. just as to any other MODx resource. and use that folder ID as the Resource ID for the menu snippet. is a person. MODx loads the document and its template. navigation bar. Think of a Template like a house. and put the Weblink to it in your top-level so it will show in the main menu. triggering the redirect. Your Resource's content. Originally a menu snippet would make the link to the Weblink itself. and run it through makeUrl($id) before redirecting. even though that Resource is not in the folder. A person can live in many different houses.however. See Also Templates What are Templates? Templates typically contain the HTML markup tags that determine the layout and appearance of your site. You'll then see the TV in any Resource that's using that Template. and assign it to your "BiographyPages" Template. For example. you can edit any Resource and choose a Template for it by selecting one from the "Uses Template" drop-down list.. and only display the first 400 characters . a TV's value for that Resource will be able to be edited when editing the Resource. Once assigned to the Template.and if longer. make sure you've assigned that TV to the Template. Templates can contain any tags. 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.Note the important [[*content]] tag.): <div id="rightbar"> [[*introtext:stripTags:ellipsis=`400`]] </div> Template Variables in Templates If Templates are like a house. call it "bioPhoto". 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. After you've created one or more Templates. If you're not seeing a newly created TV in your Resources. if we wanted to show the current Resource's pagetitle in our <title> tag. and others. Say you want a 'photo' field on your Resources in your "BiographyPages" Template. including Template Variables. but strip any HTML tags from it. Each MODx page has a single Template that it uses to format output. You can have an infinite number of TVs in a Template. 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. you must create a template using the manager. A list of available Resource Fields can be found here. Simple . add an ellipsis (. give it an input and output type of "image".just create a TV. it's important to note that Template Variables must be explicitly assigned to the Template to be used. Chunks. the fields of a Resource can be referenced using the [[*fieldName]] syntax. say we wanted to display the "introtext" field on a right navbar. Snippets. For example. think of Template Variables (TVs) like rooms in that house. Using Resource Fields in the Template As you noticed from our Template sample code above. this tag tells MODx where to put the Resource's content. See Also Tag Syntax Template Variables Chunks . MODx stores template data in its database. in that they can have output filters applied to them. 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. you cannot create a template by uploading a file to the filesystem. You could fill those values with: [[$intro? &name=`George` &messageCount=`12`]] Which would output: Hello. similar in function to include files or "blocks" in other content management systems. George. . Common examples of Chunks might be your contact information or a copyright notice. You can also pass properties to a Chunk. you reference it by name in your templates or in your page content. You have [[*messageCount]] messages. [[+name]]. You have 12 messages. you must first create and name one by pasting text into the MODx manager (Elements --> Chunks --> New Chunk): Usage To use the Chunk. 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. which are executable bits of PHP code which produce dynamic output. You have [[+messageCount]] messages. [[$chunkName]] That reference is then replaced with the contents of the Chunk. they can however contain calls to Snippets. [[*usersName]]. You could even take it one step further. Although Chunks cannot contain any logic directly. Create Before you can use a Chunk.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. Say you had a chunk named 'intro' with the contents: Hello. $i++. ?> See Also modChunk Using Snippets Using a Snippet Snippet Properties Installing Snippets See Also Snippets are MODx's answer to inline PHP code. } return '<table><tbody>'.$properties). array('name' => 'MyExistingChunk')). blog or news listings.'NewChunkName'). $chunk->save(). foreach ($resources as $resource) { $properties = $resource->toArray(). setting the class to "alt" if for even rows: $resources = $modx->getCollection('modResource'. give it some content and save it to the database */ $chunk = $modx->newObject('modChunk').array('published' => true)).$output. Modifying a Chunk Via the API Chunks can also be manipulated by the MODx API: <?php /* create a new chunk. modify the content and save changes to the database */ $chunk = $modx->getObject('modChunk'. and returns formatted results as a table. $i = 0. They provide customizable dynamic content. for example. array('name' => 'MyObsoleteChunk')).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. $output = ''. . 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.= $modx->getChunk('rowTpl'. $output . $properties['rowCls'] = $i % 2 ? '' : 'alt'.'</tbody></table>'. if ($chunk) $chunk->remove(). /* get an existing chunk. search and other form-based functionality and anything else that your site needs to generate on-demand. $chunk->setContent('<p>This is my new chunk!</p>'). $chunk->set('name'. such as menus. } /* get an existing chunk and delete it from the database */ $chunk = $modx->getObject('modChunk'. $chunk->save(). if ($chunk) { $chunk->setContent('<p>This is my existing chunks new content!</p>'). improve parsing performance and avoid confusion with many new adopters.g. Tag Format Changes for Content Elements and Content Tags Content Elements Templates Template Variables Chunks Snippets Plugins Modules Evolution (Old) no tag representation [*templatevar*] {{chunk }} [[snippet]] no tag representation no tag representation Revolution (New) no tag representation [[*templatevar]] [[$chunk]] [[snippet]] no tag representation does not exist in Revolution . e. Installing Snippets You can also download and install Snippets via Package Management. 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. setting it instead to 2. you can also call a snippet uncached: [[!MySnippet]] Snippet Properties Snippets can have Properties. you can use it simply by putting its tags in your template. which is a dynamic collection of properties that can be attached to any Snippet (or Element for that matter). 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.Using a Snippet Once you have a Snippet installed. which can be passed in the Snippet call. [[tokenIdentifier]]. [[MySnippet]] If you expect the snippet code to be dynamic for different users. like so: [[!Wayfinder? &startId=`0` &level=`1`]] You can also aggregate these Properties into a Property Set. or a document's content wherever you want the Snippet's output to be displayed. a chunk or TV. This allows you to share common property configs in a snippet call in one place. See the tutorial on installing a Package for more information. And even those properties can be overridden: [[!Wayfinder@Menu? &level=`2`]] which would override the set's value on `level` of 1. See Also Installing a Package Tag Syntax To simplify parsing logic. all tags are now of a single format. g. as well. Template Variables What is a Template Variable? Usage See Also . So. Properties All tags . this was required to be done with a Snippet.no longer just Snippets . wow!?&=`). no longer. let's say we had a Chunk named 'Hello' with the content: Hello [[+name]]! You'll note the new placeholder syntax. e. In 096. For example. Customizing Content MODx offers many ways of customizing content. [[!$chunk]]. 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. 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. etc. Previously. 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. &param=`?=&is ok now.Content Tags Placeholders Links System Settings Language [+placeholder+] [~link~] [(system_setting)] no tag representation [[+placeholder]] [[~link]] [[++system_setting]] [[%language_string_key]] Adopting this simplified format allows the new parser to be fully-recursive. we'll definitely want to parse that Chunk's property. Caching In Evolution.now accept properties. each tag set was parsed independently in a specific order. with any embedded tags delayed until the next pass. MODx Content Tags offer a powerful new set of capabilities for mashing up your content. [[!+placeholder]]. following a source-order mechanism that does not depend on regular expressions. that can be used. This section covers the ways to do so that are built into the core of MODx. any tag can be called uncached by inserting an exclamation point immediately after the double-bracket: [[!snippet]]. one level at a time. [[!*template_var]]. page title.What is a Template Variable? A Template Variable (TV) is a custom Resource Field that is created by the site developer. you have 123 messages. TVs are Template-specific. alias). and each page is a row. Output Filters are also great tools to be applied to TVs. you have [[+messageCount]] messages. TV tags are replaced with the actual value entered by the user. for example a page about books might require additional attributes for "author" or "publisher". we'd simply place this tag anywhere we want: [[*bioPhoto]] TVs can also have Properties. General Settings First off.g. You can also specify a caption and description. Template Variable Output Renders make it easier for users to add special visual effects to their web sites in a matter of seconds. When a Resources is displayed on the web. By default there are a specified number of properties or columns for each page (e. TVs in MODx are case-sensitive. so you'll want to be careful about the name you choose. Usage Let's say we have a TV named 'bioPhoto'. We've assigned it to our 'Biography Pages' Template. . that is an Image TV we created. you'll want to specify a name for the Template Variable. Template Variables allow for you to extend the attributes based on your content. Imagine that you represent the pages on your site via an Excel spreadsheet: each property is a column on that spreadsheet. MODx allows you to have a virtually unlimited number of TVs. and want to show it on our page. Say you had a TV called 'intromsg' with the value: Hello [[+name]]. content. 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. You could fill the data with the call: [[*intromsg?name=`George` &messageCount=`123`]] Which would output: Hello George. meaning they can only be used in Templates that they are assigned to. URL or custom render to your website. A TV is used to represent a value inside a Resource. With just a few clicks you can add an Image. Say you wanted to limit a TV's output to 100 chars. To do so. you'll want to select an input type: . you can select all kinds of rendering options for the TV. First off.Rendering Options (Input Type) From there. TVs can be restricted to certain Resource Groups. In it. Also. In our content.These determine how the input form is rendered for the TV in the MODx manager interface. Properties From there. We'll select 'Date' as well. as well. Say we created a Property Set named 'CarsSectionTVPS' (PS for Property Set). 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. 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. 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. and as you'll note. a Data Source is the location of the information to be displayed. let's say we're doing a textarea TV named "viewingSS". we'll select the output rendering options. or dropdown. "How can you use properties on a TV?". or Template. For this example. and then allow that property to be overridden via property sets. We'd then attach it to the TV in our Resource. 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. we'll choose "Date". This allows those Resources assigned to those Templates to edit the TVs for each Resource. we can specify any default properties we want for the TV. In addition. we've got this: Viewing: [[+subsection]] We can add a list property 'subsection' to the grid. Well. selectable in the grid labeled "Access Permissions". the bound data in the TVs can be almost effortlessly formatted via the Display Controls within the TV system for some truly stunning results. Should we have chosen a list. The format for using the types of data source bindings available to all template variables follows: . See Also Bindings What are @ Bindings? In the context to Template Variables. Next. we set the 'subsection' property to "Cars". you might ask. . Usage @CHUNK MycontactForm See Also Template Variables . When placing @ bindings inside the "Default Value" field the returned value is used to render to the final web page. Please note that @ bindings will work only when used inside "Input Option Values" or "Default Value" fields. they are used to format input options only when editing document within the Manager. Where chunk_name is the name of the chunk... When placing @ bindings inside the "Input Option Values" field. Syntax @CHUNK chunk_name Binds the variable to a document.@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. This binding is very similar to the @RESOURCE binding with the exception that it will bind the TV to a Chunk. For example. etc). 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. Some display controls will attempt to either convert the returned value into a string or an array. 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. The value returned is dependent on the type of binding used. The returned value is a string containing the content of the chunk. The value returned from the data source can either be a string value (including numbers. This makes it simple to build complex forms for data input on the web very quickly. an array or a recordset. dates. for example to create a drop-down list of Cities or Countries. g./dir_above_root This binding will work with or without a trailing slash in the directory name.. REMEMBER: it returns ALL contents of a directory. e." syntax. If you want to list files above your site's root. See Also Template Variables Bindings EVAL Binding What is the @EVAL Binding? The @EVAL Binding executes the specified PHP code.3124. using *.g. Be careful if you were using this binding to select JavaScript files to be executed. This can really useful when you tie it into a List control widget. e.g. including all files and all directories . It is not an absolute file path. the path used for the @DIRECTORY binding is relative to the site's root. you must use the ".jpg # doesn't work! There are PHP code snippets out there that emulate this functionality. In MODx Revolution. If you are using the @DIRECTORY binding for your template variable [[*myTV]]. if you want to do something like give the user a list of logo images to choose for a page. 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.jpg? The following DOES NOT WORK: @DIRECTORY /list/*. place the following text into the Input Option Values box: @DIRECTORY /path/to/some_directory Frequently.html Security Depending on how the file is used on the page.php/topic.. See the following forum thread: http://modxcms. it may pose a security risk.with the sole exception of directories prefixed with a period.Bindings DIRECTORY Binding What is the @DIRECTORY Binding? The DIRECTORY binding reads the contents of a directory. or choose which mp3 file plays on a particular page. Syntax @EVAL php_code_here . What if a user had the ability to upload (and thus execute) a JavaScript file? Also.com/forums/index. Usage When you create a Template Variable. always be wary of letting users see your directory structure. this is coupled with an Input Type of "DropDown List Menu" to allow the user to select a file from the list. It should be used with careful security precautions.0. @DIRECTORY /. We can use the Delimiter render to separate each item and display them one at a time.time(). 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. We then add the @FILE command inside the default value of the TV.txt file is separated by a new-line (lf or \n) character.txt Let's say each headline in the headline_news. The file path is the absolute path from the root of the server or your particular installation. Our fields will look like this: Unable to render embedded object: File (filebinding. This will point to where the headline_news. Our default value might look like this: @FILE assets/news/headline_news. Be careful with these! Thankfully. This nefarious user could eval some nasty unlink() or rmdir() statements and destroy your web server files. 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. @EVAL $a = 'dog'.Usage Simply put a PHP statement after the @EVAL tag: @EVAL return "The time stamp is now ". return $a.txt that is external to our database system.png) not found. This file is constantly being updated with up-to-the-minute news items by another external system.. The @FILE command is very useful in cases where we might want to generate data that's available in file. Security The eval() statement raises an eyebrow with anyone concerned with security: eval statements are notorious for being exploited. If I let my cynical mind wander. The return value is a string containing the content of the file. By using the || and == characters as a delimiter we could interface with any external database application. or read sensitive files anywhere on the web-server that PHP has access to. but this context is supported by MODx. I've been unsuccessful in my attempts to unlink() a file using the @EVAL binding. How can we do that? First.txt is located in our example.. And viola! We have our dynamically rendering @FILE binding. Usage For example: Let's say we have a text file called headline_news. where file_path is the path and name of the file. We want to display these news items on our website for our visitors to see. we might create a new Template Variable. See Also Template Variables Bindings INHERIT Binding .. but I'm sure there are people out there more clever than me. so it's recommended that you find another way of doing whatever you are trying to do.. where resource_id is the ID of the Resource. The returned value is a string containing the parsed content of the Resource. 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!) . it will look at that parent's parent's value. Once you've verified that your query works. then you can create a @SELECT binding with it. 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. below for some pit-falls).What is the @INHERIT Binding? The @INHERIT binding will automatically find the value of the parent Resource and use that as its value. and so forth. you need to be familiar with MySQL syntax. Syntax @SELECT * FROM site_content To write one of these. It is recommended that you first write a functional MySQL statement that executes without error in the MySQL command line (see errors. Syntax @RESOURCE resource_id Binds the variable to a Resource. If it ends up at the root and no value has been specified. 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. If the parent Resource also has @INHERIT. the value will be 0. Here's a more tangible example: let's say all the pages in a particular folder have a template variable for opening_date.. 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. Architecturally. this table represents instances of the particular class.. you end up smashing your nose directly into the nasty truth that the formats used by PHP's strftime() (stored in site_tmplvars.tmplvarid='6' /* 6 is the opening_date */ AND DATE_FORMAT(STR_TO_DATE(tv_val. Paste the "@SELECT . The page where you want to put this binding must be using a valid template.. . one row in the site_tmplvars table might have multiple rows in this table (one row for each instance of the variable). You can also place the query into the "Default Value" box for the Template Variable. '%d-%m-%Y %H:%i:%s').value. the site_tmplvar_contentvalues. A Snippet might do the job more easily than a binding. we want to filter based on a custom date field named "opening_date". This is open to some debate.id). First.You can place this binding in one of two places: On a page (edit the page in the manager).. If you care to think of this architecturally. site_tmplvars . It sounds more complicated than it is. it's not even always clear that a column ''is'' a foreign key. site_tmplvar_templates . it just takes a bit of patience to figure out. In our example. you may run into headaches. REMEMBER: The query must be on ONE LINE. Pretty much everything that's done with the bindings is also possible with Snippets. E.. If you've created the Template Variable and associated it with a Template. be careful. the bindings just provide a shortcut. The "name" field is what triggers the substitution. MODx's database schema doesn't follow the strict best practices for foreign keys. because your pages might require a specific type of output. tmplvarid (foreign key back to site_tmplvars..contains the name of template variable. consider doing this a different way. A name of "my_template_variable" should be used as "[[*my_template_variable]]". but if you look closely. the output type that a @SELECT binding returns. This forces you to write a JOIN statement. There may be a way to automatically do this.value field is a text field.contains the values of the template variables for each page that uses them. but it's easier to just hard-code it." stuff in there. so you'll have to make use of MySQL's str_to_date() function. it is possible.display_params) are not the same as what MySQL can use in its STR_TO_DATE() function.g.value. You have to look at MODx's gory plumbing in order to pull this off. When you start over-using the shortcut. because this gets complicated.id WHERE page. rank.id=tv_val.. If you replace the default text of a Template Variable that's already in use. but unfortunately. have a look at the following tables (you may have prefixes to your table names): site_templates . this table defines the variable class: the name and type of variable that a series of pages will have. tv_val.. FROM site_content as page JOIN site_tmplvar_contentvalues as tv_val ON page.g. but this section is written verbosely for the sake of clear documentation.value. The database table has 4 fields: id. MySQL won't automatically recognize arbitrary text as a date value.. e. but rest assured. value (a text field). '%Y-%m-%d %H:%i:%s')>'2008-10-24 13:04:57' .contains the actual template code used for the site (lots of HTML appears in the content field). You might end up with a query like this: SELECT page.. but it's not. that field doesn't exist in the "site_content" table. templateid. they are stored in other tables. and the page you're working on is using that Template. and that template must have the correct template variable(s) associated with it. '%Y-%m-%d %H:%i:%s') as `Formatted Opening Date`. You may think that the site_tmplvars. DATE_FORMAT(STR_TO_DATE(tv_val. the @SELECT binding is probably not the way to go. Hold onto your butts..parent='95' AND tv_val.display_params is a savior here. No returns! Alternatives Before we get any more complicated. Contains 3 fields: tmplvarid. contentid (foreign key back to site_content.id). site_tmplvar_contentvalues . '%d-%m-%Y %H:%i:%s'). In other words.. it's not always clear which table is being referenced by a particular column.alias. 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". then you'll have a place to enter in some text for that variable when you edit the page. If your query needs to work with template variables and you need specialized formatting for the output.This is a mapping table which associates a Template Variable with a Template (maps site_template:id to site_tmplvars:id). So you'd then choose the Output Render of the TV to be an Image. say you have a TV that uses a Textbox as its Input Type. INSERT.. not InnoDB. That's dangerous. formatted in the way you want. gasp. For example. Next Step: Formatting Ok. it fails? Well. MODx will log the error to the error log. or DELETE queries (or. Errors What if your MySQL statement executes perfectly. date. and the @SELECT binding may expose some of those same vulnerabilities. image. That's great . The implementation isn't perfect. DROP TABLE statements)? Even if it doesn't directly support this. You can write your own Snippet that formats the value of a Template Variable. you might get some mileage out of the Output Renders. now what? If you need to format it intelligently. Newline characters cause the @SELECT binding to choke. Security Does this binding let you execute UPDATE. The user would then choose an Image through the TV input on their Resource. At the very least.except your TV only outputs the URL of the image! You want it to output the image itself. 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.such as a URL. so it does not rigidly enforce the foreign key constraints that are inferred by the table structure. Output Properties It's output properties look like: . huh? MODx Revolution comes packaged with a few default Output Types. If your query has an error. so you can return a bunch of data from the database. Delete all MySQL comments /* this style */ and -. there are some pit-falls. You can also create your own. and boom! Your image TV now outputs the image directly! Sweet.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. or anything else you can think of. you may be able to construct and execute a complex query that SELECT's the result of a such a destructive query. 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.MODx uses the MyISAM table engine. if you know a little PHP. 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 . but once you put it in your SELECT binding. 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. Pay close attention to the following: Your query MUST appear on one line. but you might find the available options limiting to you.. Delimiter TV Output Type Delimiter TV Output Type This type allows you to output any TV input as a delimited list. The output type will split the value on each double-bar (||). Output Properties It's output properties look like: Name Delimiter See Also Description A delimiter to separated each item with. It's very useful for TV inputs that store multiple values. or checkbox inputs. use the current time? This defaults to 'no'. which will output a blank value. If no value is set for the TV.Name Date Format Default See Also Description A format string similar to the PHP strftime method. Output Properties It's output properties look like: . such as the multiple select list. and then output it delimited by the delimiter value you set in the properties. HTML Tag TV Output Type HTML Tag TV Output Type This type allows you to wrap an HTML tag around the input. put here: core/model/modx/processors/element/tv/renders/mgr/input/test. Let's say my "test" TV input type loads a Template selecting combobox. radio.tpl The input controller. Any other attributes you want to add to the tag. img.Name Tag Name Tag ID Class Style Attributes See Also Description The tag to create.tpl. etc types already available) for your Template Variables. Creating the Files To create a custom TV input type (let's say. 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. // any other PHP i want here return $this->xpdo->smarty->fetch('element/tv/renders/input/test. etc.php An input template . test. you need a few things. Any style attributes to add to the tag. one called "test"). richtext.tpl').php. test. textarea. such as div. The dom ID of the tag. would have: $this->xpdo->lexicon->load('tv_widget'). span. for the default mgr theme would have (note that it is using Smarty syntax): . And the input template. Any CSS classes to add to the tag.put here: manager/templates/default/element/tv/renders/input/test. I'd first need 2 files: An input controller . 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.width: 300 {literal} .<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' . URL.php An input properties template .tpl An output controller . This will render an input button (or more than one) with a specified value and an optional name.put here: core/model/modx/processors/element/tv/renders/mgr/properties/button. with some other fields for attributes. etc. It could even just be a straight HTML input. It's really up to you. We'll have it contain just this: .put here: core/model/modx/processors/element/tv/renders/web/output/button.id: 'tv{$tv->id}' . You'll need 3 files: An input properties controller .listeners: { 'select': { fn:MODx. scope:this}} }).transform: 'tv{$tv->id}' . Creating the Files Let's create a custom TV Output Type called "button". date.put here: manager/templates/default/element/tv/renders/properties/button.fireResourceFormChange. MODx Revolution lets you create custom output types fairly easily.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. {/literal} // ]]> </script> And there you go! A custom TV input type. HTML tag. Some examples include outputting a TV value as an image. You don't have to use the ExtJS code as shown here to have a custom input type. autoHeight: true .{ xtype: 'textfield' .layout: 'form' . 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.value: params['attributes'] || '' .id: 'prop_id{/literal}{$tv}{literal}' .renderTo: 'tv-wprops-form{/literal}{$tv}{literal}' }).fieldLabel: _('style') .width: 300 .{ xtype: 'textfield' .items: [{ xtype: 'textfield' .fieldLabel: _('class') . Setting up the Input Properties Template This is the template for the default manager theme to render properties with.load({ xtype: 'panel' .<?php // any custom php you want to run here return $modx->smarty->fetch('element/tv/renders/properties/button.markDirty().value: params['id'] || '' .foreach.id: 'prop_style{/literal}{$tv}{literal}' . var oc = {'change':{fn:function(){Ext.border: false .last}. MODx.name: 'prop_attributes' . This tells MODx to save this field in the TV's output properties.}.tpl'). Make sure you specify your fields with this prefix! .name: 'prop_style' .width: 300 .id: 'prop_class{/literal}{$tv}{literal}' .scope:this}}. // ]]> </script> {/literal} The key way these save is that each field is prepended with 'prop_' in its name.listeners: oc }] .id: 'prop_attributes{/literal}{$tv}{literal}' .value: params['class'] || '' .fieldLabel: _('id') .getCmp('modx-panel-tv').width: 300 .listeners: oc }.name: 'prop_class' .p.labelWidth: 150 .listeners: oc }.value: params['style'] || '' .fieldLabel: _('attributes') .{/if} {/foreach}{literal} }.name: 'prop_id' .{ xtype: 'textfield' .width: 300 .listeners: oc }. I've added some custom values to it as well: So we'll save this. we'll have the first button have a custom 'name' attribute as well: Great. '||'.'="'. $buttons= $this->parseInput($value. /* allow multiple buttons separated by ||. or checkbox/multiple input tvs */ foreach ($buttons as $button) { if (!is_array($button)) { $button= explode('=='. We'll specify two buttons. you'd have to create a properties tpl for that theme as well.'"' : '').it's totally up to you. Our file looks like this (comments inline): <?php $o= ''.$text. $attr = array( 'class' => $params['class']. and we'll get an output like this: . We could also just do one button.$params['attrib']. 'alt' => htmlspecialchars($params['alttext']). 'array'). } /* the TV value must have a value of either: text or text==name */ $text = $button[0]. however . 'style' => $params['style'] ). 'id' => ($params['id'] ? $params['id'] : '').= ($v ? ' '.'</button>'. /* separate the attributes into html tag format */ foreach ($attr as $k => $v) $attributes. /* Output the image with attributes */ $o .You don't have to use ExtJS.= '<button'. separating with ||. if (!empty($text)) { $attributes = ''. $button). } } return $o. Using the Custom TV Output Type So. /* if a name is specified.$v. how does it look? Well.= ' '. and then let's go edit in in a Resource. use it! */ if (!empty($button[1])) $attr['name'] = $button[1]. And.rtrim($attributes). This controller will handle exactly how the button is outputted.$k. Setting up the Output Controller Now we get into the good stuff.you can use just straight HTML .'>'. Note that if you created another manager theme. Now let's preview the resource. it should render an output form like this when editing the TV . $attributes ."\n". where '1' is the Property Value. Snippets and Plugins can access them through the $scriptProperties array. it will be overridden in the call. as they are extract()'ed. once attached. Since the property 'user' is defined as 2 in the Property Set. Properties will be passed into the Element just as they were in MODx 0. let's have a Property Set with two properties . or in the tag call.6. Elements via that Element's editing page. if the default property of 'user' was 0. An example of a Property is the token 'debug' in this Snippet call: [[Quip? &debug=`1`]] 'debug' is the Property.9.: [[TestSnippet@DebugMode?user=`1`]] This example would call the snippet "TestSnippet". and 'user' set to 2. but they are also passed in via the $scriptProperties array.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. for those of you wanting more flexibility with knowing what properties are passed in. They are passed to the Element's parser and interpreted there. What are Property Sets? Property Sets are user-defined collections of properties for an Element. Those property sets.and make it much easier to manage your tag calls. and then to 1 by the call. This can be useful to load Elements across the site without having to repeat the tag syntax across the site . and then would set the value 'user' to 1.'debug' set to true. then it would then be set to 2 by the property set. and end up as 1. They can be attached to one. Then let's call it in a snippet. for an example. then can be called in their Element's call syntax like such: [[ElementName@PropertySetName]] So. The order of property loading is: Default Element Properties -> Property Set -> Tag-defined Properties So. Assigning Property Sets to Elements . or straight in their key values. load the Property Set 'DebugMode'. or more. The property set can also contain properties not defined in either the default element properties. It will show a window like this: From there. and these fields will show: . To add a property set to an Element. but are defined in the Property Set. you'll simply click the "Add Property Set" toolbar item in the top right of the grid. you can select the property set you want to add.Property Sets can only be used on Elements that they are assigned to. as shown by the + icon to the left. This can be done via either the element's properties grid. here's an image of a property set named 'TestPropertySet' in a snippet's editing page: As you can see here. When clicked. Properties that are purple are properties that do not exist in the Element's default properties. you can do so by checking the "Create New Property Set" checkbox. the description will appear below. Properties can also have descriptions. If you'd like to create a completely new Property Set and automatically attach it to this element. For example. there is a property set loaded with some properties. or Tools -> Property Sets. Properties in green are default properties that have been overridden in the property set. Simply click on the corresponding buttons at the bottom. Note here that we are creating a property of type "List". That will load this window: From there. and then click "Create Property". When you import properties. it will be automatically attached to that Element. Importing and Exporting Properties You can also import and export properties using the grid. You can add options to that property from the grid. you can create a property set. which is a dropdown property. Make sure that you want to do this before importing! Using Properties Programmatically . Once you save the property. it will overwrite your properties in the grid currently. Creating Properties in a Property Set To create a Property in a Property Set.Then once you save your new Property Set. you'll simply need to just load the Property Set you want to work on. it will be added to the property set. Uses the current value the system setting "modx_charset" Safely escapes character values Example [[+numbooks:cat=` books`]] [[+title:lcase]] [[+headline:ucase]] [[+title:ucwords]] [[+name:ucfirst]] [[+email:htmlent]] [[+email:escape]] . the parameter value will override the default value in the property set. Output Filters behave similarly to PHx calls in MODx Evolution . Note that if a parameter is sent in the snippet call that has the same name as a property in a property set. More documentation to come. 'default').escape Description Appends the option's value (if not empty) to the input value Similar to PHP's strtolower Similar to PHP's strtoupper Similar to PHP's ucwords Similar to PHP's ucfirst Similar to PHP's htmlentities. Input Filters Currently input filters process tag calls.except they're built into the core. 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. Output Filters In Revolution. 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 cat lcase ucase ucwords ucfirst htmlent esc. $scriptProperties.Properties are available in a snippet via the $scriptProperties array: $prop = $scriptProperties['propertyName']. Using getOption You can also get a snippet property with $modx->getOption() like this: $modx->getOption('propertyName'. They allow you to modify values from inside your templates. `]] [[+pagetitle:replace=`Mr. The element must be a modUser ID.decrement. The value field is the column to grab. Similar to PHP's strtotime.`]] [[+code:strip_tags]] [[+longstring:strlen]] [[+mirrortext:reverse]] [[+bodytext:wordwrap=`80`]] [[+bodytext:wordwrap=`80`]] [[+description:limit=`50`]] [[+description:ellipsis=`50`]] [[+showThis:tag]] [[+downloads:incr]] [[+blackjack:add=`21`]] [[+countdown:decr]] [[+moneys:subtract=`100`]] [[+trifecta:mpy=`3`] [[+rating:div=`4`]] [[+number:mod]] [[+name:default=`anonymous`]] [[+name:notempty=`Hello [[+name]]!`]] [[+textfile:nl2br]] [[+birthyear:date=`%Y`]] [[+thetime:strtotime]] [[+createdon:fuzzydate]] [[+createdon:ago]] [[+password:md5]] [[+content:cdata]] [[+userId:userinfo=`username`]] [[+mystring:urlencode]] [[+myparam:urldecode]] Examples A good example of chaining would be to format a date string to another format.increment. Defaults to 100. Returns true if user is not authenticated in this context. Similar to PHP's urlencode Similar to PHP's urldecode [[+textdocument:strip]] [[+name:stripString=`Mr. Takes optional value to set wordwrap position.mod ifempty. with word cutting enabled. Returns a pretty date format with yesterday and today being filtered. Adds an ellipsis to and truncates a string if it's longer than a certain number of characters.length reverse wordwrap wordwrap limit ellipsis tag math add. Useful for documentation. tabs and multiple spaces with just one space Strips string of specified value Replaces one value with another Similar to PHP's strip_tags Similar to PHP's strlen Similar to PHP's strrev Similar to PHP's wordwrap. Takes in a date. weeks or months ago.incr subtract. Displays the raw element without the :tag. minutes. Returns the option modulus on input (default: %2. returns 0 or 1) Returns the input value if empty Returns the input value if not empty Similar to PHP's nl2br Similar to PHP's strftime. Returns true if user is authenticated in this context.strip stripString replace stripTags. Takes optional value to set wordwrap position.==Mrs. Similar to PHP's md5. Defaults to 100.empty notempty nl2br date strtotime fuzzydate ago md5 cdata userinfo isloggedin isnotloggedin urlencode urldecode Replaces all linebreaks. Value is format.mpy divide.default. like so: . Similar to PHP's wordwrap.div modulus. Limits a string to a certain number of characters. Takes in a date. Wraps the text with CDATA tags Returns the requested user data.decr multiply. not recommended) Returns input incremented by option (default: +1) Returns input decremented by option (default: -1) Returns input multiplied by option (default: *2) Returns input divided by option (default: /2) Does not accept 0. Takes in a date. Returns a pretty date format in seconds.notags len. Returns the result of an advanced calculation (expensive on processor. Select the appropriate column from the table and link to it. The name of the parent element. so you dont need to use the "userinfo" filter: [[+modx. See Also Properties and Property Sets Templates Template Variables Snippets Administering Your Site .Prints the ID [[+modx.user.user.[[+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. Snippets can be used as custom modifiers and filters. The complete parent tag.id]] . Example Result The value of [[+file]] 'notitle' + (the token on `file`) file [[+file:makeDownloadLink=`notitle`]] And then the return value of that call would be whatever the snippet returns. The type of the parent element. Simply put the Snippet name instead of the modifier.username]] . Example with a snippet named 'makeDownloadLink': [[+file:makeDownloadLink=`notitle`]] This will pass these properties to the snippet: Param input options token name tag Value The element's value.Prints the username Also. Any value passed to the modifier. 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. and getOption respects that. and site_start was still '1' (from the System Setting). context. the setting would be 1. This example sets a default value to the 'showPublished' property should it not be specified: . overriding the Context Setting. An array to search first before looking for the setting 3. and I had added a Context Setting for site_start in the 'boo' Context. as well. I create a User Setting 'use_editor' for his user and set it to 0. the above code would output '3'. I created a Context Setting called 'use_editor' for the 'mgr' context and set it to 1. I've got a user named 'johndoe' who I don't want to use the editor. for example. getOption would return 1. which are specific to each Context. and set it to 3. Now. This would mean that any time I'm in the mgr context.but only if you're executing the PHP in that Context that has the Setting. For example. So. Retrieving Settings in PHP Getting settings is simple in MODx Revolution. Further. overriding the System Setting. if you had set site_start as a Context Setting that overrode the System Setting. So. The key of the setting 2. the order of relevance is: System Setting -> Context Setting -> User Setting Let's say I set the System Setting named 'use_editor' to 0.This section contains information about maintaining and administering your MODx site. For example. Settings can also be specific to Namespaces. which are specific to each user. if in the above code example. However. for example. simply: $siteStartId = $modx->getOption('site_start'). simply use getOption. to get the setting 'site_start'. Usage They can be referenced at any point via their Tag. if I were in a Snippet and wanted some default properties at the top. Snippets automatically pass in an array of all the Properties attached to that snippet (or specified in the Tag call) via the $scriptProperties array. John Doe's "use_editor" setting will be 0. Default values with getOption getOption supports 3 parameters: 1. as described above. all settings are overridable by Context and User. I could use getOption. Context Settings can be overridden by User Settings. So. A default value should the setting not be found. getOption will respect that . This allows you to easily group your settings for each of your different Components. Now. if I were using that code block in a Context called 'boo'. for the 'site_start' Setting: [[++site_start]] System Settings can also be overridden by Context Settings. Now if I were in the 'web' context. or user-level customization. you can use that array to check for default properties. 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. then showPublished would be 0. If it did have the default Property attached to it that set the value to 0. assuming the Snippet doesnt have showPublished as a default property. or the showPublished property was specified as 0 in the tag.$scriptProperties. and can easily be edited and changed. 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. Now. Users may share the same email address. this will allow duplicate aliases to be saved. allow_multiple_emails allow_multiple_emails Name: Allow Duplicate Emails for Users Type: Yes/No Default: Yes If enabled. if you called the Snippet via the tag call: [[mySnippet]] showPublished will be set to true. allow_tags_in_post .$showPublished = $modx->getOption('showPublished'.true). 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 option should be used with use_alias_path set to 'Yes' in order to avoid problems when referencing a resource. They are found in System -> System Settings. 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. MODx will automatically check for updates for packages in Package Management. Do not change this value for the manager context. 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. actions (or controller maps) will be cached to greatly reduce manager page load times. auto_check_pkg_updates auto_check_pkg_updates Name: Automatic Check for Package Updates Type: Yes/No Default: Yes If 'Yes'. This can cause problems in a MODx install if you change it without an explicit purpose. Only use this in a Context if you specifically want to. auto_menuindex auto_menuindex Name: Menu Indexing Default Type: Yes/No Default: Yes Select 'Yes' to turn on automatic menu index incrementing by default. cache_action_map cache_action_map Name: Enable Action Map Cache Type: Yes/No Default: Yes When enabled.allow_tags_in_post Name: Allow Tags in Post Type: Yes/No Default: Yes If set to true. This may slow the loading of the Packages grid. . will allow POST requests to contain HTML form tags. cache_default cache_default Name: Cacheable Default Type: Yes/No Default: Yes Makes all new resources cacheable by default. the cache will never expire unless a record is updated. This feature is experimental. This can take up a good amount of hard drive space. cache_handler cache_handler Name: Cache Handler Class . If set to '0'. Only do this if you have enough space to scale the requests. objects and raw result sets from SQL queries are cached to significantly reduce database loads. as it can significantly slow down your site. disables all MODx caching features. cache_disabled cache_disabled Name: Disable Global Cache Options Type: Yes/No Default: No If true. cache_db cache_db Name: Enable Database Cache Type: Yes/No Default: No When enabled. MODx recommends not turning off caching site-wide. 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.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. Can be set to a custom caching class you provide. cache_json cache_json Name: Cache JSON Data Type: Yes/No Default: No If Yes. or xPDOMemCache if you have memcached installed. all Lexicon Topics will be cached so as to greatly reduce load times for Internationalization functionality. non-core Lexicon Topics will be not be cached. caching will be stored JSON format. 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. This is useful to disable when developing your own Extras. A value of zero means the cache never expires. . cache_noncore_lexicon_topics cache_noncore_lexicon_topics Name: Cache Non-Core Lexicon Topics Type: Yes/No Default: Yes When disabled. A value of 0 means the cache will never expire. MODx strongly recommends leaving this set to Yes. cache_lexicon_topics cache_lexicon_topics Name: Cache Lexicon Topics Type: Yes/No Default: Yes When enabled. as setting it to No will greatly slow down your manager load times.Type: String Default: xPDOFileCache The class name of the type handler to use for caching. 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. cache_json_expires cache_json_expires Name: Expiration Time for JSON Cache Type: Number Default: 0 Expiration time for JSON format cached data. Disabling this feature will disable it globally. container_suffix container_suffix . MODx will use a compressed version of its custom CSS stylesheets. compress_css compress_css Name: Use Compressed CSS Stylesheets Type: Yes/No Default: Yes When this is enabled. Leave set to 'No'. MODx recommends leaving this set to 'Yes'. Leave it at 'No'. This setting will not work in Git deployments of Revolution. Disable only if you are modifying core elements. This setting will not work in Git deployments of Revolution. concat_js concat_js Name: Use Concatenated Javascript Libraries Type: Yes/No Default: Yes When this is enabled. Disable only if you are modifying core elements. MODx will use a compressed version of its custom JavaScript libraries. Leave at 'No'. This greatly reduces load and execution time. MODx will cache all Scripts (Snippets and Plugins) to file to reduce load times. This setting will not work in Git deployments of Revolution. This greatly reduces load and execution time. compress_js compress_js Name: Use Compressed Javascript Libraries Type: Yes/No Default: Yes When this is enabled. Disable only if you are modifying core elements. This greatly reduces load and execution time.cache_scripts cache_scripts Name: Enable Script Cache Type: Yes/No Default: Yes When enabled. MODx will use a concatenated version of its custom core JavaScript libraries. To specify the controller location for each class. If you do not wish to load a style sheet into the editor.css. This is the suffix added to the Friendly URL when a Resource is checked as a container. The best way to enter the path is to enter the path from the root of your server. 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. this setting just pre-selects one of the Templates.html cultureKey cultureKey Name: Language Type: String Default: en Select the language for all non-manager Contexts.html' will render: www. editor_css_selectors editor_css_selectors Name: CSS Selectors for Editor .mysite.com/test/ whereas a container suffix of '. for example: /assets/site/style.Name: Container Suffix Type: String Default: / Sets the container suffix for the site. All custom Resource classes must extend modResource. Example A container suffix of / will render a Resource with an alias of 'test' as: www. default_template default_template Name: Default Template Type: Template Default: 1 The default Template to use for new Resources. leave this field blank.com/test. 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. You can still select a different Template in the Resource editing page. Specify with lowercase_lexicon_key:className (Ex: wiki_resource:WikiResource). custom_resource_classes custom_resource_classes Name: Custom Resource Classes Type: String Default: A comma-separated list of custom Resource classes. including web.mysite. This setting is deprecated and no longer in use. feed_modx_news feed_modx_news Name: MODx News Feed URL Type: String . emailsubject email_subject Name: Email Subject Type: String Default: Your login details The subject line for the default signup email. 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. 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.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. html if it's not a container. friendly_alias_urls friendly_alias_urls Name: Use Friendly URL Aliases Type: Yes/No Default: No Turns on friendly URLs.com/modx-announce Set the URL for the RSS feed for the MODx News panel in the manager. and its "alias" field. or an absolute path. For example. then the blog Resource's URL would be: www.com/modxsecurity Set the URL for the RSS feed for the MODx Security panel in the manager.feedburner. feed_modx_news_enabled feed_modx_news_enabled Name: Enable MODx News Feed Type: Yes/No Default: Yes If 'No'. a Resource with alias 'blog' and a Content Type of "HTML" will be rendered as www. 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. feed_modx_security_enabled feed_modx_security_enabled Name: Enable MODx Security Feed Type: Yes/No Default: Yes If 'No'.com/blog.mysite. The URL map is determined by the Resource's place in the site tree. Usage Specify either a relative path from the MODx root directory.Default: http://feeds.feedburner. MODx will hide the Security feed in the welcome section of the manager. MODx will hide the News feed in the welcome section of the manager.com/test/blog. . which generate SEO-friendly URL mappings for your Resources. It can be outside of the webroot. feed_modx_security feed_modx_security Name: MODx Security Feed URL Type: String Default: http://feeds. If the blog Resource was under another Resource with an alias of 'test'.html This allows for completely customizable and automatically built SEO-friendly URLs.mysite. 'iso-8859-1' or 'UTF-8'.g. and change the RewriteBase parameter in it to your site's base URL to get this to work. a prefix setting of 'page' will turn the URL /index.htaccess file included in the distribution for more info. See the . See Content Types for how suffixes are now handled.html' will append .You'll need to rename the ht.html (assuming the suffix is set to .html). 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. e. and you'll need to write a . mail_charset mail_charset Name: Mail Charset Type: String Default: UTF-8 The (default) charset for e-mails. friendly_url_suffix friendly_url_suffix Name: Friendly URL Suffix Type: String Default: .html This setting is deprecated and is no longer in use. Specifying '. friendly_urls friendly_urls Name: Use Friendly URLs Type: Yes/No Default: No This allows you to use search engine friendly URLs with MODx.access file to . This way you can specify what your users (and search engines) see for links on your site.php?id=2 to the friendly URL /page2. this only works for MODx installations running on Apache. Here you can specify the suffix for Friendly URLs. Please note. Here you can specify the prefix to use for friendly URLs.htaccess file for this to work.html to all your friendly URLs.htaccess in your site root. mail_encoding mail_encoding Name: Mail Encoding Type: String Default: 8bit . For example. mail_smtp_hosts mail_smtp_hosts Name: SMTP Hosts Type: String Default: localhost Sets the SMTP hosts. Utilizes the mail_smtp_user and mail_smtp_pass settings. Not recommended.com").example. and "quoted-printable". 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.Sets the Encoding of the message.com:25. mail_smtp_helo mail_smtp_helo Name: SMTP Helo Message Type: String Default: Sets the SMTP HELO of the message (Defaults to the hostname). "binary".g. Options for this are "8bit". You can also specify a different port for each host by using this format: hostname:port (e. mail_smtp_prefix . mail_smtp_pass mail_smtp_pass Name: SMTP Password Type: String Default: The password to authenticate to SMTP against.smtp2. mail_smtp_auth mail_smtp_auth Name: Use SMTP Authentication Type: Yes/No Default: No Sets SMTP authentication.example. "smtp1. "7bit". Hosts will be tried in order. All hosts must be separated by a semicolon. "base64". mail_smtp_port mail_smtp_port Name: SMTP Port Type: String Default: 587 Sets the default SMTP server port. mail_use_smtp mail_use_smtp Name: Use SMTP in Mail Type: Yes/No Default: No If true.mail_smtp_prefix Name: SMTP Connection Prefix Type: String Default: Sets connection prefix. manager_direction manager_text_direction Name: Manager Text Direction Type: String Default: ltr . instead of sending to entire TO addresses. in PHP date() format. for the dates represented in the manager. This function will not work in win32 servers. "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. mail_smtp_timeout mail_smtp_timeout Name: SMTP Timeout Type: Number Default: 10 Sets the SMTP server timeout in seconds. 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. Options are "". mail_smtp_user mail_smtp_user Name: SMTP User Type: String Default: The user to authenticate to SMTP against. This will override any cultureKey setting for the mgr context. new_folder_permissions . the File Manager will attempt to change the file permissions to those entered in this setting. new_file_permissions new_file_permissions Name: New File Permissions Type: string Default: 0644 When uploading a new file in the File Manager. manager_language manager_language Name: Language for the Manager Type: String Default: en The language for the MODx Manager interface. for the time settings represented in the manager. in PHP date() format. such as IIS. left to right or right to left. Please note that MODx primarily works with UTF-8. This may not work on some setups. This will ensure that the browser can present content in the best format for you. 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. modx_charset modx_charset Name: Character Encoding Type: string Default: UTF-8 The character encoding used. manager_theme manager_theme Name: Manager Theme Type: String Default: default The current Theme for the backend Manager. in which case you will need to manually change the permissions. manager_time_format manager_time_format Name: Manager Time Format Type: String Default: g:i a The format string.The direction that the text will be rendered in the Manager. 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. such as IIS. in which case you will need to manually change the permissions. 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_source_enabled phpthumb_cache_source_enabled Name: phpThumb Cache Source Files Type: Yes/No . 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. 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_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.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. Defaults to C to force aspect ratio toward the center. MODx will attempt to use the proxy when connecting to the RSS feeds or Package Management. proxy_auth_type proxy_auth_type Name: Proxy Authentication Type Type: String Default: BASIC Supports either BASIC or NTLM. phpthumb_zoomcrop phpthumb_zoomcrop Name: phpThumb Zoom-Crop Type: String Default: 0 The default zc setting for phpThumb when used in MODx. proxy_password proxy_password Name: Proxy Password Type: String Default: The password to authenticate with on your proxy server. phpthumb_far phpthumb_far Name: phpThumb Force Aspect Ratio Type: Yes/No Default: No The default far setting for phpThumb when used in MODx. Recommended to leave at No. proxy_port proxy_port Name: Proxy Port Type: String Default: The port for your proxy server.Default: No Whether or not to cache source files as they are loaded. If this is specified. Defaults to 0 to prevent zoom cropping. . proxy_host proxy_host Name: Proxy Host Type: String Default: The hostname of your proxy server. you can enter the path to the images directory here (the path as you'd see it in Windows Explorer). Most users can leave this as index. rb_base_url rb_base_url Name: Resource Browser Base URL Type: String Default: Enter the virtual path to resource directory. MODx may not be able to work the URL out on it's own. If you're using IIS.php. request_param_alias . you can enter the URL to the images directory here (the URL as you'd enter it on Internet Explorer). request_controller request_controller Name: Request Controller Filename Type: String Default: index. rb_base_dir rb_base_dir Name: Resource Path Type: String Default: The physical path to the resource directory. publish_default publish_default Name: Published Default Type: Yes/No Default: No If true. makes all new resources published by default. however. causing the Resource Browser to show an error.php The filename of the main request controller from which MODx is loaded. This setting is usually automatically generated. Please use filemanager_path instead. Please use filemanager_path instead.proxy_username proxy_username Name: Proxy Username Type: String Default: The username to authenticate against with your proxy server. This setting is deprecated in MODx Revolution. causing the Resource Browser to show an error. In that case. If you're using IIS. This setting is usually automatically generated. MODx may not be able to work the path out on its own. This setting is deprecated in MODx Revolution. however. In that case. 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. if a document with an alias called 'child' is located inside a container document with an alias called 'parent'. 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. then the full alias path to the document will be displayed as '/parent/child. welcome_screen welcome_screen Name: Show Welcome Screen Type: Yes/No Default: No If set to true. use_browser use_browser Name: Use File Browser Type: Yes/No Default: Yes If set to Yes. and then not show after that.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. will enable any installed Rich Text Editor in the manager. enables the resource browser. This setting is deprecated in MODx Revolution. 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.html'. Use the file_tree and file_manager Permissions instead. This will allow your users to browse and upload resources such as images. For example. use_editor use_editor Name: Enable RichText Editor Type: Yes/No Default: Yes If Yes. flash and media files on the server. which_editor which_editor Name: RichText Editor to Use . the welcome screen will show on the next successful loading of the welcome page. htaccess sample MODx supplies an ht. You can download and install additional Rich Text editors from Package Management.*)$ index. # Force all pages to go to www. your server uses The .php?q=$1 [L. 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.DOMAINANME. net. change the TLD to the com. whatever you have For me: com # Friendly URLs Part RewriteEngine On RewriteBase / RewriteCond %{HTTP_HOST} . Be aware some control panels like to write their own .DOMAINNAME\.TLD/$1 [R=301. For me: shawnwilkerson 2. Using Friendly URLs Summary: You can have firendly URLs fully functioning in under two minutes by following a simple three step process: 1) Working .php or above the document MODx is presenting. You can also simply cut and paste this one into any text editor and edit it accordingly: 1.domain.com for SEO RewriteCond %{HTTP_HOST} !^www\. info.access file for you to edit to match your server settings.L] # Friendly URLs RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule ^(.htacess has to be at the same level as the main index.*) http://www. org. change the DOMAINNAME to your domain name in lowercase.Type: Richtext Combo Default: Selects which rich text editor to use when editing Resources.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.TLD [NC] RewriteRule (. This file will need to renamed to .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) .[0-9]{2}" brokenvary=1 BrowserMatch "Opera" !brokenvary SetEnvIf brokenvary 1 force-no-vary /htdocs is fine or /public_html or what ever.htaccess when uploaded to the root of your site (can be anywhere above the MODx installation or at the top of the site). you'll have to override the context settings in the context for any real. changing the "Automatically generate alias" to YES from the default NO (shown below). Italicized content left to show entire file. sub-domains. This will prompt you for a key and description. sub-sites. 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. Creating a Context First.pdf file so I turned off automatically generate alias. specific web applications. cultural-specific sections. and an empty grid of settings. I personally had issues with linking directly to my resume. You can easily create a context from the Contexts menu under Tools. if you create a new context. 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. via System -> Contexts. The context will then show up in your resource tree. distinguishable change to appear. as my resume was the first thing I did after turning on Friendly URLS and even before building the first page or templates. requiring you to include any and all settings you will be using. Your new context will be completely empty. or shared across domains. right-click on your newly-created context. More than likely it was something I was not doing correctly. extended. . Then. and click "Update Context". isolated. but is not necessary for Friendly URLS. So.The RewriteBase should end with a / This works fine for Redhat Linux 5 / Apache setups. click on "Create New" in the grid. From here you can add Context-specific settings that will override any System Settings. Note: there's nothing fundamentally different about resources in different contexts. multi-sites. Resources can easily be dragged between contexts to move them from one context to another. go to the Contexts page. This will bring you to a screen displaying the Context.send them this code and ask them what you need to get it working. etc. If it is not working for you contact your hosting provider -. From there. except that they now inherit the configuration settings of the context they are in. modxcms. http_host . Note: If you're going to be linking back to the 'web' context from this context. Your tree should look something like this: From there. Remember this setting is (scheme+http_host+base_url). you can at least follow the same idea and customize it to your server. We'll call it "dev". go to System -> Contexts.Set this to "/" (no quotes) since we're making the root of the URL our base. From here we'll see a Context Settings tab. Go ahead and create a "Dev Home" Resource in the Resource tree to the left.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. of course) to the 'web' context.modxcms.com <VirtualHost dev.com DocumentRoot /home/modxcms. and others if you so choose. Creating the Virtual Host Now we need to do some Apache work. clear your site cache. go ahead and right-click on the "dev" Context in the tree.conf file.com ErrorLog logs/devmodxcms-error_log TransferLog logs/devmodxcms-access_log </VirtualHost> .modxcms. base_url . and click "Edit Context". Place it in the root of the "dev" context. you'll want to add those same Context Settings (with 'web'-specific values.modxcms.Set this to the ID of your "Dev Home" resource. (If you're not using Apache. and you'll need to add a few settings: site_start . Also. We're going to create a subdomain at dev. changing where necessary for your domain name: NameVirtualHost dev. Creating the Context First off.Set this to "http://dev. create a "Dev Docs" Resource as well with an alias of "documentation". such as error_page. Click on it.com/" (or your subdomain url). We'll use this to test our context links. not really.com (of course.com> ServerAdmin dev@modxcms. but you get the idea).modxcms. Don't forget the trailing slash.com" (or your subdomain name) site_url .modxcms. Then. and add these lines.Set this to "dev. create a new context. You can add other context-specific settings. This allows MODx to know where to redirect 'web' context URLs back to. unauthorized_page. while you're at it. After creating the settings.com/public_html/dev ServerName dev.) Go to Apache's httpd. you'll need to edit a couple of them. Note the link properly builds to: http://dev. It should match the base_url context setting you set up earlier.modxcms. and click 'Preview' on your "Dev Home" document.com. you'd want to change these values. You'll need to copy 3 files from your MODx Revolution's root directory: index.htaccess You'll only need to edit one line here (and maybe not at all). You should now be showing the page at the following URL: http://dev. The Subdomain Files Now we're going to need to create the actual files to load the subdomain.com/public_html/ (or whatever base path your webroot is in). Now. you would use a plugin to switch contexts registered to the OnHandleRequest event.php . Go create a "dev/" subdirectory in /home/modxcms. and find this line (near the end): $modx->initialize('web').Some Apache installs prefer you to put the IP address of the server in the VirtualHost and NameVirtualHost parameters .modxcms. If you use a single gateway. the important field is the ServerName. if you're creating a different subdomain than dev. . not anything else. Change 'web' to 'dev'.htaccess config.php Edit index. Final Steps Clear your site cache again.php Copy those to the dev/ directory.modxcms.this is fine. Find this line (near the top): RewriteBase / Make sure that's set to /.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. index.php.core.com/ Create a [[~135]] link to the "Dev Docs" Resource in the "Dev Home" Resource. 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. something like so: . Great! Restart your server (apachectl graceful). refresh the Resource tree. Obviously. Save the file and close. Reload your page. They also can be tied to User Groups.tld:80': case 'domain2. 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.php in the same directory and rename it to do this. which are database representations of any MODx Revolution Manager page. Note that you could also just copy the index. which govern how manager pages render their forms in the MODx Revolution Manager. Form Customization bases its methods off of Actions. you would simply copy the index. should the CMPs be using MODExt. break. You can also still use a custom core location in either of these scenarios. This also means that Custom Manager Pages can also have Rules applied to them.tld'). this is independent of the context-driven multi-site capabilities.tld': // if the http_host is of a specific domain. can be found here: . What are Rules? Rules in MODx Revolution are simply generic rules that apply to any Action (manager page). along with their fields and other info. don't do anything break. to $modx->initialize('aContextNameOfYourChoice'). wherein a Rule will only be applied if the User is part of that User Group. but your rewrite rules would have to be smart enough to route requests to the appropriate context gateway. A rule can be one of the following: What Forms can I Customize? Technically any manager page can be customized.config.php file from the default web context (along with the core. and you would need to configure the request_controller option in Context Settings appropriately. switch the context $modx->switchContext('domain2. A full list of documented customizable forms. default: // by default.php and . } ?> Alternatively.htaccess for rewrite rules altered appropriately) to another directory and change the line $modx->initialize('web'). It is similar to ManagerManager in MODx Evolution.<?php switch ($modx->getOption('http_host')) { case 'domain2. Resource Create/Update These pages encompass the following Actions: resource/update resource/create Available Fields Field Page Title Name pagetitle Containing Panel modx-panel-resource .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. just set the "Name" attribute of the Rule to "tv#".Published Long Title Description Introtext Link Attributes Alias Menu Title Menu Index Hide from Menus Container Rich Text Published On Publish Date Un-Publish Date Searchable Cacheable Deleted Empty Cache Content Type Content Disposition Class Key published longtitle description introtext link_attributes alias menutitle menuindex hidemenu isfolder richtext publishedon pub_date unpub_date searchable cacheable deleted syncsite content_type content_dispo class_key modx-panel-resource modx-panel-resource modx-panel-resource modx-panel-resource modx-panel-resource modx-panel-resource modx-panel-resource modx-panel-resource modx-panel-resource modx-panel-resource modx-panel-resource modx-panel-resource modx-panel-resource modx-panel-resource modx-panel-resource modx-panel-resource modx-panel-resource modx-panel-resource modx-panel-resource modx-panel-resource modx-panel-resource Available Tabs These tabs are available for renaming/hiding: Tab Create/edit Resource Page Settings Template Variables Access Permissions Name (ID) modx-resource-settings modx-page-settings modx-panel-resource-tv modx-resource-access-permissions Containing TabPanel modx-resource-tabs modx-resource-tabs modx-resource-tabs 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 . and replace # with the ID of the TV you'd like to affect. You can leave the Containing Panel blank. FC-Template . 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). wherein a Rule will only be applied if the User is part of that User Group. go to the Form Customization page. FC-Chunk This page is under construction. under the Security main menu. You'll be presented with a window: . What are the Rules available? The available Rules are: Creating a Rule First off. They also can be tied to User Groups. click "Add" in the grid's toolbar. FC-Snippet This page is under construction. From there.This page is under construction. FC-Plugin This page is under construction. If set. Field Action User Group Description Field Containing Panel Value Constraint Class Constraint Field Constraint Description This field lets you select any available Action. Optional No Yes Yes No No No Yes Yes Yes Constraints . The name of the field or tab to apply the Rule to. will restrict the Rule to a field value (via Constraint Field and Constraint) on this class. description of the Rule. The type of value can vary depending on the Rule. An optional. will restrict the Rule to the value of the field in the object specified in the constraint options.Let's go into the fields. will restrict the Rule to the field specified. The rule will only apply to that Action. with the value of Constraint If set. Here you can optionally choose a User Group to tie this Rule to. Example: modResource for the Resource Update screen. If set. Most all Rules will need this value. the Rule will only apply to Users of that User Group. The value you'd like to set the Rule to. The name of the containing panel that the Field or Tab exists in. If a User Group is chosen. for you. and no other. 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. Let's say you want to restrict a Rule for resource/update to only occur on Resource's with a Template ID of 4. See Also Field Default The Field Default Rule The Field Default Rule. will automatically set the default value of a field.Rule Constraints are special abilities that let you restrict the Rule to a certain value on an object's field. Examples An example Rule of setting the default Category for a Snippet for all Users in the User Group HR Department would look like this: . You'd simply set the Constraints like so: Constraint Class: modResource Constraint Field: template Constraint: 4 Or. not "update" pages. This will only work on "create" pages. when set. further. 0. when set. Field Label The Field Label Rule The Field Label Rule. Examples An example Rule of changing the introtext's label to "Comments" for the Users in the User Group "Marketing" would look like this: . will change a Label of a field to any text value.See Also The root page Form Customization could not be found in space MODx Revolution 2. Field Visible The Field Visible Rule The Field Visible Rule. 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. will declare a field "visible" or not to a user. when set.0. See Also The root page Form Customization could not be found in space MODx Revolution 2. Then specify the TV's ID prefixed with 'tv' in the "value" field. 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.0. The second tab. Usage Specify the ID of the tab to move to in the "name" field of the Rule. or Page Settings tab. The list of available tabs is: ID modx-resource-settings modx-page-settings Description The first tab. or Create/Edit resource tab. You can also create a tab with the New Tab rule and move a TV there. . . 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. when set.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. will create a new tab in the panel. Usage Specify the ID of this new tab in the "Name" field. . The Template Variable page. Tab Panels on non-Resource pages are only available in 2.0.ID modx-resource-tabs modx-chunk-tabs modx-snippet-tabs modx-plugin-tabs modx-template-tabs modx-tv-tabs modx-user-tabs modx-usergroup-tabs modx-context-tabs Corresponding Page The Resource edit/create page. The Context page. The User Group page. The Chunk page. The Snippet page. The User page. The Template page. 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-pl and up.0. The Plugin page. Tab Title The Tab Title Rule The Tab Title Rule will change the title of any Tab. Rule: Tab Title Value: The new text to change the tab title to. 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 . Containing Panel: The ID of the tabpanel to change. Usage The values for the Rule should be as follows: Name: The ID of the tab to change. For a list of tabs available for each page. see Form Customization Pages. 0. TV Default The TV Default Value Rule . 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. will hide a tab from a User. Tab Visible The Tab Visible Rule The Tab Visible Rule. if set to 0.The root page Form Customization could not be found in space MODx Revolution 2.0. Usage The values for the Rule should be as follows: Name: The ID of the TV prefixed with 'tv'. not "update" Actions. This only works on "create" Actions.The TV Default Value Rule.0. TV Title . for example. 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. will set the default value for a TV. if set. 'tv23' Containing Panel: 'modx-panel-resource' Rule: 'TV Default Value' Value: The value you want to set as the default. for the TV with ID 23. will hide a TV from a User. 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.The TV Title Rule The TV Title Rule will change the title of a TV.0. TV Visible The TV Visible Rule The TV Visible Rule. Examples An example Rule of hiding the a TV with ID of 1 for all Users would look like this: . if set to 0. Once an ACL is applied to an object. From here. a Context or Resource Group. for example. Each User Group then has attributes assigned to it via Access Control Lists (ACLs). and going to the 'Access Permissions' tab. by default. This is in MODx. you can give a User Group (say. meaning that access is "open" by default. Access is allow/deny in MODx.0. but all share one common principle they contain a list of Permissions. the .The list of Permissions that is gained by this ACL. those Contexts or Resource Groups will now only be accessible to the objects with appropriate Permissions. which can be assigned to any number of User Groups.The object they apply to. Authority . ACLs usually have the following: Principal .The minimum Authority level required to use this ACL (see Roles).The object that is getting the access 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'. Target . This can be done by editing the Context. 'HR Dept') explicit access to this Context by selecting the User Group. Each user in MODx has a User object.See Also The root page Form Customization could not be found in space MODx Revolution 2. and assigning an ACL to it. a User Group. These Permissions allow access to different areas or actions within MODx. such as a Context or Resource Group. Access Policy . Security Security in MODx Revolution Security in MODx Revolution is primarily driven by an Attribute-Based Access Control (ABAC) paradigm. These ACLs take a variety of names depending on how they are applied. 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.username]] . They can also be completely unique settings as well.user. 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.Prints the ID [[+modx. User Settings User Settings in MODx Revolution will automatically override for that user any System or Context settings with the same key.Prints the username If a user is not logged in. and specifying a required Authority (say. their username and ID can be accessed by the following Properties: [[+modx.'Administrator' Access Policy. which can have ACLs attached to them to provide Access Controls.id]] . 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. Users can also be assigned to User Groups. . User Fields Users contain the following fields: Name id username Description The ID of the user. The username of the user.user. ID will be blank. and Username will be "(anonymous)". The zip or postal code for the user. the user will not be able to log in. this snippet grabs the email of the user and returns it: . Note that to grab Profile fields. It contains the following fields: Name internalKey fullname email phone mobilephone fax blocked blockeduntil blockedafter logincount lastlogin thislogin failedlogincount sessionid dob gender address country city zip state photo comment website extended Description The ID of the user. A remote user Key used by remote authentication apps. The email of the user. 1 for male and 2 for female. The country of the user. For example. 0 for neither. An optional comment field for comments on the User. The User's session ID that maps to the session table. The physical address. you'll need to first get the modUserProfile object via the Profile alias. The number of logins for this user. this snippet outputs the username of the user: return $modx->user->get('username').password active remote_key remote_data The user's encrypted password. The number of times the user has failed to log in since last logging in. An optional field for a photo. will prevent the user from logging in until this date. The city of the user. Users also have a Profile attached to them. The full name of the user. If blocked is set to true. The cellphone number. A timestamp that. The fax number. when set. Not used in the UI. If not active. The time the user logged in in their current session. A JSON array that can be used to store extra fields for the User. The last time the user logged in. the user will not be able to log in. Either 1 or 0. when set. A JSON array of data used by remote authentication apps. The phone number. Either 1 or 0. A timestamp that. The website of the user. The date of birth. For example. Grabbing the User via the API The current user can be gotten in the API via the $modx->user reference. The physical state or province of the user. will prevent the user from logging in after this date. return $profile ? $profile->get('email') : ''. Roles in User Groups Users can have specific Roles within a User Group. $modx->user will still be available as an object. See Also User Groups What is a User Group? A User Group is simply a collection of Users. Just create a Role called "Supervisor". and then add in the Users to the User Group "HR Department" (via the User Group editing screen). See Also Resource Groups . From there you will see a tree of User Groups and their respective Users. should you choose. 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 . Roles allow you to fine-tune your permissions more than in previous MODx versions. They can also exist in the User Group without a Role. 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. Say you want to only allow Supervisors in the "HR Department" User Group access to some Resources. but will return 0 as the ID and (Anonymous) as the username.$profile = $modx->user->getOne('Profile'). 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. If the User is not logged in. Policies assigned on the Resource Group Access tab should be based on the standard Resource policy. set its authority to some number below 9999 (let's say 3). Users in this User Group would have access to this ACL as well. no problem. Usage Go to Security -> Access Controls. Policies assigned on the Element Category Access tab should be based on the standard Element policy.say you had a Coordinator Role with an authority of 2. Then. setting any would-be supervisors to the Supervisor Role. What is a Resource Group? A Resource Group is a collection of Resources. e. See Also Roles What is a Role? . 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.g. Usage You can add or delete members of a Resource Group in two different ways: Option 1 Go to Security -> Resource Groups. "Members Only" pages. From there you will see a tree of Resource Groups and a tree of Resources. and check which Resource Groups it is a member of. click on its "Access Permissions" tab. Usage One common example is to create Roles that mimic a basic employee position structure. Authority=1 is the first authority and trumps Authority=2 (i. We then set its Minimum Role value to "Supervisor": This means that Mark has these Permissions. Be sure you clarify your language when talking about Authority because this inverse relationship can lead to some confusing sentences. specifically). since he's in the User Group. "Editor" or "Front-end Read Only". the second authority). Mark has a Role of Supervisor. In MODx. It helps to think of "Authority" as ordinal numbers: first. we'll assign Users to those Roles (you can have multiple Users per Role. etc. Now. third. and has at least the Role of "Supervisor" (which is the Role he has.g.0 Director . as well). E. But this also means that John has these Permissions as well.A role is a position or status held within a certain situation.3 Employee . but a user Role with Authority 11 does NOT inherit any of the Group Policies from Role 10.e. since he is a "Coordinator" which has a stronger Authority level than "Supervisor".2 Supervisor . Let's say we create the following Roles and Authority levels: Administrator . a Role with Authority 10 will inherit any and all Group Policies assigned to itself and to any roles defined with Authority 11. second. So. You should generally avoid duplicate authority numbers. Within that User Group. let's say John has a Role of Coordinator. Roles in MODx use an integer value called "Authority". e.9999 We can then create a User Group called "HR Department". Lower numbers represent a stronger authority.g.1 Coordinator . it can be used to group Users into a position or status within a User Group. John as Coordinator has "inherited" the Permissions than Mark had as 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". 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 about access_permissions action_ok actions change_password change_profile content_types create credits customize_forms database database_truncate delete_category delete_chunk delete_context Description of Access The About page. Any Access Permission-related pages and actions. The Actions page. User can change their user password. User can change their profile. The Content Types page. Basic "create" access on objects. View the Credits page. View and manage the Customizing the Manager page. The System Info page The ability to truncate a database table. To delete or remove any Categories. To delete or remove any Chunks. To delete or remove any Contexts. delete_document delete_eventlog delete_plugin delete_snippet delete_template delete_tv delete_role delete_user edit_category edit_chunk edit_context edit_document edit_locked edit_parser edit_plugin edit_role edit_snippet edit_template edit_tv edit_user element_tree empty_cache export_static file_manager file_tree flush_sessions frames help home import_static languages lexicons list load logout logs menus messages namespaces To delete or remove any Resources. To empty the Event Log. To delete or remove any Plugins. To delete or remove any Snippets. To delete or remove any Templates. To delete or remove any Template Variables. To delete or remove any Roles. To delete or remove any Users. To edit any Categories. To edit any Chunks. To edit any Contexts. To edit any Resources. Allows a user to override a lock and edit a locked Resource. To edit any Plugins. To edit any Roles. To edit any Snippets. To edit any Templates. To edit any Template Variables. To edit any User. The ability to view the Elements Tree on the left nav. To empty the site cache. To export the site to static HTML. To use the file manager, including creating/deleting files. To view the Files Tree on the left nav. Can flush Sessions across the site. To use the MODx Manager UI at all. To view the Help page. To view the Welcome page. To view or use the Import pages. To edit or view Lexicon Languages. To edit or view Lexicons and Internationalization. Basic permission to "list" any object. List means to get a collection of objects. Basic permission to "load" any object, or be able to return it as an instance at all. To be able to logout as a user. To view the logs, such as error and manager logs. To edit or save any top Menu items. To send or view any personal Messages. To edit or view Namespaces. new_category new_chunk new_context new_document new_plugin new_role new_snippet new_template new_tv new_user packages property_sets providers publish_document purge_deleted remove remove_locks resource_tree save save_category save_chunk save_context save_document save_plugin save_role save_snippet save_template save_tv save_user search settings steal_locks unlock_element_properties view view_category view_chunk view_context view_document view_eventlog To create a new Category. To create a new Chunk. To create a new Context. To create a new Resources. To create a new Plugin. To create a new Role. To create a new Snippet. To create a new Template. To create a new Template Variable. To create a new User. To use any Transport Packages in the Package Management system. To view and edit Properties and Property Sets. To view and edit Providers across the site. To publish or unpublish any Resource. To empty the Recycle Bin. Basic permission to remove any object. To remove all existing Locks throughout the site. To view the Resource Tree in the left nav. Basic save permission for any object. To save any Categories. To save any Chunks. To save any Contexts. To save any Resources. To save any Plugins. To save any Roles. To save any Snippets. To save any Templates. To save any Template Variables. To save any User. To use the Search page. To view and edit any System Settings. To "steal" locks, overriding a current lock on a document. To be able to edit the default properties for any Element. Basic permission to "view" any object. To view any Categories. To view any Chunks. To view any Contexts. To view any Resources. To view the Event Log. To utilize Package Management. Permissions . 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 .view_offline view_plugin view_role view_snippet view_template view_tv view_unpublished view_user workspaces See Also To view any Plugins. To view any unpublished Resources. To view any Snippets. To view any Template Variables. To view any User. To view any Roles.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. To view any Templates. load. ACLs can be applied to any modAccessibleObject. or Access Control List..with at least the Minimum Role specified . partially written by BobRay.... 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. delete. Giving a User Manager Access The Problem You want a User to have manager editing access. etc) in the Policy specified . 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 . will help you through that. and basically allow you to restrict access to Resources (such as Documents. . but not have all the Permissions of an Administrator user. Resource ACL Resource ACLs behave a bit differently.. with at least the Minimum Role specified .. Primarily MODx Revolution 2. etc) by Resource Groups. Weblinks.Context ACL Resource ACL See Also What is an ACL (Access Control List)? An ACL. give the Resource Permissions (save... More information on ACLs can be found here in Wikipedia.. 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 .0 allows for ACLs on Resources and Contexts.that will give the Users all the Permissions in the Access Policy assigned. Usage In MODx. is a set of Permissions attached to an object.. This tutorial. Context: 'mgr' Minimum Role: 'Editor'. say "AdminLite". below follows a simple list to help you through the maze or to remind you how it works. 2. (Security -> Manage Users) 7. It should. Create a Resource Group 2. Link your member-only resource to the Resource Group 3. access policy: resource) 6. Add Resource access 6. 4. 10. 2. minimum role 'Member (9999)'. 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. 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. If you need more information and would like some examples. Access Policy 'AdminLite' 7. Create a Resource Group (Security -> Resource Groups -> Create Resource Group) Link your member-only resource to the Resource Group. For those that are savvy enough. Create a User Group 4. create your user. Context: 'web' Minimum Role: 'Editor'. If you're not quite as savvy. Edit the AdminLite Policy to use whatever Permissions you want the User to have. Add the User to the admin group with a role of Editor In Access Controls | Policies. 6. be possible to set up a member only page using this documentation. or by dragging them from the resource tree to the right) Create a User Group (Security -> Access Controls -> User Groups -> New User Group) Edit the user group you created to add Context Access (Security -> Access Controls -> User Groups -> Right click your user group -> on the Context Access tab add context "web". right click on the admin group and select "Update Group. Add users to the user group with the role of Member. Create a new role (say. Create a Resource Group . In Access Controls | User Groups. Click on Security -> Flush Sessions and re-login. 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. Add a resource group entry on the Resource Group Access tab (context: web. or would rather also know what happens when you set a certain permission or make an access entry. Access Policy 'AdminLite' b. you might find this section interesting. Editor) with an authority of say. 3. minimum role: Member (9999). 4.The Solution First off. 1. Add Context access 5. Add users to the user group 7. duplicate that policy. 1. 5. still! See Also Work in progress Please note that this document is a work in progress and may still be inaccurate. Then: 1. (By editing the resource. however." On the Context Access tab. scroll down to the related subsection below. duplicate the administrator policy and rename it to whatever you want. Do not edit it directly!)) 5. See Also Making Member-Only Pages Introduction Step-by-step explanation 1. access policy 'Resource' (if you want to modify permissions. 3. add two new ACL entries to the grid: a. Flush permissions Help! I can't get this to work. apply this context access: Context: web Minimum role: Member (9999) Access Policy: Resource Now. namely the Context. click on the New User Group button. 4. General Information: this contains the name and if applicable the parent group. In this case. 3. those pages will no longer show up for users that are not linked to the resource group. In the tutorial we expect you named it "Protected".9998. select "web" from the context dropdown. you should add resources to it. To do this. we'll add context access to the user group. Go to the Context Access tab again. This tab defines the resources your user has access to if they are protected by resource groups. It is important to realize that as soon as you have protected a resource by assigning it to one or more resource groups. For this. Three out of four fields are similar to the Context Access groups. Click on "Add Context". When a user group has access to a context. 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. you can easily classify pages to be only visible for certain user groups. Link your member-only resource to the Resource Group Now that you have a resource group. 4. The default behaviour in that case is displaying the 404-error page. as there's no way to recover changes made. As with everything MODx. it has zero access. go to Security -> Access Controls. Users: shows an overview of users in the user group and offers you to add new users. we'll make a new user group. for example "Protected" Context: web Minimum role: Member (9999) Access Policy: Resource . At this moment in the tutorial. The second option is editing your resource. 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. navigate to Security -> Resource Groups and click on the Create Resource Group button. or roles within user groups. 21. and ticking the right box on the "Access Permissions" tab. 2. So. choose an authority level of 9999. but can be used to limit access to elements. As we want our users to be able of accessing the "web" context (that means your resources in the Web context). 22. Element Category Access: will not be covered in this tutorial. The Minimum role refers to the role the user should have within the user group to receive the access to the context. A new one is Resource Group which. as you probably guessed.. as long as access to resources is not being blocked by resource groups. defines the resource group the user group can access. There are two ways to achive this. this is highly flexible. To start with. To make it applicable to all users in that group. On the (default) User Groups tab. In the popup you are prompted to enter a name for the resource group. we'll set this to Member (9999). 3. you can go to Security -> Resource Groups and drag resources from the right resource tree to the left resource groups ("Protected"). your page will not be visible as you have not yet added it to a user group. Resource Access: we will discuss this in step five. your user group has access to your Web context. right click the user group you just made and click on "Update User Group". so the user has no access to any contexts. To create a resource group. The Access Policy is key in this form. Per default there are several Access Policies added. Context Access: we will discuss this in a minute. First of all. Choose a name for the group. That determines what a user can actually do. When you have created a resource group. now you'll need to decide who will be able to view the resources. At the moment it should still be empty. Add Resource access Move on to the Resource Access tab. Add Context access At this point the user group means nothing.. 9999. If you would rather return the 403-error. viewing the User Group tab. You will find five different tabs: 1. This Access Policy includes all default permissions for resource access. As we want all users. 2. to sum up. we'll set it to Resource. 5. The settings: Resource Group: whatever you named it. More about this in a later tutorial. While still being on the Access Controls page.A resource group is a collection of resources which you can link to user groups and access list entries. Minimum Role and Access Policy. it can view (unprotected) resources within that context. you will need to give the anonymous user group "load" permission for the resource group. 5. Create a User Group You have a resource group with resource applied to them. and submit the form. as it will still use your Manager login to check for permissions. Add users to the user group Now add some users to the user group.51259. Flush permissions Now that all settings are done. Also make sure that. .0. Help! I can't get this to work. It will ask for the User Group. If everything seems to be alright. If you think the tutorial is misleading or inaccurate. Please note that in some cases it is also neccesary to clear the site cache. please visit the forum topic (linked below) and post about what is incorrect so it can be fixed. specifically for the mgr (manager) context. if you go to test it front-end. as well as the Role. If you are still having problems with Package Management after confirming these are installed. see Troubleshooting Package Management. you will need to flush permissions (Security -> Flush Permissions) before you will see an effect. make sure it automaticly puts them in the right user group. When using a websignup snippet.php/topic. check again and then go to the Forums to ask for help. Don't use a different tab or browser window. Go to System -> Package Management. Downloading packages through Package Management requires cURL or PHP Sockets. or by going back to the Users tab and adding them from there. as elements and resources may cache their permissions. you can simply use that one. still! Make sure you followed everything step by step and that you flushed permissions properly.6. You can do this by editing the user. See Also Bob's permissions guide: http://bobsguides. you use a different browser all together.html Forum topic discussing this tutorial: http://modxcms.modxcms. 7. MODx will let you know if you don't have either of these.com/forums/index.html Security: http://svn.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.com/revolution-permissions. As we assumed the Member role with an authority of 9999. You may download multiple packages at one time.Then click the Download Extras link. opening the folders to expose the individual packages. . Click Download to download whichever packages you'd like to download. Browse the available packages. and that the core/packages/ directory is writable by PHP. select the 'Scan Local' option. The package will now be visible in the Packages list. Then. you can manually copy the package into the core/packages directory.zip archive. From there. the modxcms. Now you can view your new package. by right-clicking and selecting Install Package from the pop-up menu. such as wayfinder-2. Use the Add New Package link. and click Install to choose to install it. . and you can install it as usual. Most issues can be resolved by making sure you have cURL installed. click on 'Add New Package' in the packages grid. By default. specifically with downloading and installing packages.1-beta1.1. to the left of the Download Extras link.transport.The package will be downloaded to the proper directories in your MODx installation. See Also Troubleshooting Package Management Troubleshooting Package Management This page is dedicated to problems with Package Management. or select packages on your local machine. Manual Installation If you prefer. The package must be a transport.com/extras repository is available as a remote provider. Providers You can select the location from which to download packages.zip. add a new location. Once you've done so.*/Evolution is not yet available in MODx Revolution 2. please read Troubleshooting Installation.com or in the forums. you're free to do so. And the new permissions scheme is vastly different than in 0.6.0. make sure you have the "Core Package has been manually unpacked" and "Files in-place" checkboxes unchecked. and then run setup. it's worth noting that there are no more "web users" or "manager users" . For Git users. Also. as well as all of your tags to the new Tag Syntax. simply upload the files over the existing ones.0-rc2 Upgrades after 2. and that it's writable.0. or on modxcms.php. With that in mind. please see Git Installation. After Setup Make sure to remove the setup/ directory via the last option after setup has completed. we don't recommend this. a few things will happen. Again. If you are upgrading using the Advanced distribution. Version-Specific Changes For changes relating to specific versions. so you may be able to find Revolution-compatible scripts via Package Management.0 This document assumes you are upgrading from a standard install.0. and that the core/. so that no one can run setup after you and possibly break your site. From there.9. For the advanced distribution. A migration tool will be provided sometime after the release of MODx Revolution 2. but you'll only need to do so for the core/ and setup/ directories. but if you're a *brave* soul. manager/ and connectors/ directories are writable. simply run the upgrade mode in the setup/ program.6/Evolution.0-rc-2 should run smoothly without issues. If you get errors during setup.0. feel free to backup and try it.only Users.inc. if you'd _still_ like to upgrade.Upgrading MODx Upgrading MODx Revolution 2. See Also Upgrading from MODx Evolution Automated upgrading from 0. do the same. One. . and your database tables will be upgraded. Component developers will hopefully already be converting their scripts by this point. Make sure that you don't overwrite core/config/config. please see the following pages: For Upgrades Coming From Prior to 2. Beginning Setup Simply follow the upgrade process.0. you'll probably notice most of your 3rd party scripts will be broken. we strongly recommend backing up your data first.9.0. You'll need to convert them to the Revolution core. However. selecting whichever upgrade you want to perform (normal or database). Uploading the Files For traditional distribution users. No More 5000-Document limit Although this has been mostly remedied in later versions of Evolution. we implemented "Source Order" parsing. This means tags are parsed in the order that they occur. if a web page is protected in the front end so that only logged-in users can see it. You can read more about it Security.?.* is now OK in a Snippet property. The placeholders will simply be blank. chances are you're not designing it right. there is still a performance hit in those versions. the . In Revolution. Tag Syntax Tags have changed their basic syntax. pages were parsed via eval and done as a whole . or after the Resource. [[mySnippet? &tag=`test[call]`]] is now 100% a-okay.Extras Changes from Evolution Some Extras in Evolution have been discontinued or are no longer in active development. Error Page vs Unauthorized Page This is a change from MODx Evolution. since the Snippet haven't executed yet. Security The access permissions system has been completely rewritten into a new ABAC-based system. Tags can now have tags within their properties. This document will attempt to address some of the major ones. has been fixed in Revolution.!. Archivist Quip GoogleSiteMap Gallery FormIt Wayfinder Batcher SimpleSearch 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.in Revolution. if you're creating a site that has over 10. Consider writing custom Snippets that pull from custom database tables instead for similar pages (such as inventories or e-commerce). Below is a list of Evolution Extras and their Revolution equivalents: Evolution Ditto Jot SiteMap MaxiGallery eForm Wayfinder DocManager AjaxSearch WebLogin Revolution getResources.000 Resources. a few things: Don't put Snippet calls that assign placeholders at the end of a Resource. caching-wise. That said. _Using =. Parsing Order In Evolution. You can view the Tag Syntax changes here. So what does that mean? Well. tagLister. This. getPage. However. All that needs to be changed is that they no longer need to call 'modPackageBuilder::buildLexicon' in their build scripts. you can only edit existing entries.transport. Note that some of these events are model-centric. You no longer need to 'buildLexicon' in your Extra's build scripts. In Revolution. you can do the following: Create a new Access Policy called "Load" and add a single Permission: Load. .php'. it's as if it doesn't exist — thus the "page not found" response." (credit to Bob's Guides) FURL Suffixes and Prefixes -> Content Types The settings friendly_url_prefix and friendly_url_suffix are no longer applicable. if Users don't have the "load" permission for a resource. Please see the documentation on each respective event for more information.0. Much faster lexicon loading time. These are usually: On*Save On*BeforeSave On*Remove On*BeforeRemove This means they will fire regardless of where they are executed.0 RC-2 that will only apply to developers.0 RC-2. All core translations will be committed there. If you are not: Writing translatable Extras for Revolution Writing plugins for Revolution then you do not have to read this document. Refactored the entire modLexiconEntry logic so that now DB records of modLexiconEntry are only for overridden entries. and a few new events have been introduced. This allows you to fire events even when 3rd Party Components modify those objects. you might ask. much easier to develop in.0-rc-2 Upgrading to Revolution 2. MODx will automatically parse that directory and browse it in Lexicon Management for you. In other words.0. much easier translation abilities. This also means that we will be packaging in core translations into SVN. Otherwise. Cuts down on the size of the core. Please view the in-progress documentation on these events on the System Events page. 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]. for one. If you would like them to be sent to the Unauthorized page instead. they are cached from the lexicon topic files (the .inc. There are some real benefits to the new approach: Much. this means that all packages using lexicons will need to be rebuilt for 2. What has been changed is: Dropped entirely the modLexiconTopic and modLexiconLanguage tables. Create a new Context Access ACL entry for the anonymous User Group with a Context of "web. they show up in green. Upgrading to Revolution 2. You can now successfully change any lexicon entry without harming your upgrade path. Changed the 'topic' field on modLexiconEntry to be a varchar of the topic name. Lexicon Changes First off. and when you edit them. since its file and array based rather than DB and Object based. This means they are executed from within the mod* classes. we didnt realize the limitations of the RC1 lexicon system.0-rc-2 There are a few changes that have occurred in 2.) Redid the entire Lexicon Management section to now be a grid that only allows overriding of Entries. and how it hampered Extras development and prevented us from having a stable multi-lingual distribution. but we promise that you'll find the change much. as Revolution handles those now through Content Types." a Role of "member" and an Access Policy of "Load. or view an exhaustive list via the code here.0. Much easier development.zip and massively decreases build and setup times.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. signifying they have been overridden.0. similar to Evolution. such as when a 3PC creates a user. and their lexicon directories must be under the namespace path with the directory name 'lexicon' (similar to this component).inc. "Why such a big change so late in the game?" Well. We apologize for the inconvience.php files. Plugin Changes Deprecated Plugin Events have been removed in RC-2. This means that the only Lexicon Entries stored in the database are overrides made by the user. gz tar xvf modx_revo_site. you can move the whole directory into the correct place.sql If you use mysqldump. Those of you familiar with MODx Evolution may be in the habit of zipping up the files. the same concept is true with files: package them well. but for now it runs on MySQL. On a UNIX style system. you put everything into boxes. On the new server.. grabbing a dump of the database. and then deploying them on the new server. it's good to put the zipped file into its own directory before you extract it.gz /path/to/modx_doc_root/ Forget me Not A good mnemonic for the "-czf" option is Create Zip File. Besides. Again. but in Revolution.tar.htaccess file. it's best to package the files into "boxes" – when you move out of your apartment. you may have discovered that maybe something broke in the process. you can create a compressed file using the tar command: tar -czf /path/to/backups/modx_revo_site.php).. Once you arrive on the other end. The idea here is that if it explodes and you have to scrape the files off the walls. MODx will be able to run on other databases. you won't see anything in the terminal window. it's easier to clean up a mess if it's contained in its own directory. Packaging up your Files Any time you pack up a site and move. It's more or less the same process. but the steps here also apply if you move your site to a new folder on your current web server). mysqldump -u username -p your_revo_db > /path/to/backups/my_revo_db. It's better to rename or move the containing directory instead. (Normally.tar Once you've extracted the files. you can unpackage a .tar. but there are couple extra tidbits to watch out for. transferring hundreds of files via FTP can take a long time because with many servers. So here's the official documentation of how to move your site to a new location. If you simply drag and drop files from one server to another using a GUI interface. 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. maybe the manager only comes up as a white page. 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. You can dump your MySQL database using a GUI tool such as phpMyAdmin. in other words copying over a hundred 1 megabyte files takes a lot longer than copying over a single 100 megabyte file.. or you can run the command-line mysqldump utility.sql . each file must undergo some sort of authentication.tar.gz file using the following commands from a command line: gunzip modx_revo_site. On a UNIX style system. chances are good that the GUI will omit hidden files such as the vital . so moving it to a new server involves the typical porting over of both the database and all the site's files.See Also Moving Your Site to a New Server MODx Revolution is a database-driven web application. Dumping your Database In the future.inc. be careful about moving files in bulk: you might inadvertently forget to copy those hidden files. Remember that mysqldump will prompt you for the password after you execute this command: when you type it (or paste it). this is to a new server.. but for most of us.php. You can edit it using a GUI editor (like SQL-Yog or phpMyAdmin). or any other application that allows you to execute queries on your database: SELECT `path` FROM `your_revo_db`.`workspaces` SET path='/path/to/modx_doc_root/core/' WHERE id='1'.`workspaces`. you frequently wind up changing domain names. On the new server you need to update this record. 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/'. Change "your_revo_db" to your database name. $modx_processors_path= '/path/to/modx_doc_root/core/model/modx/processors/'. you may need to loosen up the permissions. You have to update paths to 6 different resources. $modx_assets_path= '/path/to/modx_doc_root/assets/'. you need to update the main configuration file: core/config/config. Permissions Before you can edit your config file. Update your Database Sometimes developers structure their development and production servers to use the exact same path information. After you've edited it. Type the following query into phpMyAdmin.php That's because the old path is still cached. and the path to your data): UPDATE `your_revo_db`. $modx_manager_path= '/path/to/modx_doc_root/manager/'. Make sure you update any references to the old domain to the new one. you need to customize the query depending on your database. Don't Forget the Database MODx stores some path data in its database! When you move servers. the file path information will change when we move our MODx web site to a new server. so you're usually better off using a command-line tool if possible. be sure you restore the read-only permissions on the file. a MySQL command line. Update . or you can execute the following command (again. you may have to update the workspaces table.You can also use phpMyAdmin.htaccess When you change servers. $modx_connectors_path= '/path/to/modx_doc_root/connectors/'. $modx_base_path= '/path/to/modx_doc_root/'. and add an appropriate prefix to the "workspaces" table if necessary. prefix.com'. Updating your Config File Once you've deployed files to the new server. Once you've gotten the files and . 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. otherwise the manager page may show a white page.inc. but remember that web-based tools like this are subject to the same memory limits as PHP. MODx Revolution caches a lot of information in the database. /* HOST (used for command-line PHP stuff) */ $http_host='yoursite. Take a look inside your Revolution database and see for yourself that path information is stored there. Log into the manager. 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.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. 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. then: Site --> Clear Cache Finally. 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 . termination is a trivial enough task. MODx also recommends externalizing any HTML in PHP code into Chunks. not tabs.org) MODx recommends following the HTML5 specs: http://whatwg. Compression MODx suggests packaging both compressed and uncompressed JS/CSS. Extension or core code. and the DOCTYPE declaration has been simplified significantly. MODx recommends using the W3C Validator. to ensure that the markup is well formed. the forward slash should have exactly one space preceding it <br /> vs. an area previously not adequately covered by HTML. Thanks! General Practices Indentation and Line Breaks All indentation must be done with 4 spaces. Additionally. It attempts to solve issues found in previous iterations of HTML and addresses the needs of Web Applications. All HTML pages should be verified against the W3C validator. The W3C specifies that a single space should precede the self-closing slash (source).This page describes the MODx coding standards for any MODx Component. the compact but incorrect <br/>. Trailing Spaces MODx recommends removing any trailing spaces in a line in code. Meaningless attributes have been dropped. HTML HTML5 is a new version of HTML and XHTML. This in and of itself is not directly indicative of good code. It is no substitute for manual code review. Make sure to change your editor settings to reflect this. Inline HTML in Snippets MODx requires that no html be echo'ed or inline in a Snippet. which allows for either HTML or XHTML style syntax. Note: In TextMate.org/specs/web-apps/current-work/ Validation All HTML must be HTML5-validated. This allows for easier debugging. MODx does not advocate PHP compression. Self-closing Elements Though we are using HTML5. Therefore. "HTML5 Doctype" <!DOCTYPE html> . (from html5. Terseness Doctype A nice aspect of HTML5 is that it streamlines the amount of code that is required. Line breaks must be in UNIX format. The HTML5 draft specification defines a single language that can be written in HTML and XML. we prefer the strictness of XHTML. formerly a requirement to meet XML strictness in XHTML. but allowing users the option to toggle between compressed and uncompressed JS/CSS. there is no need to use CDATA to escape inline JavaScript. This page was heavily borrowed from Fellowship One's Design Standards. but it helps to weed out problems that are able to be tested via automation. MODx recommends using the compressed JS/CSS in production environments. For tags that are self-closing. Control + Shift + V will check the validity of the currently open HTML document. all tags must be properly closed. unless that spacing is for design purposes. For tags that can wrap nodes such as text or other elements. but guidelines for easier development and collaboration between developers. These are not de-facto rules. to ensure correct syntax and to check for possible accessibility issues with text and background colors.0 Transitional Doctype" <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1. with one tab of indentation. we prefer that any attribute values also be lowercase. we require that all CSS be written in a consistent manner. It is no substitute for manual code review. proper title capitalization should be followed. all CSS selectors must be listed on their own line. CSS Formatting To ease potential headaches for maintenance." attributes. Note: In TextMate. and must use double-quotes (source).flush left. Note: An exception to this rule is style="display:none" for revealing hidden elements via JavaScript. This way. "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. That single file will incorporate other files as necessary with the @import syntax. All of our CSS will be stored in external files. . all attributes must have a value. but it helps to weed out problems that are able to be tested via automation. we know that all text on a single line is part of the same selector. charset=utf-8" /> "For humans" <a href="http://example. Additionally.. when the purpose of the text therein is only to be interpreted by machines.css file called per page. For one. The closing brace must be on the same level of indentation as the selector that began it . Likewise.com/" title="Description Goes Here">Example.com</a> Quotes In keeping with the strictness of XHTML code conventions. The following are examples of proper and improper usage of quotes and attribute/value pairs. such as: "For machines" <meta http-equiv="content-type" content="text/html. For instances in which the data needs to be human readable. As a general rule of thumb.0 Transitional//EN" "http://w3.dtd"> Tags and Attributes All tags and attributes must be written in lowercase. all property/value pairs must be on their own line. Control + Shift + V will check the validity of the currently open CSS document. CSS Validation All cascading stylesheets should be verified against the W3C validator. This in and of itself is not directly indicative of good code.org/TR/xhtml1/DTD/xhtml1-transitional. according to the W3C. if there is a comma in CSS. and therefore highly discourage the use of inline style=". but inextricably ties the presentation to the data it represents. This not only makes maintenance a nightmare. with one master.. it should immediately be followed by a line break."XHTML 1. line-height: 1. Additionally. #selector_2 span. all major browsers (including IE7 and IE8) now support text resizing of pixel units and/or full-page zooming. but instead is based on a multiplier of the font-size. } Pixels vs. color: #000.813em.css" /> <![endif]--> . color: #000. pixels sizing is preferred. } "Also incorrect" #selector { background: #fff. #selector_3 span { background: #fff. color: #000. delaying time to deployment. #selector_2 span. when all other browsers appear to be working correctly. However. to accommodate for Internet Explorer 6 not resizing pixel based text. } Internet Explorer Bugs Inevitably. #selector_3 span { background: #fff. "Correct" /* 13 * 1."Correct" #selector_1 span.css" /> <![endif]--> <!--[if IE 8]> <link type="text/css" rel="stylesheet" href="/assets/styleshseets/ie8. */ #selector { font-size: 13px. but only if the browser default text size is 16px. unit-less line-height is preferred because it does not inherit a percentage value of its parent element. } "Incorrect" /* Equivalent to 13px font-size and 20px line-height. because it offers absolute control over text. While we encourage troubleshooting and building code that will work in all browsers without special modifications.5. Since IE6 is largely considered deprecated.5 ~ Rounds to 20px. "Fixing IE" <!--[if IE 7]> <link type="text/css" rel="stylesheet" href="/assets/styleshseets/ie7. which are ignored by other browsers.5 = 19.25em. line-height: 1. any and all versions of Internet Explorer will introduce a few nonsensical bugs. Ems We use the px unit of measurement to define font size. */ #selector { font-size: 0. sometimes it is necessary to use conditional if IE comments to serve up specific fixes. } "Incorrect" #selector_1 span. We realize that using the em unit for font sizing used to be popular. png) repeat-x fixed left bottom. Left. } "Incorrect . in a clock-wise manner: Top. if left is undefined. border should be a single line declaration. If bottom is undefined.png). Obviously. } "Incorrect . written in lower-case. such as the case with margin and padding. assuming that the values of the border are the same on all sides of the element. } .Shorthand In general. If only the top value is defined. No upper-case or RGB. "Shorthand . padding: 0 0 10px 0.com/journal/css-redundancy http://dustindiaz. CSS shorthand is preferred because of its terseness. background-position: left bottom. } Hex Colors We prefer hex values for all colors. denoting the order in which the sides of an element are defined. For example. Background "Correct . and color.. For more on reducing stylesheet code redundancy. please! Additionally.longhand unnecessary" #selector { background-color: #fff. a light shade of grayish beige: #f9f9f0.left attribute unnecessary" #selector { margin: 0 0 10px 0.shorthand" #selector { background: #fff url(. background-attachment: fixed. and using CSS shorthand in general: http://qrayg. This means that colors such as full blue. all colors should be written as tersely as possible. it inherits its value from right. all sides inherit from that one declaration. background-repeat: repeat-x. should be simply written as #00f. The order in which values are declared are: width.method 1" #selector { border: 1px solid #000. Bottom. Developers should be aware of the TRBL acronym.com/journal/news/css-background-shorthand http://sonspring./images/file. Right. Likewise. padding: 0 0 10px. all six characters should be used. for colors that require more precision. background-image: url(. } Border In general./images/file. which can be written lengthily as #0000FF.com/css-shorthand Margin & Padding "Correct" #selector { margin: 0 0 10px.. it inherits its value from top. and the ability to later go back and add in values that are already present. style. border-bottom-style: dotted. border-bottom-color: #666. Note: Times New Roman is encapsulated in quotes. and it is up to the discretion of the developer to decide which to use. border-top-style: solid. "Longhand" #selector { border-top-color: #fff. border-left: 4px double #ccc. } By contrast. border-width: 1px 2px 3px 4px. border-style: solid dashed dotted double. then there are two possible ways of using shorthand. border-right-width: 2px. border-bottom: 3px dotted #666. } "Shorthand . The shorthand property puts all the aspects of the font into a single declaration. 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. border-top-width: 1px. border-left-width: 4px. except in instances where only one particular value needs to be overridden. Note that method 2 follows the TRBL pattern.If the values of each side differ. 'Times New Roman'. } Font Not to be confused with the inadvisable <font> tag. border-left-color: #ccc. but when written in longhand it has its own unique property. border-right-style: dashed. whereas the longhand splits it out over several lines. border-right-color: #999. allowing the rest to flow through. "Shorthand . border-right: 2px dashed #999.method 3" #selector { border-top: 1px solid #fff. sans-serif. "Shorthand" #selector { font: italic small-caps bold 15px/1. } . While line-height can be defined within the scope of the font declaration. border-left-style: double. because the font name itself contains spaces.method 2" #selector { border-color: #fff #999 #666 #ccc. the same style declaration is extremely verbose using longhand. the CSS font property can be written in a few different ways. border-bottom-width: 3px. This should be avoided.5 Cambria. use one of the following techniques. JavaScript will perform type coercion when evaluating conditional statements. font: 11px Verdana. } Javascript Type Coercion Unlike strongly typed languages such as Java or C#. // Correct behavior. This way. . sans-serif. } if (test_1 === true) { // Code here won't run. } #selector. } As you can see in the example above. it also has a strict negation operator !==. serif. font-weight: bold. This is typically disadvantageous. // But it should. If you are unsure if certain elements will be present in an HTML page. "Longhand override" #selector { border: 1px solid #ccc. line-height: 1."Longhand" #selector { font-style: italic. Consider the following examples of potential pitfalls when it comes to evaluating comparisons. var test_2 = 0. } if (test_2 !== false) { // Code here will run. In similar fashion. anytime we see a longhand declaration used. To ensure a strict level of comparison. as might be seen in a strongly typed or compiled language. there is an even more abbreviated way.5. There is never a good reason to use the lesser form of comparison operators. } if (test_2 != false) { // Code here won't run. JavaScript (like PHP) has a triple-equals operator ===. // Correct behavior. we know that we are specifically overriding only a very precise part of an overall style. border-bottom-width: 2px. thereby leaving other aspects unaffected. font-family: Cambria. This sometimes creates awkward scenarios in which numerical values are seen as false or the existence of a string is mistaken for true. // But it shouldn't. } Longhand When overriding only parts of a style.modifier { border-bottom-color: #333. 'Times New Roman'. font-variant: small-caps. longhand declaration is preferred. the stricter comparison operators should always be used. font-family: Georgia. sans-serif. if (test_1 == true) { // Code here will run. Therefore. simply using == and != is insufficient because it makes for potentially unpredictable results. that leaves no room for ambiguity. var test_1 = 'true'. by sticking to shorthand for initial style declarations. To simply for the existence of elements in the DOM. font-size: 15px. The reasoning for this is the same philosophy that is behind not using inline style=". Event Delegation . i<j. Additionally. or onclick directly in markup. braces should always appear on the same line as their preceding argument. } Variables. but no spaces immediately inside the right and left sides of parenthesis. onfocus. i++ ) { // Do something. Such that. j=arr. Likewise.getElementById('id_name')) { return. all id and class declarations in CSS shall be written in the same manner. } // If code gets here.length) { return. onsubmit. Since JavaScript exists to manipulate markup..jpg\" alt=\"Text\" />".function first_func() { if (!document. So doing inextricably ties the behavior of a web page to its data. we advocate readability within reason.length. one or more exist. Consider the following examples of a JavaScript for-loop. Event Listeners Rather than using attributes such as onload. there will be one space after each comma and colon (and semi-colon where applicable). Neither dashes nor camelCase shall be used. we will instead attach event listeners to these elements via unobtrusive techniques. and makes maintenance more difficult. "Correct" var my_html = '<img class="photo" src="/path/file. "Correct" for (var i=0. with underscores to separate words if need be. } "Incorrect" for ( var i = 0. ID & Class All JavaScript variables shall be written in completely lowercase letters. except for words that contain dashes when written in plain English. i < j. } function second_func() { if (!document. "Incorrect" var my_html = "<img class=\"photo\" src=\"/path/file.. Quotes The preferred method of delineating strings is to use single quotes for everything. element exists. } // If code gets here. j = arr.jpg" alt="Text" />'. i++) { // Do something. using single quoted strings will better facilitate handling HTML fragments.length. the use of whitespace should follow longstanding English reading conventions.getElementsByTagName('div'). and because HTML is generally written with double quotes in W3C specifications. and keep code more readable. } White-space In general." declarations. In short... email = 'john. "Object dot notation" /* Could also be written: var john_doe = new Object(). Similarly. which is a platform and language independent way of transmitting data.doe@example. var second_variable = 'value 2'. However. within the scope of a function will not add to global scope pollution. } function second_func() { // Do something. In such cases you should use event delegation instead. and attaching event listeners to each one might negatively impact performance. } })().preferred" var john_doe = { first_name: 'John' .age = 30. Note also that the commas are before the variable or method declaration. variables defined correctly using the var syntax. occasionally there may be multiple elements which match the criteria for which you are checking. an array could be described as a list of data that all share common characteristics.married = true. john_doe. john_doe. However.job = 'Everyman Representative'. john_doe. it is typically acceptable to assign the event listener directly to the element(s) which will trigger some resulting action. This is because age is truely numerical. it is highly recommended that self-executing anonymous function be used as a closure. you may need to access variables via two or more functions.com' .email: '[email protected]: 30 }. The following code snippets show examples of objects and arrays. john_doe.com'. In such cases. Note that values such as John Doe's age and marital status do not have quotation marks around them.When assigning unobtrusive event listeners. and true is a Boolean value.JavaScript Object Notation.first_name = 'John'. "Object literal .last_name = 'Doe'.last_name: 'Doe' . multiple functions can be grouped together inside a closure. "Closure" (function() { var first_variable = 'value 1'. Objects (and arrays) are an important part of JSON . from time to time. Objects & Arrays Objects can be thought of as tiered variables that contain multiple attributes. function first_func() { // Do something. john_doe.married: true .job: 'Everyman Respresentative' . */ var john_doe = {}. For the most part. This prevents errors with trailing commas in IE and other browsers. . used as an alternative to XML. and the different ways in which they can be defined. Closures & Scope To maintain proper scope for variables. john_doe. include_once. */ var doe_family = []. Do not do any real logic in object constructors.'Jenny' . doe_family[0] = 'John'. modChunk. Example: if ($test) { } while ($test == $other) { } array_push($one. Do put parenthesis next to function names. and require_once. Put a space between. unless stated otherwise for special conditions. doe_family[1] = 'James'. "Array bracket notation" /* Could also be written: var doe_family = new Array(). doe_family[4] = 'Jared'. will be prefixed with the "mod" prefix: ie. true and false should always be lowercase. Do not use parenthesis when using include. They start one space after the end parenthesis. doe_family[3] = 'Jenny'. Never use extract(). All private methods and variables must be prefixed with the underscore _ character.$two). Avoid embedded assignments (ex: $d = ($a = $b + $c) is bad). etc. Avoid using global variables if at all possible. Document EVERYTHING. return $test. Create class methods to do so. PHP General Beginning brackets do NOT linebreak. All method names will be camelCase and will start with a lowercase letter. null. doe_family[2] = 'Jane'. as according to traditional Unix policy.'Jared' .preferred" var doe_family = [ 'John' . require. doe_family[5] = 'Jerome'. . Do not use parenthesis in return statements when it's not necessary.'James' . Parenthesis Do not put parenthesis next to keywords. modTemplate.'Jane' .'Jerome' ]. Classes All ''core'' classnames."Array literal . eg: 'finBank'. Example: $_lang['chunk_create_text'] = 'Test'. eg: 'finStatement'. Prefixing Lexicon strings for Components need to be prefixed: $_lang['mycomponent. rest are camelCase.welcome_message'] = 'Welcome!'. Always prefix class names. This prevents errors with magic_quotes. Array index names are always encapsulated with single quotes. Constants Constants must be in all UPPERCASE letters. and table and column names must be enclosed with backticks.php format. Separate words with the underscore. private function _privateFunc() { } public function publicFunc() { } } Variables Note these are not function arguments. 'finTransaction'. etc. Use only if absolutely necessary. private $_privateVar. not the dash as their separator. Always prefix Chunk names. . Use all lowercase letters. 'finDeposit' SQL All inline SQL must be capitalized. array &$anotherTest = array()) { $this->_privateVar = $testVar. Function Arguments and Class Variables The first letter is lowercase. Array index names are always lowercase. Example: class modFactor { public function testFunc($testVar. Spaces are represented by an underscore.class. } } Arrays Array index names use the underscore _.class modFactor { public $publicVar. File Structure Always name PHP class files in name. $local_variable =& $anotherTest. They are used mainly to prevent direct accessing of processors. 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. a common programming paradigm where the data's Model is only accessed through a Controller. Chunks and Resources. scalable system with flexible content management built in."Correct" UPDATE `mydatabase`. What is xPDO? . they are Templates. What is MVC? MVC is "Model-View-Controller". which connects to a View that can easily be changed without having to change the Model. designers and users who want a powerful. What is MVC²? MVC² is a MODx terminology that is "Model-View-Controller/Connector". 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. "Incorrect" update mydatabase. and limit user access to those processors. It basically adds a new way of accessing the model from the view Connectors. 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.`mytable` SET `name` = "Johnny" WHERE `id` = 123. built for developers.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. but are used differently based on what context we're talking about. and currently loaded via Smarty. they are Request Handlers (via the modRequest class) and Snippets and Plugins. What is an ORM? As defined by Wikipedia: An object-relational database (ORD). They are never accessed directly. They can be files. They simply execute PHP code whenever they are called. and much more. It's not a PHP Application Framework like CodeIgnitor or Symfony. it supports extension of the data model with custom data-types and methods. . as well as a flexible domain model that allows you to isolate domain logic from database-specific logic. in programming. they are Templates. and more.1+. In the front-end. The View Views in MODx Revolution are called 'Templates'. 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. This also includes what Revolution calls "processors". In addition. They resemble basic CRUD (Create-Read-Update-Delete) processing tasks. the View is handled by templates as well. In the Manager In the manager-side of MODx Revolution. and return whatever output they would like to push to the page. which are scripts that handle Domain Logic for MODx Revolution. it enables you to build Content Management Applications with ease and extensibility. In the front-end. PDO. but with an object-oriented database model: objects. It's a light-weight ORB (object-relational bridge) library that works on PHP 4 and 5. They represent View widgets. It's loosely based on the MVC. inherit from base classes. weblinks. REST requests. which is the core classes that manipulate data records. Basically. and are used to handle form processing. Templates Templates are what they sound like. is a database management system (DBMS) similar to a relational database. They represent a single page or resource by which the client accesses content from the server. A Brief Overview of Revolution Revolution at its core is a Content Management Framework. classes and inheritance are directly supported in database schemas and in the query language.xPDO is our name for open eXtensions to PDO. The Model The M stands for Model. although these are file-based and located in manager/templates. prefixed with 'mod' in Revolution. and takes advantage of the newly adopted standard for database persistence in PHP 5. Chunks and Resources. or object-relational database management system (ORDBMS). Templates. or model-view-controller architectural pattern. symlinks or just plain-old HTML pages wrapped by Templates. tables in SQL databases become classes that can contain table-specific methods. handle all the Domain logic for MODx Revolution. in a sense. AJAX requests. or Resources. It implements the very simple. or not. Snippets Snippets are simply PHP code that can be placed anywhere in a page. Revolution bases its internal structure on what we call a MVC² design system. They allow you to create templates that will encapsulate more page-specific data. nor does it purport to be one. These core classes. With that said. because of their modularity and ease of insertion. it's much more than a typical CMS like Wordpress or others. The Controller Controllers in MODx Revolution come in two forms. depending on your needs. Resources Resources is the basic representation of a single "webpage" in MODx Revolution. They can be placed in Chunks. but effective Active Record pattern for data access. php with a GET 'action' parameter of "get". See Also xPDO.Plugins Plugins are also PHP code. The manager system in MODx Revolution uses them extensively. built around the database ORM xPDO. you would put its non-webroot files in "core/components/test/". secure JSON requests (and eventually REST-based requests) straight from your MODx manager. we recommend putting them in: core/components/myname So. but we do have some recommendations. Every Connector request is also secured down by Context permissions loaded on every request. They are the cornerstone of MODx Development and . etc). such as word censoring. If the user does not have access (via the Access Policy assigned to the request's context). this is JSON by default in Revolution) format. or a single Snippet. "assets/components/test". separate cache handling. a connector request that loads /modx/connectors/resource/index. the connector will refuse to provide data. . They allow users to write generic code that affects basic page functionality. They are usually transported and installed via Transport Packages. or many more places. For files that don't need to be in the webroot (config files. or just a collection of files. such as css. before the request is handled. but are targeted at specific System Events that occur throughout the request processing. will (assuming the request's client has access) grab the Resource with the ID specified and return it in JSON (or whatever is configured. Plugins and Chunks. 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. They can be a collection of Snippets. if you had a component named 'test'. and more. they provide secure locations for AJAX requests to process data on certain objects. Connectors allow for dynamic. The 2nd C: The Connectors Connectors are a new idea to MODx Revolution. automatic link creation. js and other files. Snippets Snippets are simply php scripts that can be executed on any page or other Element. for 'test'. For example.php. This standardization of paths makes it easier for other developers using your components to find your files easily. after it is. context redirection. we recommend: assets/components/myname Ergo. core/components and assets/components MODx doesn't necessarily limit where you can put your custom 3rd party component files. 3rd-Party Components (3PCs) 3rd-Party Components (3PCs) are collections of any sort of MODx Objects. they are access points for processors. and a GET parameter of 'id'. For files that need to be web-accessible. They can occur before the webpage is rendered. etc).class. since MODx users can move the core outside the webroot. Property Sets are user-defined groupings of Properties that can be used to quickly centralize custom tag syntax calls.'config/'. Why the separation? Well.dynamic customization. which can be parsed by each individual Element. Using MODx Externally Using the MODx object (and all of its respective classes) is quite simple. policies.MODX_CONFIG_KEY. 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. in an average MODx page request. Properties and Property Sets Properties are simply placeholders on Elements (Snippets/Plugins/Chunks/TVs/Templates). are custom pages in the manager built by 3rd Party developers to allow backend management of Components. This will initialize the MODx object into the 'web' Context. For example. Plugins Plugins are similar to snippets in that they are snippets of code that have access to the MODx API .8 Netbeans Subversion and JIRA plugins .php'. Now. JS and images.php'. 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. They allow customization and argument passing for each Element.however the big difference is that plugins are associated to specific system events.php'.core. See Also Setting up a Development Environment Recommended Development Tools and Environments for MODx Revolution In developing MODx Revolution. you'll just need to change 'web' to whatever Context you want to load. $modx = new modX(). Extras Directories Extras are most commonly stored in 2 directories when they are installed: core/components/ . All you need is this code: require_once '/absolute/path/to/modx/config. there are many events that are available in the MODx Manager. require_once MODX_CORE_PATH.This is the location for all the PHP and non-web-accessible files for the Extra. require_once MODX_CORE_PATH.'model/modx/modx. You can read more about Snippets here. Plugins aren't just limited to front-end processing though.This is the location for the web-accessible files for the Extra. Custom Manager Pages (CMPs) Custom Manager Pages. the MODx Team has found the following environments invaluable: Netbeans Netbeans 6. More on Property Sets can be found here.'. assets/components/ . such as CSS.inc. $modx->initialize('web'). separating out the non-accessible files into core/components allows MODx developers to add an extra level of security to their Extras. or CMPs. if you want to access it under a different Context (and thereby changing its access permissions. org/update_1. Additional suggestions: Install your IDE in a location which is static and remains consistent for long periods of time. and Mac OS) which do not require erasure/formatting of the entire drive to install. Linux. 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 -.0. SVN is discouraged from continued usage in regards to future MODx related development. or Spket.6.Eclipse 3.+ (recommend latest 3. 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.2.2.x) PHPEclipse 1. Subclipse. 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.IDE Versions . 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.Install everything offered Other IDEs For Mac: TextMate .eclipse. It is not advised to install Eclipse and point to a workspace with existing projects.simply install the Subclipse option PHPEclipse .IDE Coda .net/update/nightly) Spket IDE 1.com/modxcms there is an additional and optional eclipse project eGit you may also wish to install.6.1 (http://download. 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 .6 Eclipse 3.org/webtools/updates/) Subclipse 1.5 (http://subclipse.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.6. PHPEclipse.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.3 (http://update. Eclipse versions before 3. You may also want to isolate your workspace to a dedicated partition/drive.phpeclipse. opengeek (Jason Coward) strongly suggests learning and using git from the command line to maximize your flexibility and potential.SVN client svnX .1) Web Standard Tools Project (WST) 2.18 (http://spket.SVN client . especially in operating systems (such as Soalris.install everything offered Spket .tigris.5.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. once they're up to be cached. They have access to the $modx object. and the following tools/libraries in the development of MODx Revolution: PHPUnit .IDE TortoiseSVN . and we'll be adding a test harness to MODx soon SimpleTest . as in "PHP". Simple Example Here's the perfunctory super-basic example of what a Snippet might look like: . script-driven content. most Snippets are cached.0. Snippets are then parsed by the MODx Parser. They are the main development vehicle for most developers. unit testing.x unit testing framework for xPDO. sni-"P(h)P"-et. dynamic function in the cache. What is a Snippet? According to one definition.SVN client Kate .For PC: UltraEdit . and we'll be adding a test harness to MODx soon PHPDocumentor .IDE for Linux / KDE Development Server Environments We also MacPorts. e. 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.1+ unit testing framework for xPDO.IDE E . various distribution builds.this drives the PHP 5. and we'll be developing tutorials and other extended documentation for inclusion in the PHPDocs in DocBook XML format Phing . meaning they're stored as a temporary. and using the structures in MODx to create dynamic.all of the classes in MODx Revolution are documented in PHPDoc format.this drives the PHP 4/5..g. XAMPP and MAMP. If they're flagged as uncached.will be used to allow automation of nightly builds. How Do They Work? First off.. Some people have a hard time distinguishing this from a "chunk". Then. and many other development tasks Basic Development This section is geared at familiarizing developers with basic MODx development principles. Don't try to mix PHP and HTML in a Snippet. then they are not parsed until the parser has done all of the other cached content. so a helpful mnemonic might lie in the p's. a "snippet" is "a short reusable piece of computer source code". 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. Parameter Escaping . Note how we returned the code rather than 'echo'ed the content out.. don't forget the '&' in front of the would be variable names. SQL Abstraction . Cleaner. That's when we use the powerful $modx->newQuery() method: . and sort them by menuindex. 2. 'grenades' => 42. or Chunks. And let's make it so they aren't hidden from menus or deleted. take note that those are backticks. shorter Code .What could be done in 40+ lines in mysql_* calls can now be done in 10 or less. $input. MODx uses an Object Relational Model (ORM) called xPDO for database connectivity. Why an ORM? You might be asking. which is an object representation of the Resource.This means that you can write code that works in a variety of different database types. not single quotes! Database Interaction in Snippets Accessing the database layer in MODx is quite simple. Secondly. ?> If you named this "helloWorld". All without having to rewrite a single line of code. sqllite. Let's look at a few examples: Example DB Code Let's get a chunk named 'LineItem'.I. and change the placeholders in it (done with [[+placeholderName]] syntax) to some custom values: $chunk = $modx->getObject('modChunk'. if your Snippet looks something like this: <?php return 'My input was: ' . getting the first 10 Resources with a parent of 23.always return the output. are published. 'Input' not 'input'). )).e. xPDO uses PHP's PDO to escape all variables passed in to the SQL call to prevent any malicious calls. such as MySQL. World!'. if (!$chunk) return 'No line item chunk!'. as MODx expands to those databases. and more. you could call this snippet by using [[helloWorld]] in your documents. and return it processed with the placeholders set. There are more reasons. Passing Values Into a Snippet Snippets can take input values using a modifed CGI web-form type notation. postegresql. say. )). The $chunk variable there is actually an xPDOObject. return $chunk->process(array( 'name' => 'G. What about more complex queries? Like. Never use echo in a Snippet . That code would get a chunk with the name of 'LineItem'. templates. but that's for brevity.<?php return 'Hello.. ?> 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. 3. a few reasons: 1. Joe'. For example. 24 or 25. why use an ORM instead of just straight SQL? Well.No more having to worry about SQL injection. And last but most certainly not least.array( 'name' => 'LineItem'. $c->where(array( 'parent:IN' => array(23. ?> You'll find that MODx will append PHP tags to beginning and end of the snippet. creating an invalid syntax.g. $resources = $modx->getCollection('modResource'.separate the PHP into a Snippet. Then we sorted and limited them.and then used our where() function to add some restrictions. on the bash command line. Don't try it!&nbsp.just create an 'include' snippet. load its output into a placeholder with the modx API placeholder functions or chunk processing. $c->sortby('menuindex'.$c = $modx->newQuery('modResource'). It's bad architecture and it won't work!!</p>".returns a collection. e. } else { $o = 'File not found at: '. Don't try to mix PHP and HTML in a Snippet. You can use the include snippet on a page like such: [[!include? &file=`/absolute/path/to/my/snippet.php to check the script for syntax errors). )). } return $o. This is pretty easy to do . Note how we first create an xPDOQuery object ($c) using $modx->newQuery().$file. of xPDOObjects.unlike getObject . the following code won't work: <p>This is a horrible mixture of HTML and PHP</p> <?php return "<p>and PHP!&nbsp.g. you may also get some useful error messages to help you with debugging. Further Database Reading For further reading on xPDO. And finally. 'published' => true.php`]] And run your Snippets externally while you develop them! Then you can test them to make sure they work (e. or array. 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.24. use a Chunk . 'hidemenu' => false. Depending on your environment. or Resources . and include the Snippet's placeholders in the Chunk: .'ASC'). but make its content be this: if (file_exists($file)) { $o = include $file. We passed in the name of the class we wanted to build the query from . Snippets execute PHP code.$c). Copy and paste the code into MODx only when you're sure it's working.here 'modResource'.25). $c->limit(10). They should always begin with a <?php and end with a ?> You cannot mix PHP and HTML in a Snippet! For example.: <?php <?php //something here ?> ?> If you need to do something like this. We could then iterate over those using foreach and do whatever we want with them. 'deleted' => false. we called getCollection which . you can use the command php -l my_script. Go ahead and create a snippet called 'ResourceLister'. 'name' => 'Harry'. and then output them as LI tags with the pagetitle and a link to click them. say you want to iterate across the published.$output = $modx->getChunk('myChunk'.runSnippet modX.array( 'placeholderOne' => 'test'.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. Use Default Properties Consider adding your properties for your snippet into the Properties grid. so you have to backup code yourself. See Also modX. This tutorial shows you how to do that in a Snippet. )). non-deleted Resources that are children of the Resource with ID 390. but template out the HTML into Chunks.setPlaceholder modX. return $output. 'scar' => 'Lightning'. 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. 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. and put this inside: . so that the user can add custom Property Sets to override them. sorted by menuindex. Don't Work on Live Snippets If you're writing new versions of Snippets. 'ResourceItem').'" >'.$c).$modx->makeUrl($resource->get('id')). we make an LI tag. /* get all the children of ID 390 */ $children = $modx->getChildIds(390). or change it if they want to. foreach ($resources as $resource) { $output . called 'tpl'. change the foreach loop in the Snippet to this: foreach ($resources as $resource) { $resourceArray = $resource->toArray(). We want more flexibility. and returns us some content. /* get the resources as xPDOObjects */ $resources = $modx->getCollection('modResource'. let's create a chunk that we'll use for each item in the result set. Call it "ResourceItem". Templating the Snippet First off. It doesn't let the user control the markup. build the query */ $c = $modx->newQuery('modResource'). getOption defaults the value to ResourceItem (the Chunk we just made). If 'tpl' doesn't exist. } return $output. since $scriptProperties just holds all the properties in the Snippet call. $output = ''.$scriptProperties. Now the user can call the snippet this way to override the chunk for each Resource with this call: [[!ResourceLister? &tpl=`MyOwnChunk`]] . We have available any field in the Resource. } The code first turns the modResource object into an array of field=name pairs (ie.= $modx->getChunk($tpl. This gets us the &tpl= property from the Snippet call. but puts the HTML inline./* first. )).$resource->get('pagetitle'). and make this its content: <li><a href="[[~[[+id]]]]">[[+pagetitle]]</a></li> Basically. The [[~ tells MODx to make a link from the ID passed in the [[+id]] property. Then. replaces the properties. $resourceArray['pagetitle'] is the pagetitle) via the toArray() method.'</a></li>'. to the top of our snippet code: $tpl = $modx->getOption('tpl'.= '<li><a href="'. 'deleted' => false. )). And then.$resourceArray). we use $modx->getChunk() to pass our tpl Chunk and the resource array into it as properties. This does what we want. We don't want that. MODx parses the chunk. $output . } /* sort by menuindex ascending */ $c->sortby('menuindex'. Now let's add a default property to the snippet. and here we're just using the ID and pagetitle fields.'ASC'). if (count($children) > 0) { $c->where(array( 'id:IN' => $children. and put some placeholders were our content was. /* we only want published and undeleted resources */ $c->where(array( 'published' => true. sortBy. $resourceArray['rowCls'] = $rowCls.Meaning they can template their results however they want . with an &id=`123` property . but not have to make their own custom chunk? Simple. we just add a default property 'rowCls' to our snippet code at the top. We do this because we've already gotten the value of rowCls earlier in the snippet (with the getOption call).'resource-item'). lastRowCls. or table rows. and we know that it's not going to vary per row. But we want it to default to 390. or hide resources that are folders.to say Resource 123. powerful snippet. allow the user to override the parent ID for the Snippet . And then we'll change the getChildIds line to this: $children = $modx->getChildIds($id). below our first getOption call: $rowCls = $modx->getOption('rowCls'.= $modx->getChunk($tpl. For reference. etc. limit. or whatever! You've now created a flexible. sortDir. we just add a default property 'id' to our snippet code at the top.using LIs. Passing a Custom ID What if we want the user to be able to pass in what parent to grab resources from? Again. or anything else you could dream up. Adding A Row Class What if we want the user to be able to specify a CSS class for each LI row. The important part is that now you have the general idea. change our foreach loop to this: foreach ($resources as $resource) { $resourceArray = $resource->toArray(). firstRowTpl. below our getOption calls: $id = (int)$modx->getOption('id'. Basically. such as firstRowCls (for only the first row in the results).$scriptProperties.$scriptProperties.in their snippet call.390). Let's go edit our ResourceItem chunk: <li class="[[+rowCls]]"><a href="[[~[[+id]]]]">[[+pagetitle]]</a></li> And finally. Obviously.$resourceArray). } Note how we're explicitly setting the 'rowCls' variable into our $resourceArray property array. This tells MODx to default the &rowCls property for the snippet to 'resource-item'. We could even make it so the 'published' filter is a property as well. you could add more options to this snippet. $output . our final code looks like this: . 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.$c). } return $output. $output .$resourceArray).'resource-item').$scriptProperties. but don't want to have to setup a custom Template Variable and edit it on every Resource your Snippet is used on. $resources = $modx->getCollection('modResource'. if (count($children) > 0) { $c->where(array( 'id:IN' => $children. regClientStartupHTMLBlock This function is useful if you need to set some JS variables. so if you need them in a certain order.css'). make sure you execute the methods in that order as well. foreach ($resources as $resource) { $resourceArray = $resource->toArray(). $resourceArray['cls'] = $rowCls. $rowCls = $modx->getOption('rowCls'. using some MODx API methods.$tpl = $modx->getOption('tpl'. They will run in the order that they're added.= $modx->getChunk($tpl. See Also Adding CSS and JS to Your Pages Through Snippets Learning How to Register CSS and JS So.js'). )). regClientStartupScript This function lets you register any custom JavaScript to the HEAD of the document: $modx->regClientStartupScript('assets/js/site. You want the Snippet to do it. actually. $output = ''. Adding to the HEAD There are a few methods that automatically add CSS and/or JavaScript to the HEAD of the current page. $c->where(array( 'published' => true. } $c->sortby('menuindex'. $c = $modx->newQuery('modResource'). $id = (int)$modx->getOption('id'.'ResourceItem'). )). you've got a Snippet that you've been writing and want to add CSS and/or JavaScript to your pages.'ASC').390).$scriptProperties. 'deleted' => false. it's pretty easy. $children = $modx->getChildIds($id).$scriptProperties. dagnabbit! Well. or output some HTML into the HEAD: . MODx also recommends in any Extras you are distributing. in an average MODx page request. The Event Model . so that the user can customize the content or javascript framework should they so choose. Once the Plugin's code has executed. control returns to the point after the spot where the System Event was triggered. or JS that needs to be run at the body-level rather than in the HEAD. Adding Before the BODY End There are also methods that can be used to insert Javascript or HTML at the end of every page. Conclusion MODx offers Extras developers many options on how to insert custom CSS/JS into their pages at the Snippet level. There is a list of MODx System Events here. However." control is transferred to any Plugin "listening" for that event.$modx->regClientStartupHTMLBlock(' <meta tag="here" /> <script type="text/javascript"> var myCustomJSVar = 123. </script>'). That means that when those events "fire.however the big difference is that Plugins are associated with specific System Events. to make sure inserting CSS or JS into a page is a toggleable option. 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 . regClientScript Similar to regClientStartupScript except that it runs before the closing BODY tag: $modx->regClientScript('assets/js/footer. </script>'). 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. They are often useful for custom analytics scripts. there are many events that are available in the MODx Manager. For example. 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(). Plugins aren't limited to front-end processing.js'). right before the BODY tag closes. 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". If you need to know which event triggered your plugin (say.mailto: Mail To Address pnf. for a plugin that listens to more than one event). you access the output directly and modify it.page: Error Resource ID pnf. Page-Not-Found Redirector: Description: Redirects a user to selected document and sends a message System Events: OnPageNotFound System Settings: pnf. how you handle the output depends on the System Event you are in.MODx invokes System Events across its code processes to allow you to modify core functionality without hacking the core. For some system events. and will execute each Plugin in rank according to it's priority. switch($eventName) { case 'OnWebPageInit': /* do something */ break. } Plugin Examples Plugins can be used for a variety of different applications.$output). case 'OnWebPagePrerender': /* do something else */ break. you return a value from the Plugin."<b>[filtered]</b>". // get a reference to the output $output = str_replace($words. // words to filter $output = &$modx->resource->_output. The code for a Plugin listening to more than one event looks like this: $eventName = $modx->event->name. Handling an Event In your Plugin. "template"). you can access the Event's name like so: $eventName = $modx->event->name. For others. These System Events can have any number of Plugins attached to them.mailfrom: Mail From Address . 'MODx'). $modx->mail->set(modMail::MAIL_SUBJECT. $modx->mail->address('to'. primarily for filtering. } } 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. name .The name of the event. $modx->mail->set(modMail::MAIL_FROM_NAME. They are: 1 . $body).Manager Access Events 3 . $body = 'Someone tried to access document id '.Web Access Service Events 4 . $modx->mail->setHTML( true). $subject = 'Page not found'.invokeEvent method.page').Parser Service Events 2 . $modx->mail->set(modMail::MAIL_BODY. } $url = $ this->makeUrl($scriptProperties['page']). '$modx->getOption('pnf.Used for user interfaces. the numbers reference different types of System Events.$mailto).modPHPMailer'). 'mail. if (empty($errorPage)) { $modx->sendErrorPage(). $modx->mail->send(). via the modX.What type of system event this event is. 1). if (!empty($mailto)) { // send a message to a local account $resourceId = $modx->resource->get('id').$resourceId. grouping and sorting of events.User Defined Events . Service Types The 'service' field in the System event is a number. 'MODx'). groupname .Cache Service Events 5 .mailto'). Not used explicitly in the modx model. This is how they are referenced in code. $modx->sendRedirect($url. $modx->mail->set(modMail::MAIL_FROM. service . and has the following fields: id .The unique ID of the event. } else { $mailto = $modx->getOption('pnf.mailfrom')). $modx->mail->set(modMail::MAIL_SENDER. $modx->getService('mail'. $subject). allowing Plugins to interact with MODx code and add custom functionality without hacking core code.if ($modx->event->name == 'OnPageNotFound') { $errorPage = $modx->getOption('pnf. The Model of a System Event The system events table is found under {table_prefix}_system_eventnames.Template Service Events 6 . exit. The TV. See Also System Events Plugins OnBeforeChunkFormDelete Event: OnBeforeChunkFormDelete Fires before a chunk is deleted. Service: 1 . or view here. See Also OnBeforeCacheUpdate Event: OnBeforeCacheUpdate Fired before the entire site cache is cleared. Template and Snippet events are still to be documented. Note also that all WUsr (web-user) events have been removed. System Events Plugins OnBeforeChunkFormSave Event: OnBeforeChunkFormSave . Available Events This is not an exhaustive list as events are still being documented. Thank you for your patience.3 is not fired in the 'mgr' context. 2 is not fired in any context but 'mgr'.Parser Service Events Group: Chunks Event Parameters Name chunk id See Also Description A reference to the modChunk object. For a complete list. please either view a Plugin in the manager and see the System Events tab. The ID of the Chunk.Cache Service Events Group: None Event Parameters None. Service: 4 . System Events Plugins OnBeforeDocFormDelete Event: OnBeforeDocFormDelete Fires before a Resource is deleted via the manager. Service: 1 . The ID of the Resource.Parser Service Events Group: Chunks Event Parameters Name mode chunk id See Also Description Either 'upd' or 'new'. The ID of the chunk. Will be 0 for new Resources. depending on the circumstances.Fires after a form is submitted but before a Chunk is saved in the manager. .Parser Service Events Group: Documents Event Parameters Name mode resource id See Also Description Either 'new' or 'upd'. A reference to the modResource object. An array of IDs of children of this resource which will also be deleted. depending on the circumstance. Service: 1 .Parser Service Events Group: Documents Event Parameters Name resource id children See Also Description A reference to the modResource object. The ID of the Resource. Will be 0 for new chunks. A reference to the modChunk object. System Events Plugins OnBeforeDocFormSave Event: OnBeforeDocFormSave Fires before a Resource is saved in the manager via the editing form. Service: 1 . Web Access Service Events Group: None Event Parameters Name user userid username Description A reference to the modUser object of the user. Service: 4 . this event will not fire. The username of the user. See Also System Events Plugins OnBeforeWebLogout Event: OnBeforeWebLogout Fires right before a user logs out of a non-mgr context. The user ID of the user. (deprecated) System Events Plugins OnBeforeSaveWebPageCache Event: OnBeforeSaveWebPageCache Fired after the Resource is loaded and before the Resource is cached.Manager Access Service Events Group: None Event Parameters Name user userid username See Also Description A reference to the modUser object of the user. . Service: 2 . The user ID of the user. (deprecated) The username of the user.System Events Plugins OnBeforeManagerLogout Event: OnBeforeManagerLogout Fires before a user is logged out of the manager. The resource can be referenced via $modx->resource.Cache Service Events Group: None Event Parameters None. If the Resource is not cacheable. Service: 3 . Cache Service Events Group: None Event Parameters Name results paths options See Also Description The results of the clearing. Can be used to render custom Javascript for the mgr. An array of paths that were to be cleared. The ID of the Chunk. Service: 4 . Service: 1 . An array of options passed to the cache clearing method. but after the JS is registered.See Also System Events Plugins OnCacheUpdate Event: OnCacheUpdate Fired after the cache is cleared at any time.Parser Service Events Group: Chunks Event Parameters . Service: 1 . System Events Plugins OnChunkFormDelete Event: OnChunkFormDelete Fires after a chunk is deleted.Parser Service Events Group: Chunks Event Parameters Name chunk id See Also Description A reference to the modChunk object. System Events Plugins OnChunkFormPrerender Event: OnChunkFormPrerender Occurs before the chunk modification form is rendered. Name mode id chunk See Also Description Either 'new' or 'upd', depending on the circumstance. The ID of the Chunk. This will be 0 for new chunks. A reference to the modChunk object. Will be null in new chunks. 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 mode id chunk See Also Description Either 'new' or 'upd', depending on the circumstance. The ID of the Chunk. This will be 0 for new chunks. A reference to the modChunk object. Will be null in new chunks. 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 mode chunk id See Also Description Either 'upd' or 'new', depending on the circumstance. A reference to the modChunk object. The ID of the chunk. 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 resource id children See Also Description A reference to the modResource object. The ID of the Resource. An array of IDs of children of this resource which were deleted. 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 mode resource id See Also Description Either 'new' or 'upd', depending on the circumstance. A reference to the modResource object. Will be null for new Resources. The ID of the Resource. Will be 0 for new Resources. 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 mode resource id Description Either 'new' or 'upd', depending on the circumstance. A reference to the modResource object. Will be null for new Resources. 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 mode resource id See Also Description Either 'new' or 'upd', depending on the circumstances. A reference to the modResource object. The ID of the Resource. Will be 0 for new Resources. 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 docid id resource See Also Description The ID of the resource being published. (deprecated) The ID of the resource being published. A reference to the modResource object being published. 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 docid id resource See Also Description The ID of the resource being unpublished. (deprecated) The ID of the resource being unpublished. A reference to the modResource object being unpublished. 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 user attributes Description A reference to the modUser object. 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 user userid username See Also Description A reference to the modUser object of the user. The user ID of the user. (deprecated) The username of the user. (deprecated) 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 results See Also Description An array of results. System Events Plugins OnUserChangePassword Event: OnUserChangePassword Fires anytime the user properly changes their password. Service: 3 - Template Service Events Group: None Event Parameters Name user newpassword oldpassword userid username userpassword See Also Description A reference to the modUser object of the user. The new password being set. The old password being overridden. The user ID of the user. (deprecated) The username of the user. (deprecated) The new password being set. (deprecated) nor has the output been flushed. Service: 3 .The context key this login is occurring in. Content Type headers have not yet been sent.Web Access Service Events Group: None Event Parameters Name user userid username See Also Description A reference to the modUser object of the user. Service: 5 .Web Access Service Events Group: None Event Parameters Name user attributes Description A reference to the modUser object. loginContext .The session cookie lifetime for this login. Service: 3 . but before it is rendered. See Also System Events Plugins OnWebLogout Event: OnWebLogout Fires right after the user logs out of a context and their context session is removed. System Events Plugins OnWebPagePrerender Event: OnWebPagePrerender Fired after a Resource is parsed.System Events Plugins OnWebLogin Event: OnWebLogin Fired anytime a user logs into a non-mgr context. The user ID of the user.Template Service Events . An array of: rememberme . lifetime . The username of the user.Boolean set if user wants password to be remembered. loginContext .Web Access Events Group: None Event Parameters Name username password attributes Description The provided username.The context key this login is occurring in.Boolean set if user wants password to be remembered.The session cookie lifetime for this login. The provided password. An array of: rememberme . See Also System Events Plugins OnBeforeEmptyTrash Event: OnBeforeEmptyTrash Fires before the trash is emptied for the site. lifetime .Parser Service Events Group: Documents Event Parameters Name ids See Also Description An array of Resource IDs that will be permanently deleted. Service: 1 . System Events Plugins OnBeforeManagerLogin Event: OnBeforeManagerLogin Fires before the login process is started for a user when logging in to the manager context.Group: None Event Parameters None. See Also System Events Plugins OnBeforeManagerPageInit Event: OnBeforeManagerPageInit . Service: 3 . System Events Plugins OnBeforePluginFormSave Event: OnBeforePluginFormSave Fires after a form is submitted but before a Plugin is saved in the manager.Parser Service Events Group: Plugins Event Parameters Name plugin id See Also Description A reference to the modPlugin object.Loaded right before a manager controller is run. depending on the circumstance.Manager Access Events Group: None Event Parameters Name action filename See Also Description The ID of the action being loaded.Parser Service Events Group: Plugin Event Parameters Name mode plugin id See Also Description Either 'upd' or 'new'. System Events Plugins OnBeforePluginFormDelete Event: OnBeforePluginFormDelete Fires before a plugin is deleted in the manager. The ID of the Plugin. The ID of the plugin. Service: 1 . Service: 1 . Will be 0 for new plugins. A reference to the modPlugin object. The filename of the controller being loaded. Service: 2 . System Events Plugins . OnBeforeSnipFormDelete Event: OnBeforeSnipFormDelete Fires before a Snippet is deleted in the manager. Will be 0 for new Snippets.Parser Service Events Group: Snippets Event Parameters Name snippet id See Also Description A reference to the modSnippet object. Service: 1 . The ID of the Snippet. The ID of the Template. Service: 1 . Service: 1 . . The ID of the Snippet. A reference to the modSnippet object. System Events Plugins OnBeforeTempFormDelete Event: OnBeforeTempFormDelete Fires before a Template is deleted in the manager. System Events Plugins OnBeforeSnipFormSave Event: OnBeforeSnipFormSave Fires after a form is submitted but before a Snippet is saved in the manager. depending on the circumstance.Parser Service Events Group: Templates Event Parameters Name template id Description A reference to the modTemplate object.Parser Service Events Group: Snippets Event Parameters Name mode snippet id See Also Description Either 'upd' or 'new'. Parser Service Events Group: Templates Event Parameters Name mode template id See Also Description Either 'upd' or 'new'.Parser Service Events Group: Template Variables Event Parameters . The ID of the Template. depending on the circumstance. Service: 1 . A reference to the modTemplate object. System Events Plugins OnBeforeTVFormDelete Event: OnBeforeTVFormDelete Fires before a TV is deleted in the manager. The ID of the TV. System Events Plugins OnBeforeTVFormSave Event: OnBeforeTVFormSave Fires after a form is submitted but before a Chunk is saved in the manager.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: Template Variables Event Parameters Name tv id See Also Description A reference to the modTemplateVar object. Will be 0 for new Templates. Service: 1 . The ID of the User.Parser Service Events Group: Users Event Parameters . can be used by 3rd Party Components (such as Login) when a User is being activated.Parser Service Events Group: Users Event Parameters Name user id See Also Description A reference to the modUser object. Service: 1 . depending on the circumstance. System Events Plugins OnBeforeUserFormSave Event: OnBeforeUserFormSave Fires after a form is submitted but before a User is saved in the manager. The ID of the TV. Service: 1 . Service: 1 . See Also System Events Plugins OnBeforeUserFormDelete Event: OnBeforeUserFormDelete Fires before a User is deleted in the manager. A reference to the modTemplateVar object.Name mode tv id See Also Description Either 'upd' or 'new'.Parser Service Events Group: modUser Event Parameters Depends on implementation. System Events Plugins OnBeforeUserActivate Event: OnBeforeUserActivate Never fired in MODx core. Will be 0 for new TVs. An array of: rememberme . The provided password. depending on the circumstance.Web Access Events Group: None Event Parameters Name username password attributes Description The provided username.Name mode user id See Also Description Either 'upd' or 'new'. Service: 3 .Boolean set if user wants password to be remembered. Service: 2 . The ID of the User. loginContext . See Also System Events Plugins OnCategoryBeforeRemove Event: OnCategoryBeforeRemove Happens right before a category is removed.The session cookie lifetime for this login. System Events Plugins OnCategoryBeforeSave . System Events Plugins OnBeforeWebLogin Event: OnBeforeWebLogin Fires before the login process is started for a user when logging in via a non-manager context. Will be 0 for new Users.The context key this login is occurring in. lifetime .Manager Access Events Group: Categories Event Parameters Name category See Also Description A reference to the modCategory object. A reference to the modUser object. Manager Access Events Group: modCategory Event Parameters Name category See Also Description A reference to the modCategory object.Event: OnCategoryBeforeSave Happens right before a category is saved. Service: 2 .Manager Access Events Group: modCategory Event Parameters Name category See Also Description A reference to the modCategory object. Service: 2 . Service: 2 . System Events Plugins OnCategorySave Event: OnCategorySave Happens after a category is saved. System Events Plugins OnCategoryRemove Event: OnCategoryRemove Happens after a category is removed. A reference to the modCategory object. System Events Plugins OnChunkBeforeRemove .Manager Access Events Group: Categories Event Parameters Name id category See Also Description The ID of the modCategory object. System Events Plugins OnChunkSave .Parser Service Events Group: modChunk Event Parameters Name chunk See Also Description A reference to the modChunk object. Service: 2 . System Events Plugins OnChunkRemove Event: OnChunkRemove Happens after a chunk is removed.Event: OnChunkBeforeRemove Fires right before a Chunk is removed. Service: 1 .Parser Service Events Group: modChunk Event Parameters Name chunk See Also Description A reference to the modChunk object. System Events Plugins OnChunkBeforeSave Event: OnChunkBeforeSave Fires right before a Chunk is saved.Manager Access Events Group: modChunk Event Parameters Name chunk See Also Description A reference to the modChunk object. Service: 1 . See Also System Events Plugins OnContextFormPrerender . System Events Plugins OnContextBeforeSave Event: OnContextBeforeSave Happens right before a context is saved. Service: 2 .Manager Access Events Group: modContext Event Parameters Name context See Also Description A reference to the modContext object. Either 'new' (modSystemEvent::MODE_NEW) or 'upd' (modSystemEvent::MODE_UPD) depending on whether is a new object or an existing one. Service: 2 . Service: 2 .Event: OnChunkSave Happens after a chunk is saved.Manager Access Events Group: modChunk Event Parameters Name chunk See Also Description A reference to the modChunk object.Manager Access Events Event Parameters Name context mode Description A reference to the modContext object. System Events Plugins OnContextBeforeRemove Event: OnContextBeforeRemove Happens right before a context is removed. A reference to the modContext object. System Events Plugins OnContextFormRender Event: OnContextFormRender Fires after the context editing form has loaded. depending on the situation. Service: 2 .Event: OnContextFormPrerender Fires prior to the context editing form loading. Either 'upd' or 'new'. A reference to the modContext object.Manager Access Events Group: modContext Event Parameters Name context See Also Description A reference to the modContext object. System Events Plugins . Service: 2 . Service: 2 . depending on the situation.Manager Access Events Group: modContext Event Parameters Name key context mode See Also Description The key of the context. Useful for running custom javascript. System Events Plugins OnContextRemove Event: OnContextRemove Happens after a context is removed.Manager Access Events Group: modContext Event Parameters Name key context mode See Also Description The key of the context. Either 'upd' or 'new'. System Events Plugins OnFileManagerUpload Event: OnFileManagerUpload Fires after any files are uploaded via the manager.Parser Service Events Group: Documents Event Parameters Name ids num_deleted See Also Description An array of Resource IDs that were attempted to be permanently deleted. Service: 1 . See Also System Events Plugins OnEmptyTrash Event: OnEmptyTrash Fires after the trash is emptied for the site. The number of Resources actually deleted. . Service: 1 . Either 'new' (modSystemEvent::MODE_NEW) or 'upd' (modSystemEvent::MODE_UPD) depending on whether is a new object or an existing one.Parser Service Events Group: None Event Parameters Name files directory See Also Description An array of files from the PHP $_FILES array. Service: 2 .Manager Access Events Event Parameters Name context mode Description A reference to the modContext object.OnContextSave Event: OnContextSave Happens whenever a context is saved. A reference to the modDirectory object that the files are being uploaded to. Service: 1 .Template Service Events Group: None Event Parameters None. Service: 5 .System Events Plugins OnHandleRequest Event: OnHandleRequest Fires at the beginning of a request handler. See Also System Events Plugins OnLoadWebDocument Event: OnLoadWebDocument Fires directly before the Response is sent and after a Resource is loaded. The resource can be referenced via $modx->resource.Parser Service Events Group: None Event Parameters None. See Also System Events Plugins OnManagerAuthentication Event: OnManagerAuthentication . Service: 5 . See Also System Events Plugins OnInitCulture Event: OnInitCulture Fires after a Culture and Lexicon has been initialized.Template Service Events Group: None Event Parameters None. Manager Access Events Group: None Event Parameters Name user password rememberme lifetime See Also Description A reference to the modUser object. or an array where at least one index is set to true. The provided password.Fires right before the user is authenticated or its session is added to the manager context.Manager Access Events Group: None Event Parameters None. If its output is true. See Also System Events Plugins OnManagerLoginFormRender Event: OnManagerLoginFormRender Fires when the login form is rendered for the MODx manager. Service: 2 . System Events Plugins OnManagerLoginFormPrerender Event: OnManagerLoginFormPrerender Fires before the login form is rendered for the MODx manager.Manager Access Events Group: None Event Parameters None. Whether or not to remember the user via cookie. This event can be used to provide external authentication support. Service: 2 . Useful for inserting custom HTML into the login form. See Also System Events Plugins OnManagerPageInit . Service: 2 . then MODx will assume that the user has successfully logged in and bypass the default authentication via password. The lifetime of the session cookie. Defaults to "HTTP/1.Parser Service Events Group: None Event Parameters Name response_code error_type error_header error_pagetitle error_message See Also Description The response code to send. before the manager page response is loaded. Service: 1 . The header being sent: Defaults to "HTTP/1.Event: OnManagerPageInit Fired in the manager request handler.Manager Access Events Group: None Event Parameters Name action See Also Description The ID of the action currently being loaded.1 401 Unauthorized" .1 404 Not Found" The type. Defaults to 401.1 401 Unauthorized" The type.1 404 Not Found" The pagetitle of the error page. Defaults to "HTTP/1.Parser Service Events Group: None Event Parameters Name response_code error_type error_header Description The response code to send. 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. 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. Defaults to 404. The message being sent in the error page. Service: 2 . The header being sent: Defaults to "HTTP/1. Service: 1 . Service: 2 .error_pagetitle error_message See Also The pagetitle of the unauthorized page. use $modx->documentOutput. To reference the content of the Resource. The message being sent in the unauthorized page. .Manager Access Events Group: modPlugin Event Parameters Name plugin Description A reference to the modPlugin object. See Also System Events Plugins OnPluginBeforeRemove Event: OnPluginBeforeRemove Happens right before a plugin is removed. System Events Plugins OnParseDocument Event: OnParseDocument Fires on each time the Element tags are parsed.Manager Access Events Group: modPlugin Event Parameters Name plugin See Also Description A reference to the modPlugin object. Service: 1 .Template Service Events Group: None Event Parameters None. System Events Plugins OnPluginBeforeSave Event: OnPluginBeforeSave Happens right before a plugin is saved. This can happen many times during the loading of a Resource. Service: 2 . but after the JS is registered. The ID of the Plugin.Parser Service Events Group: Plugins Event Parameters Name Description .Parser Service Events Group: None Event Parameters Name id pluginEvent See Also Description The id of the modPluginEvent object A reference to the modPluginEvent object System Events Plugins OnPluginFormDelete Event: OnPluginFormDelete Fires after a plugin is deleted. System Events Plugins OnPluginFormPrerender Event: OnPluginFormPrerender Occurs before the plugin modification form is rendered. Service: 1 . Service: 1 . Service: 1 . Can be used to render custom Javascript for the mgr.Parser Service Events Group: Plugins Event Parameters Name chunk id See Also Description A reference to the modPlugin object.See Also System Events Plugins OnPluginEventRemove Event: OnPluginEventRemove Fires after a plugin is disassociated directly from an event. Useful for rendering HTML straight into the Plugin form. This will be 0 for new plugins. A reference to the modPlugin object. The ID of the plugin. depending on the circumstance. Will be null in new plugins. The ID of the Plugin. Will be null in new plugins.Parser Service Events Group: Plugins Event Parameters Name mode id chunk See Also Description Either 'new' or 'upd'.mode id plugin See Also Either 'new' or 'upd'. Service: 1 . Service: 1 . A reference to the modPlugin object. depending on the circumstance. System Events Plugins OnPluginFormSave Event: OnPluginFormSave Fires after a Plugin is saved in the manager. System Events Plugins OnPluginFormRender Event: OnPluginFormRender Fires during the rendering of a form.Parser Service Events Group: Plugins Event Parameters Name mode chunk id See Also Description Either 'upd' or 'new'. System Events Plugins OnPluginRemove Event: OnPluginRemove . A reference to the modPlugin object. The ID of the Plugin. Will be 0 for new plugins. depending on the circumstance. Happens right after a plugin is removed. Service: 2 . System Events Plugins OnPluginSave Event: OnPluginSave Happens right after a plugin is removed. System Events Plugins OnPropertySetBeforeRemove Event: OnPropertySetBeforeRemove Fires right before a Property Set is removed.Manager Access Events Group: modPlugin Event Parameters Name plugin See Also Description A reference to the modPlugin object. System Events Plugins OnPropertySetBeforeSave Event: OnPropertySetBeforeSave Fires right before a Property Set is saved.Parser Service Events Event Parameters Name propertySet See Also Description A reference to the modPropertySet object.Parser Service Events .Manager Access Events Group: modPlugin Event Parameters Name plugin See Also Description A reference to the modPlugin object. Service: 1 . Service: 1 . Service: 2 . Parser Service Events Event Parameters Name Description .Parser Service Events Event Parameters Name propertySet See Also Description A reference to the modPropertySet object. Service: 1 .Parser Service Events Event Parameters Name propertySet See Also Description A reference to the modPropertySet object.Event Parameters Name propertySet See Also Description A reference to the modPropertySet object. System Events Plugins OnResourceGroupBeforeRemove Event: OnResourceGroupBeforeRemove Fires right before a Resource Group is removed. System Events Plugins OnPropertySetRemove Event: OnPropertySetRemove Fires after a Property Set is removed. Service: 1 . System Events Plugins OnPropertySetSave Event: OnPropertySetSave Fires right after a Property Set is saved. Service: 1 . Parser Service Events Event Parameters Name group See Also Description A reference to the modResourceGroup object. System Events Plugins OnResourceGroupRemove Event: OnResourceGroupRemove Fires after a Resource Group is removed. System Events Plugins OnResourceGroupSave Event: OnResourceGroupSave Fires after a Resource Group is saved. .group See Also A reference to the modResourceGroup object. Service: 1 . Service: 1 .Parser Service Events Group: modResourceGroup Event Parameters Name group Description A reference to the modResourceGroup object. System Events Plugins OnResourceGroupBeforeSave Event: OnResourceGroupBeforeSave Fires right before a Resource Group is saved.Parser Service Events Group: modResourceGroup Event Parameters Name group See Also Description A reference to the modResourceGroup object. Service: 1 . See Also System Events Plugins OnRichTextBrowserInit Event: OnRichTextBrowserInit Used to handle MODx. This will then be matched to the System Setting 'which_editor'. such as: forfrontend width height See Also If passed. this will indicate to the plugin that this is to be loaded in a front-end context.Browser's filesystem implementation for custom RTEs. . which will allow users to select your RTE to use. If you are developing a custom RTE. The requested height of the RTE. Other properties might be passed. Simply pass in a Javascript function that will handle the return url for the file selected from MODx.Browser to your RTE. System Events Plugins OnRichTextEditorRegister Event: OnRichTextEditorRegister Renders during any dropdown or select for available richtext editors for MODx. Service: 1 . The requested width of the RTE. not the manager. Service: 1 . simply return the name of the RTE that you are developing. An array of elements to transform into an RTE.Parser Service Events Group: Richtext Editor Event Parameters None.Parser Service Events Group: Richtext Editor Event Parameters Name editor elements Description The specified editor that the user wants to use. 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 None. .Parser Service Events Group: System Settings Event Parameters None. can be used by 3rd Party Components (such as Login) when a User is being activated. See Also System Events Plugins OnUserActivate Event: OnUserActivate Never fired in MODx core.Parser Service Events Group: modUser Event Parameters Depends on implementation. See Also System Events Plugins OnSiteSettingsRender Event: OnSiteSettingsRender Fires before the system settings page is rendered. Service: 1 . Useful for adding custom HTML into the System Settings page. Service: 1 . See Also System Events Plugins OnUserBeforeRemove Event: OnUserBeforeRemove Fires right before a User is removed.Parser Service Events Group: modUser Event Parameters Name user Description A reference to the modUser object.Service: 1 . Service: 1 . Service: 1 . 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.Parser Service Events Group: modUser Event Parameters . The ID of the user.Parser Service Events Group: modUser Event Parameters Name user mode Description A reference to the modUser object. System Events Plugins OnUserFormSave Event: OnUserFormSave Fires after a User is updated via the manager form.Parser Service Events Group: modUser Event Parameters Name user id See Also Description A reference to the modUser object.See Also System Events Plugins OnUserBeforeSave Event: OnUserBeforeSave Fires right before a User is saved. Service: 1 . System Events Plugins OnUserNotFound Event: OnUserNotFound Fires when a user is not found during login.The context key this login is occurring in. Service: 1 .The session cookie lifetime for this login. loginContext .Parser Service Events Group: modUser Event Parameters Name user See Also Description A reference to the modUser object. The ID of the user. where one of the indexes in the array is an instance of (or extending) a modUser object. System Events Plugins OnUserSave . lifetime .Name user id See Also Description A reference to the modUser object. See Also System Events Plugins OnUserRemove Event: OnUserRemove Fires after a User is removed. Service: 6 . An array of: rememberme .User Defined Events Group: modUser Event Parameters Name username password attributes Description The specified username. Can be used to provide external authentication by returning an array.Boolean set if user wants password to be remembered. The specified password. System Events Plugins OnWebPageComplete Event: OnWebPageComplete Fires after the Resource is loaded. The lifetime of the session cookie. This event can be used to provide external authentication support.Web Access Events Group: None Event Parameters Name user password rememberme lifetime See Also Description A reference to the modUser object. Service: 3 .Template Service Events Group: None Event Parameters None. Service: 2 . . 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. response is sent. Either 'new' (modSystemEvent::MODE_NEW) or 'upd' (modSystemEvent::MODE_UPD) depending on whether is a new object or an existing one. then MODx will assume that the user has successfully logged in and bypass the default authentication via password. The provided password. Whether or not to remember the user via cookie. If its output is true.Parser Service Events Group: modUser Event Parameters Name user mode Description A reference to the modUser object. Service: 1 .Event: OnUserSave Fires when a User is saved. cache is stored (if applicable) and execution is completed. or an array where at least one index is set to true. and takes advantage of the newly adopted standard for database persistence in PHP 5.1+. but effective. Documentation for xPDO Documentation on xPDO can be found here: xPDO 2. as well as provide a basic way for Revolution to know what objects belong to what package. Service: 5 . They relate lexicon strings and packages to one another.Template Service Events Group: None Event Parameters None. It implements the very simple.0 only) and 5. 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). or a response is sent. but before config placeholders are set and any 404 or unauthorized page checking are done. The library works on PHP 4 (xPDO 1. Advanced Development This section is for advanced techniques and tools in MODx development.See Also System Events Plugins OnWebPageInit Event: OnWebPageInit Fires during the initialization process of a Resource. Active Record pattern for data access.0 Documentation See Also More information about xPDO can be found at the xPDO website. Usage . after modRequest::beforeRender is called. Namespaces What are Namespaces? Namespaces are organizational elements for Components. Event Map.php file containing the context settings. via: $namespace = $modx->getObject('modNamespace'.e. This can be enabled in environments where database access is more expensive than PHP include time. How to get a Namespace You can access a Namespace.'mynamespace'). xPDO Object and Database Result Set Caching If you enable this. 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.cache. 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.php where the 1 represents the id of the resource. echo $namespace->get('name'). as well as managing custom language strings for those 3rd Party Components. so it is possible to implement different kinds of features in custom component cache mechanisms (i. Different aspects of the caching can be extended with user-defined classes.php which contains the system settings which are loaded on any request.cache. Element Caching Within each context you will also find an elements/ subdirectory containing cache files for various kinds of elements (especially snippets and plugins). where you cache your own custom data). and the plugin elements that are registered in the event map.' has a path of: '. a plugin event map for the context. How is Caching Done in Revolution? First. This is separate from all of the other caching mechanisms which are very specific to the operation of MODx. For instance. just not throughout the entire core. Programmatic Caching . Context Configuration. and its specified path. 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. These files contain the resource object as well as any cached element output used by the resource.$namespace->get('path'). snippets and plugins both cache a generated global function which is included and made available at runtime. xPDO can cache query result sets representing any objects and collections in the objects subdirectory.e. web/ or mgr/ or connector/) you will find a context. Event Map.cache. See Also Package Management Internationalization Settings Caching How is Caching Done in Revolution? Configuration Cache Context Configuration.Revolution uses namespace paths to determine where to load 3rd Party Component files for custom manager pages. and Plugin Caching In a subdirectory for each context (i. These contain various kinds of cache files which the element classes make use of when they are being processed. as well as various parts of the MODx-specific caching mechanisms described above.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.php or .php // in the cachePath + all object caches) $modx->cacheManager->clearCache(). // clear only cache files with extension . 'extensions' => array('. Using modCacheManager::clearCache Now. Using modCacheManager // write some data to a cache file $colors = array('red'. $options = array('objects' => '*'. 'custom/'. /* writes to core/cache/colors. 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. foreach ($colors as $color) { echo $color.php in the cachePath // + all objects + execute the timed publishing checks $paths = array(''). // this caches the data for 2 hours. 'logs/').log in the web/ custom/ // or logs/ paths. $options).cache.php')). 'publishing' => true. '. no objects are cleared $paths = array('web/'. here are some examples of using modCacheManager->clearCache(): <?php // clear all the usual stuff by default (all files with the extension . $modx->cacheManager->clearCache($paths.'.7200).php */ // get that data $colors = $modx->cacheManager->get('colors'). $options).log')). } /* this echoes 'red-blue-green' */ ?> You can also set time expirations on cached files: $str = 'My test cached data. $modx->cacheManager->set('colors'.php'. $modx->cacheManager->clearCache($paths.'-'.cache. 'extensions' => array('.$colors).'blue'. $options = array('objects' => null.'green').$str. $modx->cacheManager->set('testdata'. 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 . // clear all cache files with extension . Right-click your Namespace from the 'Actions' tree and select "Create Action Here. CMP's were handled by Modules. In MODx Evolution. you can also use placeholders for MODx paths: {core_path} . the generic term for a script or function which generates a page is a 'controller'.core. These paths are called 'Namespaces'. you have to define a path to tell MODx where to look for the custom PHP controllers to load the Custom Manager Page. From there. Namespaces Because CMPs are generated by code on the filesystem. The Manager will search for the controller file in the path defined by the Namespace. without hacking the MODx core. The following window will show something similar to the following: .Resolves to the MODX_BASE_PATH. usually in the form of sending a request to a controller file. The following window is an example of what information makes up a Namespace: In the path. or it can simply add functionality to the Revolution core. and in case you're not familiar with MVC nomenclature. Because the Namespace name is often used as part of a URL. and it is simply a custom page that a developer or user can create that can be accessed from within the MODx Revolution manager. Creating the modAction To create a modAction. often it is /home/username/modx_location/core/ {base_path} .Brief Overview of MODExt Custom Connectors Conclusion See Also What is a CMP? CMP stands for Custom Manager Page. this helps avoid inconsistent behavior that may occur with some webservers that may handle capitalization differently. but Revolution does not use Modules. modAction objects tell the menu items what to do.Resolves to the MODX_CORE_PATH. MODx recommends to make the Namespace name lowercase. modMenu objects are the actual menu items you see on the navigation bar in the Manager. you can give it a name and 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. It can be used to create custom administration interfaces for 3rd Party Components (Extras/3PCs). How the CMP handles redirection from there on is up to the developer. go to System -> Actions. Creating a Namespace You can create a Namespace through System -> Namespaces.php at the root of the site. This is set in the /config. Make sure to leave off the . It is not currently used in MODx Revolution 2. Controller: This is where you'll put the name of the controller file to look for. and clicking 'Place Action Here'.php that you'll want to use to handle your CMPs. MODx will load the Manager header and footer files. Assets: A placeholder field for whatever you want to put in. Make sure this is set to the Namespace for your Component. if in your Namespace path. and poses no programmatic change if set to something else.php extension. They are in the standard Lexicon loading format. Parent Controller: This is currently only for organizational purposes.0. Creating the modMenu From there.There are several noteworthy fields here: 1. MODx will automatically load for the Action any Lexicon Topics you specify in the "Language Topics" field you put here. This is recommended unless you want to have a completely separate view for your CMP. 5. 6. there is a index. you can create your menu item by right clicking on an already-existing menu item (MODx recommends placing custom CMP's under 'Components'). set this field to 'index'. This will load a window where you can enter details for the new modMenu: . Load Headers: If this is checked. 4. 3. For example. 2. Namespace: This is the name of the Namespace the new action belongs to.0. Language Topics: A comma-separated list of Lexicon Topics to load prior to the page load. should it exist. Note that each variable must be prepended with an ampersand (&). http://yourdomain. Parameters: Allows you to specify other GET parameters to be added to the Menu's href URL should you want to.php?a=65&x=1&y=2 6. you simply use the following to output the content of your list.0.com/manager/index.There are also several noteworthy fields here: 1. 3. 5. The MODx Manager is powered by Smarty. One common use of Smarty is to assign MODx configuration settings and lexicons to a "placeholder" which can then be used in your templates. in your controller file you might place the following code: $modx->smarty->assign( '_lang'. Permissions: If you'd like to restrict view access to this menu item.tpl onto the page: return $modx->smarty->fetch( '/path/to/templates/list.0.tpl' ). Icon: This field is currently not in use in MODx Revolution 2. Text: The Lexicon Entry key* that will be the menu item's displayed value. so place your Lexicon Entry in that Topic. a templating engine which focuses on making it easy for developers to create their own custom templates. To use a template in the Manager. this is because the contents of this field will simply be appended to a manager url. Smarty One way to create page content is by the use of Smarty templates. 2. MODx will automatically load your Namespace's "default" Lexicon Topic. e. Steer clear of the a variable and other Reserved Parameters.g. Action: The action that connects your menu to the appropriate connector. you can do so here. the Menu will default to that handler and ignore the HREF attribute entirely. Description: The Lexicon entry key* that will be used for the menu item's description. Returning the Page Creating a CMP is very similar to a Snippet. The Lexicon Entry you specify for the Text or Description must be in the "default" Topic for your Namespace. &x=1&y=2. 7.g. e. MODx recommends you do not use 'echo'. Use this if you just want to execute a JavaScript action instead of load a page. $this->modx->lexicon->fetch() ). Handler: Allows you add a Javascript handler to execute instead of the default page loading action. you'll just return the page content using the PHP 'return' statement. Just specify the Permission name you'd want users to have to have to see this menu item. Select the modAction you just created. If this is specified. since this will load the page content before the headers have a chance to load. . For example. 4. However. $plaintext [boolean] . Scripts and CSS Since ExtJS plays an important part in the MODx Manager. Therefore. by using a Connector as a proxy to connect to a Processor. MODX_CONFIG_KEY . 2.php'. <?php $basePath = dirname( dirname( dirname( dirname( dirname( __FILE__ ) ) ) ) ). '. Custom Connectors A Connector is essentially a PHP file whose main purpose is to provide a connection between an AJAX-based request. instead of calling $modx->smarty->fetch(): $o = '<div class="test"><h2>My Component</h2></div>'.The path to your JavaScript file.The name of the placeholder. or another JS framework. the ExtJS integration for MODx Revolution. and a Processor file. The first thing your Connector must do is include the MODx configuration file. Connectors must be HTTP-accessible. and delete) operations on a database. $src [string] .mycomponent}</h2> This would output a standard MODx Manager page header with the content of the lexicon matching the "mycomponent" key. require_once MODX_CORE_PATH . you will probably need to include your own JavaScript files for your components. If you do decide to use another JS framework. Since Processors are usually involved in CRUD (create. This function takes two parameters: 1. Brief Overview of MODExt More info on MODExt. MODx recommends that you not set "Load Headers" to true on the modAction. which are used in the Manager to display an actual page (and belong in your component's /core/ directory). . Simply use $modx->regClientCSS(). they should never be accessed directly. update. return $o. it's best to place them in your component's /assets/ directory. Unlike Controllers. or pass the request data on to the appropriate Processor file. or the actual script content. The best way to include a JavaScript file on your page is to use $modx->regClientStartupScript(). '/config. $value [string|array] . which accepts a single parameter: the path to your CSS file. One could simply return the HTML code in their controller. Next. 'config/' . You'll need to create your own header file and output that with your normal output.core.An string or associative array of data (key => value) to load into the placeholder.$modx->smarty->assign() takes two parameters: 1. 'index. Defaults to false (file path). since this will load the ExtJS scripts.php'. additional authentication and security checks can be performed before allowing access to the Processor. we simply have to 'handle' the. require_once MODX_CONNECTORS_PATH . can be found here. $tplVar [string] .you can use plain HTML.inc. read.Whether the $src content is a path to a file. let's take a look at the structure of a Connector file. Now. Instead. require_once $basePath . you don't have to use ExtJS in your Custom Manager Pages . if you like. Plain-Old HTML Of course. 2. you don't need to use Smarty if you don't want to. You can also output custom CSS files in the same way. or the content of the script to output. as well as the main MODx Connector file.php'. Here's an example of using placeholders: <h2 class="modx-page-header">{$_lang. If the file is an index. You can then load the topic via: $modx->lexicon->load('tinymce:buttons'). that must be found in the manager/controllers and manager/templates directories. See Also Actions and Menus MODx Revolution introduces an entirely new program structure for its core. ?> As you can see. They integrate seamlessly into the core MODx installation. and new topics can easily be created simply by using the Lexicon Management section. If you are wanting just a blank page for the manager page.or lets users rearrange the top menu.This points to the controller file. Related Pages Custom Manager Pages . They can simply either A) use a build script to install the lexicon via a transport package.php. For example.Language Topics are simply separations of language areas that allow for quicker i18n access.php. 'location' => '' ) ). 'components/mycomponent/processors/'. modAction's require a controller and a template to exist. 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. which are abstract representations of the top menubar in the manager.inc. Load Headers . They can be found in the core/lexicon/en (or fr. which use AJAX processing to send data to connectors that access processors. this lets 3rd party developers easily and quickly develop custom menu implementations for their components . which is found under the Tools menu. The controllers are simply PHP files that load the correct Smarty template to display. These can all be managed via the Actions panel. allowing 3rd party developers to easily create custom manager pages that 'hook' into the current MODx system without modifying the core.de. handleRequest() accepts an array of options: processors_path: The base directory where your component's processor files are found.if checked.etc) directory. which would reference the topic in core/components/tinymce/lexicon/en/buttons. and fetch any pre-render data for the template. 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). this will load the MODx header and footer for the internal page. or B) have users import the lexicon topics using the Import Lexicon utility in Lexicon Management. Revolution abstracts these controllers into the DB as modAction's. modAction's are also easily hooked to modMenu's. They have a few certain parameters that are worth noting: Controller . you can leave that off and MODx will try and find it through a smart search. leave this unchecked.$modx->request->handleRequest( array( 'processors_path' => $modx->getOption( 'core_path' ) . Any changes to the order of 'core' menu items will be reverted during Revolution upgrades. action: This tells MODx the filename of the Processor file to load. Again. location: The subdirectory of processors_path to find the processor. 3rd party devs might want to create a Lexicon Topic named 'buttons' for TinyMCE. Language Topics . The manager is also built on what are called controllers and templates . and allow for entire custom applications to be built with MODx technologies. removing a Namespace from the manager will not actually delete files and folders.php">My Custom Manager Page</a> That should work. the controller). Using your FTP client. this process was handled by "Modules". right? Well. A clickable Menu object (modMenu) which associates the clickable link to the action. but those have been deprecated in Revolution. 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.. and there's a good reason that it is the way it is. the extra steps will ensure that the solution will be usable in a far greater number of scenarios.a. Watch out for typos! Make sure the Namespace path matches the directory name! . you're just telling MODx about it.x and earlier).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. There are a lot of moving parts that have to get connected to make this seemingly simple task work. a Lexicon entry which would allow you to translate the label on your menu item. for simple scenarios. maybe. 1. In our example. Click Create New b. Technically. just trust that the smart folks behind MODx put a lot of thought into how this was built.k. Name: mycmp (all lowercase. please refer to the page on Custom Manager Pages for a more detailed description. System->Namespaces a. Explanation and Mental Preparation "What's the big deal?" you might ask. Path: {core_path}components/mycmp/ (note the use of the system "core_path" placeholder. What we'll need: A PHP file on the webserver which generates the text for our CMP (a. likewise. A Namespace (i. we've chosen to call our namespace (and its folder) mycmp. this is a bit more complicated than is strictly required. one word) c. or CMP. but it's not that simple. "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. and to be fair. execute) the contents of an PHP file that has been uploaded to the webserver. 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. create a directory inside core/components named mycmp. An Action object (modAction) which is an abstract representation of your file. a path) which defines a dedicated folder for our script(s). Allowing for internationalization and scalability requires that this process include several layers of abstraction that are not immediately obvious. In MODx Evolution (versions 1. and remember to include the trailing slash) 2. Onward. Keep in mind that creating the Namespace inside the MODx manager does not actually create the directory. such a page is called a Custom Manager Page. But rest assured.e. It goes far beyond what's possible with the simple anchor tag solution above. When you "create" the namespace. And optionally. Maybe you're baffled by the complexity here. Create the Action The Action object identifies the location of your index. After you edit the menu item. or echo.php extension) Check Load Headers Optionally. Lexicon Key: My CMP b. ?> Upload the file to your MODx site into the directory (i. 1. Refresh your browser page. enter in one or more Language topics (e. System->Actions Right-click mycmp from the list of namespaces and select "Create Action Here". then you probably have no need to create a Lexicon entry. Save 3.e. the Namespace) you've just created: core/components/mycmp/index. 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. Controller: index (this should match the name of your PHP file WITHOUT the . so it should return a value. you should see your message! Make your CMP Translatable (Optional) If you never intend on internationalizing your site. The menu item will not be visible until you refresh your browser. 3. Create a file named index. 5.php Notice that we did NOT use print. we're going to keep it simple. If you use any of these. Action: mynamespace . any changes you make to an existing menu item will not be visible until you refresh the page.index d. System->Actions (in the same window where you created the Action) 2. If you add any GET Parameters to the menu item under System -> Actions. you may want to try navigating to the file in a browser window: http://yourdomain. But if you do want to provide .php which contains the following: <?php return 'This is my first Custom Manager Page'. and when you click it.Make the Controller File For our first manager page. be sure to refresh the manager page.g. Right-Click "Components" and choose "Place Action Here" a. likewise.com/core/components/mycmp/index. 4.php file within the namespace. Description: My first custom manager page c. "mycmp:default" to load the default topic for the mycmp Namespace) Create the Menu Object 1. 2. you'll find that the text floats to the top of the page. or raw HTML in our PHP. remember that a Custom Manager Page is really acting as a function.php As a superficial check. specifies a page_id MODExt . Set the 'Description' field to 'mycmp. 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. when creating a Weblink or static resource id -. Now.menu_desc'] = 'My custom manager page. you've now pointed your Action/Menu to use a particular Topic and Key. 4. e. e. 5. 3.or use your language code of choice).php: $_lang['mycmp'] = 'My CMP'. You should have something like: core/components/mycmp/lexicon/en/ Identify your Lexicon key 1. Set it to 'mycmp'.inc. 'My CMP' which can get translated into other languages. 1.php).menu_desc'. Create the Entries (Provide the Translations) Go ahead and add these entries to core/components/mycmp/lexicon/en/default. System->Actions Find your Menu action in the menu on the right (under Components) Update Menu Note the 'Lexicon Key' field.g.g. 2. 3. Did you make sure you created a directory on your webserver with the EXACT path as defined by your Namespace? Are you sure your controller file is using the return statement instead of using print or echo? Your menu items aren't being translated? Be sure to clear your cache! Site->Clear Cache Translations aren't appearing in your CMP? Make sure you specified the "lexicon" in the Action object (ie. core/components/mycmp/lexicon/en/default. The Lexicon key is a unique identifier. but here we're assuming that you are adding Lexicon entries after creating the Action and Menu objects. We need both a Language Topic and a Lexicon Key in order to define a Lexicon entry. 4. It's entirely possible to set up the Lexicon entries first and then point your Action and Menu objects to reference them second. add an 'en' directory as well ('en' for 'English' -. but you haven't yet defined them in the Lexicon.'. in this format: $_lang['lexicon_entry_key'] = 'Translation for Entry'. 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. 2.translations.g. "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.inc. Create the Topic File Create a file name default. via Site -> Clear Cache. "web" or "mgr") class_key – specifies a class name.e. the Lexicon is MODx's way of doing it. $_lang['mycmp. From there. Troubleshooting / Errors Having problems? Here are a couple things that you may have run into. and place your entries in them.php in your new 'en' lexicon directory (i. clear the site cache to reload the lexicon cache. By doing the above.inc. First. we've created our own class (MyComponent. /* Register "mycomponent-grid-mygrid" as an xtype */ Ext.call( this.grid.MyGrid. and it is also available to developers wanting to use it in their CMP development. there are more MODExt components at your disposal. Here. It drives MODx Revolution's manager interface. config ).grid.grid.0 JavaScript Framework that provides extra. browse to the manager/assets/modext/widgets/core/ directory of your MODx installation.MyGrid = function( config ) { /* Class parent constructor */ MyComponent.grid.Grid class to create our own custom grid.Grid.Grid.grid. and will likely be used in CMPs More MODExt Components Of course. create a new JavaScript file and place the following code: MyComponent. It has no additional functionality -. MyComponent.MyGrid ).reg( "mycomponent-grid-mygrid". complete with a custom toolbar.grid.constructor.extend on the MODx. MODx.MyGrid.yet! Now. let's add some configuration options: . Let's extend the MODx. Commonly-Used Components There are a few components that are used throughout the MODx Manager.grid.extend( MyComponent.grid.superclass. { /* Class members will go here */ } ). customized-to-MODx functionality. We have also registered "mycomponent-grid-mygrid" as an Ext xtype. A developer simply needs to use Ext. For a full list (and source code to examine). }.MyGrid) which extends MODx. which can be used to display this grid in a FormPanel or other component.* class to instantly get the benefit of custom MODExt components. Ext. Extending a MODExt Class Extending a MODExt component is actually quite simple.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. remoteSort : true.MyGrid. type : "int" }.reg( "mycomponent-grid-mygrid". autosave : true.MyGrid ). /* Class parent constructor */ MyComponent. { name : "menu" } ].call( this. baseParams : { action : "getlist" }.MyGrid. { name : "name". and menu). sorting. The handler creates a window used for creating a record to add to our database.grid. blankValues : true }.connectors_url + "list. it's automatically updated in the database. /* Register "mycomponent-grid-mygrid" as an xtype */ Ext. See Also ExtJS 3. { /* Class members will go here */ } ). { header : _( "name" ).Grid.php". text : _( "create" ). sortable : true } ]. dataIndex : "name". sortable : true }. scope : this } ] } ).constructor. }. type : "string" }. title : _( "my_grid" ). /* Grid configuration options */ Ext. MODx. { id : "mycomponent-grid-mygrid". and enables "autosave" functionality so that whenever a record is changed.grid. config ). /* Grid ColumnModel */ columns : [ { header : _( "id" ).MyGrid = function( config ) { config = config || {}. dataIndex : "id".grid. paging : true. /* Store field list */ fields : [ { name : "id". url : MyComponent.grid.extend( MyComponent. consisting of a button. name. Ext. handler : { xtype : "mycomponent-window-create". We then set up our fields (id. /* Top toolbar */ tbar : [ { xtype : "button".config.0 API Documentation . we create the top toolbar.applyIf( config. using the "getlist" action parameter. It also sets up paging. and our ColumnModel which references the fields in our store. Our basic configuration sets the grid up to work with a "list" connector. MyComponent.grid. Lastly.MyComponent.superclass. form.ComboBox Extends: Ext. grid renderer.combo. easily integrate toolbar items and MODx. Grids.Grid Extends: Ext.Window.FormPanel Key Features: Drag-and-drop functionality.grid. top toolbar (tbar) and bottom toolbar (bbar). FormPanels are found throughout the MODx Manager.Grid MODx.ComboBox Key Features: Remote and local data stores. or locally (using a basic Javascript array or an Ext ArrayStore.FormPanel Extends: Ext. It has built-in support . complete with a ColumnModel. It allows developers to use a ComboBox as a grid editor. built-in context menu functionality. and automatically takes care of displaying the correct displayValue in the grid cell: MODx. They can contain form fields.just about any component available.grid. One unique feature of the MODx ComboBox class is the built-in renderer for grids.grid. MODExt Grids are used to display tabular data. changed ("dirty") field checking functionality.combo. with the "mode" config option set to "local"). The MODExt ComboBox class contains all of the functionality of a regular Ext ComboBox. Trees . MODx. It may be populated remotely by an array of JSON objects from a connector (default).MODx.EditorGridPanel Key Features: Connector functionality.ComboBox MODx. connector functionality.FormPanel MODx. Grid class. 'name' => $obj->get( 'name' ). Simply provide a URL and optional parameters and a connector request will be sent after the user confirms the prompt.grid.Grid.Msg class provides the functionality of the Ext. 'handler' => ' this. The MODx.for paging as well.grid.grid.MessageBox class.tree.myHandler' ) ) ).Tree .LocalGrid MODExt MODx. however rather than using a connector to populate it with data.tree.grid. 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' ). it must be loaded through a local store. See Also MODx.Msg Extends: Ext.grid.Tree MODx." and the handler being the myHandler function registered to your Grid object.Grid MODExt MODx. The MODExt LocalGrid class is similar to the MODx.Component Key Features: AJAX connector features.Msg MODx.LocalGrid MODx. See Also MODx.EditorGridPanel Key Features: Similar to MODx.LocalGrid Extends: Ext. Grids are populated remotely from a connector request returning a JSON object. MODx. } The above code would create a context menu for each item with the text being the lexicon key matching "my_lexicon. with the added benefit of using an AJAX callback function (for confirmation dialogs).grid. 'menu' => array( array( 'text' => $modx->lexicon( 'my_lexicon' ).grid. MODExt Windows are a convenient way to display record data from a Grid or AJAX request for editing. drag-and-drop to form fields functionality. MODx.TreePanel Key Features: Remotely-loaded toolbars. such as users or resources.Window MODx. and initiates an AJAX request to your connector.tree. connector functionality for saving. Windows automatically include a FormPanel which you can add form fields (and other components) to. Trees provide a quick and easy way to display multiple levels of objects which have a parent-child relationship. Submitting/saving a Window actually submits the FormPanel.Window Key Features: Drag-and-drop functionality. Internationalization An Overview . connector functionality for removing and dragging/sorting.Window Extends: Ext.Extends: Ext. This is the name. so that the document may be viewed by a multitude of different languages without having to duplicate the page for every different language. If 'topic' is not specified. Lexicon Entries A Lexicon Entry (or modLexiconEntry in the MODx model) is simply a single translation of a string into another language. Lexicons in Code Using lexicons in code is fairly simple. When using Lexicons.The translation of the key.The IANA key of the language this Entry is translated into.'modLexicon'). the modNamespace class is used to further separate Lexicon Topics into separate namespaces. lower JS language cache load times. modLexicon::load() . Then we'll want to load the Topic using the load() method. first off you'll want to make sure the modLexicon class is loaded by instantiating it as a service: $modx->getService('lexicon'. MODx leaves you with the preference. this is a trivial process. if the tag has been run earlier on the page with the same 'topic' property value. simply do: [[%key? &topic=`topicname` &namespace=`namespace_name` &language=`en`]] The 'language'. this is best done through a System or Context Setting for the entire site or context. this is how you will reference this key. If 'namespace' is not specified. and 'namespace' properties are optional. or "key" of the Entry. or i18n. A lexicon is simply a collection of the following: Languages (IANA format) Topics Entries A Lexicon Topic is a collection of Lexicon Entries.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. with a key and a value. Loading and Using Lexicons Lexicons must first be loaded if they are to be used in the front-end. The best option is different contexts for each language. it will assume 'core'.The topic that this entry belongs to. Lexicons Via Tag To use a Lexicon Entry in a tag. Furthermore. A Lexicon Entry is one single language string. it will assume 'default'. It is preferable not to use the 'language' property for every tag should you be changing languages. and ease of maintenance. through what it calls "Lexicons". that topic will have already been loaded. But again. preventing you from accidentally overwriting a core lexicon. topic . or the MODx Revolution Core Namespace. language . 'topic'. value . It has a few important fields we'll note: name . MODx Revolution supports i18n at the core level. however. Revolution separates Entries into Topics to make for quicker language file loading. is the process of extrapolating text strings on a document to separate languages. 'user'.'user'. and the 'playground' Topic from the 'school' Namespace. The load() function supports Namespace-specific loading. [[+name]]! And then in tag form: [[!%welcome_message? &name=`John`]] Or in PHP: $modx->lexicon('welcome_message'. This would load the 'default' Topic in the 'school' Namespace. For example. You can have an infinite number of placeholders for each Tag. and can be set via Settings). John!" if John is the username of the currently logged in user.basketball': $modx->lexicon('school. Fun. The load() function also takes an infinite number of parameters.The syntax for the modLexicon::load method is pretty simple: $modx->lexicon->load('topicname'). and a setting called 'gallery. that says. Lexicons with Placeholders Say we wanted to load a Lexicon Entry with some dynamic values we have in our page.basketball'). since our string might be changing before the page cache does. which is set differently depending on the Context loaded. say you had a Lexicon Topic named 'default' in a Namespace called 'school'. this makes sure the Tag isn't cached. Example: $modx->lexicon->load('chunk'. which is the default Namespace for the MODx Revolution backend. "Hello. Our Lexicon Entry "welcome_message" value would look like this: Hello.'school:playground'). each parameter loads a separate Topic.display_thumbs' . You'd simply load it like so: $modx->lexicon->load('school:default'). This would load 3 Topics: 'chunk'. you could load it like so: $modx->lexicon->load('es:school:playground').array('name' => 'John')). it defaults to 'core'. say we want a greeting on a website. This would load the Spanish version of the 'playground' Topic for the 'school' Namespace. the load parameter supports language-specific loading. huh? modX::lexicon() Now we can use the lexicon() method on the MODx object to get our Entry with key 'school. So. should you want to override the default language that is being loaded (which defaults to the current value of $this->modx->cultureKey. The syntax for auto-loading them into the Revolution Settings grid is simple. Lexicons for Settings So say you're creating System Settings for your 3rd Party Component (3PC). Note our ! prefix for the Tag. Furthermore. If the Namespace is not specified. Let's say we have a Namespace for our Component called 'gallery'. They can be called by PHP method calls. Once an object is loaded with getService.Recommended Format The recommended format for 3PC developers is to use a prefix which identifies the parent component: $_lang['name-of-component.display_thumbs_desc'] = 'When set to true.'/path/to/twitter/model/'.array( 'api_key' => 3212423. for example: $modx->getService('twitter'. we'd simply add the following 2 strings into our 'default' Lexicon Topic for our 'gallery' Namespace: $_lang['setting_gallery. And we're done! Conclusion Lexicons provide MODx Revolution users with a plethora of avenues and options to do their i18n work. $_lang['setting_gallery.'myTwitter'. it is accessible via $modx->(servicename). or by MODx Tags.'. This helps to prevent name collisions. this will display thumbnails for the gallery. So. To add a lexicon name and description. )). keep in mind that the $_lang array may have thousands of entries.getService modMail What is modMail? . Lexicons are composed of multiple Entries for each Language. so you want to make sure each entry is unique. It can be a custom class provided by the user. or by MODx itself.lexicon Creating Lexicons for Your Components MODx Services What is a Service? A service is any object that is loaded via $modx->getService. What are the Default Included Services? A list of the core-included MODx Services is as follows: See Also modX.'.key-name'] = 'Your translation here.display_thumbs'] = 'Display Thumbnails'. and are grouped into Topics. $modx->twitter->tweet('Success!'). See Also modX. then load your class via getService. Downloading Packages Installing Packages Updating Packages Uninstalling Packages See Also Downloading Packages . you can simply call address('to') again.modPHPMailer'). We want to send it via mail to marketing@modxcms. $modx->mail->address('to'.log if the mail isn't sent for some reason (usually a server misconfiguration). to the core itself. $modx->mail->set(modMail::MAIL_SENDER. $modx->mail->address('reply-to'. Here's how we'd do it: $message = $modx->getChunk('myEmailTemplate').$err). manager templates. Also. this resets all the fields to blank. 'mail.'mom@home. What is modPHPMailer? modPHPMailer is a class that extends modMail to provide an implementation for the open source PHPMailer class.org'.modMail is an abstract class that can be extended to provide mail services for Revolution.from snippets.$message).'Check out my new email template!'). And finally.'Johnny Tester'). You'll get all the modMail functionality. $modx->mail->set(modMail::MAIL_BODY. It cannot be run by itself. the example code above will send a message to our error. $modx->mail->set(modMail::MAIL_SUBJECT. the fields above are optional (just like PHPMailer). $modx->mail->set(modMail::MAIL_FROM. if you want to send the email to multiple addresses. See Also MODx Services modX. components.'An error occurred while trying to send the email: '. so that if you didn't want to specify a 'reply-to' (though we recommend it!) you can. $modx->getService('mail'. like so: $modx->mail->address('to'.org'). We also want it to be an HTML email. if (!$modx->mail->send()) { $modx->log(modX::LOG_LEVEL_ERROR. $modx->mail->address('to'. but must be extended with an implementation class (such as PHPMailer).'[email protected] Package Management MODx Revolution introduces what are called Transport Packages.'marketing@modxcms. with the From address being 'me@xpdo. which are download locations that allow for downloading packages straight from within the MODx manager itself. Usage Let's say we have an email template in the Chunk 'myEmailTemplate'.'Johnny Tester').org').com').'me@xpdo.'me@xpdo. which are compiled zips of almost anything . no? Note that we have to reset() if we want to send mail again.com').just extend modMail with that class. Simple.com. } $modx->mail->reset(). Revolution also has Providers.com'). $modx->mail->set(modMail::MAIL_FROM_NAME. What if I want to use another email class? Simple . Also. $modx->mail->setHTML(true). but you will have to provide the wrapper class (like modPHPMailer) to do so. Downloading Packages requires you to either have cURL or sockets installed on your web server. if you want to revert back. and click the 'Revert' option. Or. packages can be downloaded directly from a browser via MODx's Extras section.com/extras/. and select the "Search Locally for Packages" option. MODx will then scan the core package directory. Should your package be already up-to-date. 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.0. a message will appear. Removing a package removes the zip file entirely from your core/packages . If the package should have a License Agreement. Also. Installing Packages You can easily install packages by right-clicking on the package and clicking "Install". and add any packages you have. located at http://modxcms. which will revert back to the prior package that was installed. The package zips are loaded simply by uploading them to your core/packages/ directory. You can then select the version you would like to install. and then running the Package Management section of the manager. you'll need to agree to it before you can proceed.You have a few options: you can download remotely via the Provider option. Uninstalling Packages You can click on any package to either remove or uninstall a package. Now. Finally.0. The Official Provider of modxcms.com has a URL of: http://rest. From there. the package might provide a README file for you to purvey before installing. click on "Add New Package". If you do not have these installed. Simply click the 'Check for Updates' context menu item (after right-clicking on the package). the package may or may not have some pre-install options and settings for you to set. you'll simply uninstall the package. To download the packages. and MODx will load a window showing any newer versions.com provider from the menu (or just by clicking 'Download Extras' in the grid toolbar).modxcms. and MODx will download the package and start the install process.com/extras/ and comes packaged in with MODx Revolution 2. the list of packages will show blank. A console will load showing you the details of the package installation. simply select the package you wish to download and click the "Download" button. by selecting the modxcms. Note the three modes when you uninstall a package: Each is self-explanatory. They can be uploaded and installed anywhere there is a MODx Revolution instance . Once done.transport. . easily-manageable format. ending with ". there might be a 'setup-options.regardless of the server configuration. or unzips.php file Okay.php file Subdirectories of each Vehicle (more on those later) It may also contain a "preserved. The Internals of a Transport Package MODx Revolution automatically "unpacks".transport. in that they match based on a simple format.0-rc2. if the package is an upgrade from a prior package.zip". Transport packages are stored in . or even to transport 3rd-Party Components in a simple. Uninstall simply uninstalls it.from database data. an example Transport Package might be "myextra-1. See Also Transport Packages What is a Transport Package? The Internals of a Transport Package The manifest. It would then behave in "upgrade" mode.transport. MODx would then interpret this as part of the same "package" but a newer version of it.zip files. your transport packages for you.zip").zip". which contains the metadata for the install to be restored.transport. This directory will contain: A manifest.zip So. Transport Packages can transport nearly anything . 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.0-rc1. If you were to upload a "myextra-1. And finally.directory.zip".php" file. files and even scripts to run during its install. Transport Packages also allow for versioning. a subdirectory in your core/packages directory will appear with the name of the zip file (minus ". complying with PHP version number standards: packagename-version-release.transport. In other words.php' file if the package has packaged one inside. explained later.These are any custom attributes that were set on the package when it was being built. Sometimes. If the vehicle is a database object. this will most likely be 'modx' or blank.These are the Vehicles metadata. vehicle_package . the vehicle will try and preserve the primary key of the database record on install.If true.An array of arrays which contain resolvers. Let's go over the common ones: class . The most common are 'license'.An array that contains the object information. which may or may not be set: validate . Inside a Vehicle's Source Vehicles can actually have a few different files grouped with them. as auto-incrementing fields often do not match across different databases. Vehicles also come in different types. the class name of the vehicle. you'll see these keys for each vehicle: vehicle_package .Similar to the manifest. this will be its primary key by which it is identified. If you open the manifest. the class type of the vehicle. the vehicle will UPDATE the object if it's found already in the database during install. resolve . you'll see that it contains a giant PHP array being returned. let's open up a Vehicle by looking a filename and diving in. In xPDOFileVehicles. Okay. explained later. a grouping value for the objects in a transport package.php file The manifest basically stores all the relevant information for the package.For transporting database data xPDOFileVehicle . including the locations of files and information about them. it may be necessary to package in "related" objects to achieve the desired end result. class . For files. and then add related objects .This tells us what type of package is holding these vehicles. you will also see a directory with the same filename as the vehicle. If false.This tells us what version the manifest definition is. what are these Vehicles? Transport Vehicles are the parts of a Transport Package. with similar keys. .The attributes for the above related objects. which is specified in the manifest and often ends with '. but we'll first concern ourselves with the main vehicle file. related_objects .Certain packages use the 'namespace' field to group vehicles and other objects to make them uniquely identifiable within a MODx installation. Within that are some keys you might be interested in: manifest-version . Currently the only type is 'transport'. the currently available ones are: xPDOObjectVehicle . A package can contain as many Vehicles as it likes. in array format. it will be skipped.Similar to the manifest.If true. filename .The column name by which the database object can be uniquely identified . So now that we've seen what the vehicles represent in the manifest. vehicle_class . MODx uses it to determine how to interpret the manifest and make it easier for future MODx versions to be backwards-compatible. target and name of the vehicle.to it. the transport type of the vehicle. update_object . There are also some optional ones. namespace .Similar to the manifest.A randomly generated GUID for the vehicle. He would make the vehicle's object be the Category.the snippets .Where the vehicle's source file can be found within the transport package's folder. xPDOFileVehicle and xPDOObjectVehicle can have different keys.Only applicable to xPDOObjectVehicles. manifest-attributes . there will be the files for the vehicle.php file. native_key .The class name of the DB object being transported. vehicle_class . it will be a PHP array with the source. signature .vehicle". this will be its primary key by which it is identified. minus the ". a unique identifier for the vehicle. often have these extra keys: preserve_keys . Again. which are important. or xPDOFileVehicle if it's a file vehicle. related_object_attributes . guid .For transporting files In the 'manifest-vehicles' array.An array of arrays which contain validators. 'readme' and 'setup-options'. For DB objects this will most likely be a JSON array representation of the DB table. guid . package .The class name of the type of Vehicle this is. or database vehicles.Similar to the manifest. The xPDOObjectVehicle. it looks like a big PHP array. native_key . object .often this is not the primary key.Similar to the manifest.If the vehicle is a database object. A great example is if the packager wants to put all of his Snippets in a Category. It has some extra keys though.vehicle'.Similar to the manifest. which MODx interprets during install time. unique_key . manifest-vehicles .The manifest. namespace . If you open it.A complex array of any related objects to this vehicle's main database object.The filename signature for this vehicle. you can easily hook into the Transport Provider and grab the latest Transport Packages easily from it.Resolvers and Validators What are resolvers and validators? Well.resolver" or ". if you open them up. a Validator would be a great place for it. Uploading the file manually to core/packages/. Each will execute in turn regardless of any other resolver results.modxcms. and each one can be from any source. Simply by specifying a service URL. Then it will present a form with pre-installation options. which you can manage easily. and from there click on the 'Providers' panel heading at the bottom. (In fact. which may or may not exist depending on the package. The user can then click 'Install' to install the package.) They are named the same filename as the vehicle. They are useful for determining whether or not the Vehicle should still be installed. in essence. Also. and clicking Install. upgraded or uninstalled. . A Resolver Resolvers are executed after the Vehicle is installed. simply go to the Package Management page.com/extras/ Usage To setup a Transport Provider. We recommend the modxcms. think of them like pre and post installation scripts. and prompting to read the README should the package contain one.com Official Provider at: http://rest. 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. Once installed. Resolvers are useful for 'cleaning up' after a Vehicle is installed. if the package was downloaded from a Transport Provider. the user can uninstall the package at any time. they look exactly like PHP scripts. A Validator A validator is executed before the Vehicle is installed. This will prompt the user to accept a License Agreement should the package come with one. upgraded or uninstalled. This allows updates to be remotely downloaded for a package as well. the Vehicle is not installed. If they return false. Once downloaded. This will open up a grid of Providers. and is skipped. MODx supports an unlimited number of Transport Providers. MODx recommends not downloading Transport Packages from providers you cannot verify or do not trust. They are. Downloading the package from a Transport Provider. but are postfixed with ". PHP scripts.validator". Usage Transport Packages can be managed in the Package Management section of the Revolution manager. and then clicking "Add New Package" and selecting the "Search Locally for Packages" option 2. then the user can check for updates for the package. For example if you want to have dependencies and not have a Vehicle installed unless something else is found. they can be installed by right-clicking a package in the grid. They can be added to the Revolution instance by either: 1. or setting custom configuration options (such as ones setup in Setup Options during install). uninstalled or upgraded in the package process. you can connect to it by going up to the Packages grid. clicking "Add New Provider". click Next. data).com is a Provider. Note that the Extras section of modxcms. and in the correct Provider format. or right-click on any provider to get more options. and the download tree will be populated with the contents of that Provider's payload (ie. This will bring up a dropdown of Provider options: Once you've selected your provider. and then select the "Select a Provider" option. web-accessible. absolute location of the provider file. This will show you a tree of Package Versions you can download: Related Pages . you can click "Add New Provider" to add another.From there. The Service URL is the actual. Providers must be valid JSON files. Once you have a provider. setup options. let's take a quick look at our directory structure.com or through Revolution's Package Management section. This isn't always how you have to do it . you might ask? This is the meat of the packaging process. but it's definitely recommended. lexicons. neat . here is where your component is actually put into the nice.zip transport package that you find on modxcms. some chunks and a snippet. which contains a modAction. especially with the assets/components/quip/ and core/components/quip/ structures.0. a license.this one is specifically built this way for SVN. a few menus. Directory Structure First off. easy run through of all the basics to creating a fundamental build script. .0-beta-4 or earlier should note that the defines are different in beta5 and onward.Creating a 3rd Party Component Build Script Directory Structure Starting the Build Script Packaging in Objects Validators and Resolvers Lexicons Package Attributes: License. This tutorial will guide you through how to create one of those scripts. Readme and Setup Options Related Pages Users using Revolution 2. We'll be using a sample component called Quip. An example: xPDOTransport::UNIQUE_KEYS in beta5+ is XPDO_TRANSPORT_UNIQUE_KEYS in beta4 and earlier. and system settings. A build script. since that makes creating the transport package much easier. It's basically a quick. a readme. MODx recommends to just update to beta5/SVN. What is that. We can define these up top into a "sources" array to make them easier to reach later in the build script. This is required. and then start the timer. . <?php /** * Quip build script * * @package quip * @subpackage build */ $mtime = microtime().Starting the Build Script Let's first start with some phpdoc comments at the top. /* makes sure our script doesnt timeout */ Now let's define some basic paths. set_time_limit(0). $tstart = $mtime. $mtime = explode(" ". $mtime). $mtime = $mtime[1] + $mtime[0]. Note how the 'source_core' and 'source_assets' directories do not post-fix a foreslash onto their paths. and set the log output to HTML to make our errors and info messages nice and formatted .php'.'{core_path}components/quip/').php'.false.'/'.1-alpha7.'core/components/quip'.'_build/'. '_build/data/'. 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. 'source_assets' => $root. ). MODx does so by relating it to it's Namespace. so let's go with that. First. let's include a file we'll create called 'build. define('MODX_CONFIG_KEY'. a Namespace is an organizing tool for MODx so that MODx can know what objects are tied to what package. Let's first off use $modx->loadClass to load the modPackageBuilder class.unless we're doing this from the cmd line. Not all packages need Namespaces. '/build. you'll want to include the modX class. Okay. '/absolute/path/to/modx/core/'). $builder = new modPackageBuilder($modx). This is helpful later on should we want to uninstall our package. 'data' => $root . $modx->loadClass('transport. $builder->registerNamespace('quip'.config. true). so we can easily manage just our Lexicon Entries. and create a package. /* save memory */ Now.config. Then we'll instantiate an instance of it. version. we'd want it to remove the objects we'd install. $modx= new modX().'alpha7').modPackageBuilder'. MODX_CONFIG_KEY can stay the same. as well as where to put the package when finished. Now. Basically. unless you're doing a multi-domain install.1'. For us. . 'core/components/quip/lexicon/'. 'resolvers' => $root . 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. we'll be doing quip-0. Plus. and instantiate it. The modPackageBuilder::createPackage function has 3 parameters: name. 'build' => $root . 'docs' => $root. $modx->setLogTarget(XPDO_CLI_MODE ? 'ECHO' : 'HTML'). 'lexicon' => $root . In this file. we'll need to include some files to get the build libraries we'll need. and release.'0. Our package builder will assign our Lexicon Entries to the Namespace. $sources= array ( 'root' => $root. where we'll want just standard echo messages.true. We'll also initialize it into the 'mgr' context.false.'core/components/quip/docs/'. $modx->initialize('mgr'). require_once dirname(__FILE__) . not any others. but all 3rd Party Components do. require_once MODX_CORE_PATH . $modx->setLogLevel(modX::LOG_LEVEL_INFO).class. 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'. unset($root). '_build/resolvers/'.$root = dirname(dirname(__FILE__)). should we want to add any Lexicon Entries to this package (which we will). $builder->createPackage('quip'.'config'). Next. 'model/modx/modx. 'source_core' => $root.php' in our build dir.''.'assets/components/quip'. we'll register a Namespace to this package. it's time for the meat. whichever you prefer) that the vehicle is based off of.'Test').1). So. we told MODx not to update its PK . others require different settings. 'Test') when updating or removing the object.there's no need to adjust that in this situation. this will be 'name'. If the object is not found. So. Some might even require an array of two or more fields. xPDOTransport::PRESERVE_KEYS => false. vehicles can contain many objectsor files . this tells MODx whether or not to update the object if it is found in the DB upon install (or update).Either true or false. For most objects. Let's look at those attributes options more closely: xPDOTransport::UNIQUE_KEY (string/array) . this tells MODx whether or not to rewrite the primary keys when the object is found. vehicles that contain an object must only have one reference object (or parent object. if it does find it.however.Packaging in Objects Objects are packaged as Vehicles in MODx Revolution. This can be useful if you're wanting the PKs to stay the same when you update . xPDOTransport::UPDATE_OBJECT => true.array( xPDOTransport::UNIQUE_KEY => 'name'. you may not want to update it . $vehicle = $builder->createVehicle($snippet. if the object is already there. Simple enough? So our example tells it to look for a Snippet named 'Test'. $snippet->set('name'. along with its Action associated with the modMenu? Here's a bit more complex scenario: . let's look at some examples for creating a vehicle before digging into our build script. and if it finds it. Sometimes. xPDOTransport::UPDATE_OBJECT (boolean) . we used the 'createVehicle' function in modPackageBuilder to create the vehicle object.Either true or false. we created a snippet object.some PKs are auto_increment. Note: If the object already exists. $snippet->set('id'. with some parameters: $snippet = $modx->newObject('modSnippet'). even though we wont keep it later. basically think of a vehicle as a sort of storage system that transports the data and/or files into the zip package. Note that you'll have to specify an arbitrary ID for it. )). first off. it will work regardless. Packages can contain many vehicles. However. This is required.Here you'd place the unique key that identifies the object you're creating. and if you're wanting those to stay the same number. this feature only works if xPDOTransport::UPDATE_OBJECT is set to true as well. If it doesnt find it. Now. what about related objects? What if I want to package in my modMenu. Then. you'd set this to true. create it. xPDOTransport::PRESERVE_KEYS (boolean) . This first example packages in a simple object.the update might erase the user's current settings for that object. update its contents. This will tell MODx to search for the modSnippet with the 'name' equal to the packaged in name (here. 'haslayout' => '1'. You can go however deep in nesting that you want.'controller').true). a bit more meat here.gif'. 'namespace' => 'quip'. 'menuindex' => '0'. We're introducing 2 new parameters: xPDOTransport::RELATED_OBJECTS (boolean) . Okay. Also. the format is simply an associative array of attributes .file'. if we wanted to package in related objects to the modAction objects. $menu= $modx->newObject('modMenu').Either true or false. xPDOTransport::RELATED_OBJECTS => true. xPDOTransport::UPDATE_OBJECT => true.''.true). 'description' => 'quip_desc'.schema. 'parent' => '0'.$action= $modx->newObject('modAction'). ). located in core/model/schema/modx. so far we have: . we would just have had to define that in the 'Action' attributes and addMany (or addOne) on that action. 'handler' => ''. ).similar to the parent object's attributes . back to our script. $menu->fromArray(array( 'text' => 'quip'.true. ). If you note. 'params' => ''. $action->fromArray(array( 'id' => 1. 'parent' => 'components'.This defines the types and details of the related objects we want to grab. To recap. xPDOTransport::RELATED_OBJECT_ATTRIBUTES => array ( 'Action' => array ( xPDOTransport::PRESERVE_KEYS => false. xPDOTransport::RELATED_OBJECT_ATTRIBUTES (array) .true.''.xml. We packaged them in manually using xPDO's addOne function on the modAction. this will tell MODx we want to search for related objects to this object. ). xPDOTransport::UPDATE_OBJECT => true. So. $menu->addOne($action).mysql. xPDOTransport::UNIQUE_KEY => array ('namespace'.where the key is the "alias" of the related object we want to grab. )). 'lang_topics' => 'quip:default. xPDOTransport::UNIQUE_KEY => 'text'. This must be set for the next parameter to work. $vehicle= $builder->createVehicle($menu. 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. 'controller' => 'index'. 'icon' => 'images/icons/plugin.array ( xPDOTransport::PRESERVE_KEYS => true. 'assets' => ''. The aliases can be found in the Schema. php) */ require_once dirname(__FILE__) .php'.php'. 'build' => $root . 'source_core' => $root. true).''. $modx->loadClass('transport.'/'.'assets/components/quip'. $modx->initialize('mgr'). and isolate our actions/menus to a separate file for easy management.but it does keep our build script clean.'{core_path}components/quip/'). 'lexicon' => $root . ). $builder->registerNamespace('quip'.true. $vehicle= $builder->createVehicle($menu.'core/components/quip/docs/'. $tstart = $mtime.'alpha5').false. /* to keep memory low */ Wait! Notice how I put the action data in a different file? You don't have to do this . 'data' => $root .'0. '/build.class. 'resolvers' => $root . let's first package in our modActions and modMenus for our backend: /* load action/menu */ $menu = include $sources['data']. xPDOTransport::RELATED_OBJECT_ATTRIBUTES => array ( 'Action' => array ( xPDOTransport::PRESERVE_KEYS => false. /* override with your own defines here (see build.sample.'controller'). $mtime = explode(" ". ). '_build/data/'. xPDOTransport::UPDATE_OBJECT => true. unset($root). unset($vehicle. 'docs' => $root. $root = dirname(dirname(__FILE__)).'transport.1'. $mtime).'_build/'.array ( xPDOTransport::PRESERVE_KEYS => true. $mtime = $mtime[1] + $mtime[0]. 'model/modx/modx. Let's do the same with our system settings: . 'source_assets' => $root. xPDOTransport::UNIQUE_KEY => 'text'. $builder->createPackage('quip'.config. So.modPackageBuilder'. set_time_limit(0). $sources= array ( 'root' => $root.false.menu. '_build/lexicon/'.<?php /** * Quip build script * * @package quip * @subpackage build */ $mtime = microtime(). $modx->setLogTarget(XPDO_CLI_MODE ? 'ECHO' : 'HTML'). $modx->setLogLevel(modX::LOG_LEVEL_INFO). xPDOTransport::UNIQUE_KEY => array ('namespace'.$action).php'. )). $modx= new modX(). require_once MODX_CORE_PATH .it's completely personal preference . $builder->putVehicle($vehicle). ).config. xPDOTransport::UPDATE_OBJECT => true. '_build/resolvers/'.'core/components/quip'. $builder = new modPackageBuilder($modx). xPDOTransport::RELATED_OBJECTS => true. let's create a category called 'Quip' and put our Snippet and Chunks in that category. $snippet->set('description'.just update that file. 'Quip'). 'A simple commenting component.').$attributes).inc.php'. let's take a look at that properties.'Quip'). Step one done: category created. great..file_get_contents($sources['source_core']. You'll use modSnippet's setProperties function to pass in an array of property arrays.php'./* load system settings */ $settings = include $sources['data']. $snippet->set('name'.how do we put those in? $properties = include $sources['data'].settings. Now. $attributes= array( xPDOTransport::UNIQUE_KEY => 'key'. foreach ($settings as $setting) { $vehicle = $builder->createVehicle($setting. So. menus and settings packaged in.. so we can easily see how this works: /* create category */ $category= $modx->newObject('modCategory'). Now. $snippet->set('snippet'. $builder->putVehicle($vehicle).'properties.quip. $category->set('category'. This makes it easy to run the build in future iterations. using our newfound knowledge about related objects.'/snippet.php'). $snippet->setProperties($properties).$attributes).inc. } unset($settings.'transport. xPDOTransport::PRESERVE_KEYS => true.php file: . Now about that Snippet: /* create the snippet */ $snippet= $modx->newObject('modSnippet'). $snippet->set('id'.1).$setting. we had some properties on that snippet. Great! We've got our actions. $category->addMany($snippet). no need to continually update this call . xPDOTransport::UPDATE_OBJECT => false. We'll go through this a bit slower. $category->set('id'. ). Okay. 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.0). 'Chunks' => array ( xPDOTransport::PRESERVE_KEYS => false. xPDOTransport::UPDATE_OBJECT => true.so it'll look nice and sharp! Validators and Resolvers Validators and resolvers are basically scripts that run during the install process. We also added a sanity check just in case we made a typo or something. if (is_array($chunks)) { $category->addMany($chunks). /* .$attr). 'value' => '%b %d. Simple enough. And now on to the chunks: /* add chunks */ $chunks = include $sources['data']. ).'). ). xPDOTransport::UNIQUE_KEY => 'name'.'Adding chunks failed.removed others for brevity. ). the thread will not accept new comments. xPDOTransport::UPDATE_OBJECT => true..<?php /** * Default snippet properties * * @package quip * @subpackage build */ $properties = array( array( 'name' => 'closed'.'transport. Validators are run pre-install. %Y at %I:%M %p'. $vehicle = $builder->createVehicle($category. let's package all that into a vehicle: /* create category vehicle */ $attr = array( xPDOTransport::UNIQUE_KEY => 'category'. Now. 'type' => 'combo-boolean'. the installation does not proceed.'. xPDOTransport::UPDATE_OBJECT => true. */ ). array( 'name' => 'dateFormat'. ).chunks. 'desc' => 'If set to true. 'options' => ''. } else { $modx->log(modX::LOG_LEVEL_FATAL.'. return $properties. xPDOTransport::UNIQUE_KEY => 'name'. We returned an array of chunks.php'. 'type' => 'textfield'. meaning that they are run before the main package installation happens. complete with all the related chunks and snippet. . Great! We've got our category vehicle. ) ).. They'll be installed in the right category when our users install our package... If they return false. xPDOTransport::PRESERVE_KEYS => false. xPDOTransport::RELATED_OBJECT_ATTRIBUTES => array ( 'Snippets' => array( xPDOTransport::PRESERVE_KEYS => false. 'value' => false. and used modCategory's addMany() function to add them in. xPDOTransport::RELATED_OBJECTS => true. 'desc' => 'The format of the dates displayed for a comment. 'options' => ''. too . } Good. 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' => '[email protected]', 'emailsFrom' => '[email protected]', 'emailsReplyTo' => '[email protected]', ); switch ($options[xPDOTransport::PACKAGE_ACTION]) { case xPDOTransport::ACTION_INSTALL: case xPDOTransport::ACTION_UPGRADE: $setting = $modx->getObject('modSystemSetting',array('key' => 'quip.emailsTo')); if ($setting != null) { $values['emailsTo'] = $setting->get('value'); } unset($setting); $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: Typically use the modUser suggestions to access modUser methods. but do not create code to replace modUser. The focus is purely user authentication. etc. If the user (object) has not been extended. MODx Revolution already handles users and probably does not need your help. $tend= $mtime. which can be assigned per context. You can get much more complex. 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.4f s". and other applications not yet conceptualized. user management systems. Also. such as attributes. $mtime= explode(" ". 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. In other words use the Revolution resources for your extended users.$builder->pack(). enhanced. Lastly. we're done! You'll only need to run this script now. While you may use your extension on *your* data.$tstart)."\nPackage Built. Instead we should be using it as a platform to build upon. $mtime= microtime(). For fully functional applications please refer to Currenty available extended modUser classes. An example follows: . Steps to extending modUser 1. is to create an extended user schema which extends modUser. 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. etc. Please note that there is no aggregate relation upwards from your "main" class which is extending modUser. $mtime). $totalTime= sprintf("%2. ) Create the schema and generate a model The first thing we need to accomplish. 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. Your extension should be used to access your extension. get familiar with modUser. At no time should an extended application actually attempt to completely replace the modUser Class. $mtime= $mtime[1] + $mtime[0]. please understand this is a a simplified working concept. This ability to extend the modUser class is just one example of the underlying power of MODx Revolution. The Rules Extending modUser does NOT mean we are adding anything to the modx_users table in the database. do not allow your extension to interact with them -. please do not begin writing "bloat" which is already in modUser. $totalTime= ($tend . Great. Purpose Extending modUser is for those situations when user authentication interaction needs overridden. extended. resource. $modx->log(modX::LOG_LEVEL_INFO. $totalTime).\nExecution time: {$totalTime}\n"). exit(). before you begin to code. and viola! A fully zipped transport package file will appear in your core/packages directory. By following the steps detailed below you will quickly be on your way to developing your own user "interfaces" or sub-systems.hence: let your extension die. Some methods are not one-to-one as you might assume. add . If the key already exists. extendeduser:{core_path}components/extendeduser/model/ Note the comma at the beginning of the Value.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.) Edit the extuser.class.'extUser').0" encoding="UTF-8"?> <model package="extendeduser" baseClass="xPDOObject" platform="mysql" defaultEngine="MyISAM" tablePrefix="ext_"> <!-. please refer to Using Custom Database Tables in your 3rd Party Components for further information.php To access the extended class.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> <!-.php file created when you generated the model. we have to inform modUser that the user in question has been extended.<?xml version="1.) Create (or edit) extension_packages in System Settings Access the System settings found in the System menu of the manager. The default value in this field is modUser. Edit the file to resemble the following: <?php /** * @package extendeduser * @subpackage user. $this->set('class_key'. 2. } } ?> 3.mysql */ class extUser extends modUser { function __construct(xPDO & $xpdo) { parent :: __construct($xpdo). As this process is out of the scope of this topic. The specific file is the one found in the top of the model tree (you should see a mysql directory) in this same folder.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> <!-. If the key does not exists .class. and search for: extension_packages. The modx_users table in the database contains a field specifically for this purpose: class_key. namely extUser in our example. Edit the extuser. As users are added to your site using your extension we need to "force" the name of our "main" class in the schema. 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 . MODX_CORE_PATH). $this->extPath .null. * @param $userID */ public function getUserObj($userID) { return $this->modx->getObject('modUser'. } /** * Returns object utUser instance of modUser Defaults to current user. } } } 5. $this->userID. Shawn Wilkerson * @link http://www. return $this->userObj->Phones.'model/'.<?php /** * File sample.'components/extendeduser/'.class. $userID). /* Establish the environment */ $this->extPath= $modx->getOption('core_path'.) Accessing the class In our example we will be accessing our extended user throughout our site. $this->userObj. $this->_config= array_merge(array ( 'userID' => $this->modx->user->get('id'). 2010 * Project shawn_wilkerson * @package extendedUser * @version 1.0 * @category User Extension * @author W.php (requires MODx Revolution 2. $this->userID = $this->userObj->get('id'). * @param int $userID */ public function setUser($userID){ return $this->getUserObj($userID). } /** * Returns object of type Phone. All rights reserved. Shawn Wilkerson. } function __destruct() { unset ($this->extPath. /* Define the user */ $this->userObj = $this->setUser($this->_config['userID']). therefore we load it as a service as shown in the following example: .x) * Created on Aug 18. } /** * Establishes the user. $this->modx->addPackage('extendeduser'. W. array $config= array ()) { /* Import modx as a reference */ $this->modx= & $modx. * @license GPL * */ if (!class_exists('Sampleclass')) { class Sampleclass { function __construct(modX & $modx. $this->_config). $config). ). 'ut_'). */ public function getPhoneObj() { $this->userObj->getOne('Phones').shawnWilkerson.com * @copyright Copyright (c) 2010. login correctly and then alter the path to a correct representation of the path. Any user with the class_key of extUser will return an error upon login: "User cannot be found. access your database directly.'[Extendeduser] Could not load Extendeduser class. This process can be automated and captured upon user login. } return. $modx->event->output(true). The snippets attached to the class will intermittently work or fail altogether 6.php The events: http://github..$scriptProperties). This is simply done by following this process for each of your extensions. $this->modx->getCount('extPhones'). where he provides a real world application: The plugins: http://github. To get counts from your data (i. if (!($x instanceof Extendeduser)) { $modx->log(modX::LOG_LEVEL_ERROR. it is best to refer you to splittingred's github.com/splittingred/modActiveDirectory/blob/master/core/components/activedirectory/elements/events/onusernotfound. It would even be feasible to extend Jason Coward's rpx extension into a hybrid system utilizing the benefits of both systems.schema. Symptoms of step 3 (extension_packages path) not being correct: a.mysql. If this is the admin.null. but the attached relations will 4.e. 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.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.'Sampleclass'.com/splittingred/modActiveDirectory/blob/master/core/components/activedirectory/elements/plugins/plugin.com/splittingred/modActiveDirectory/blob/master/core/components/activedirectory/elements/events/onauthentication. The extUser will not have a table created in the database. It is also completely possible to have multiple extended modUser applications running autonomously. It is completely possible to have multiple extended modUser systems active at the same time.').'components/extendeduser/'.$modx->getOption('core_path'. Suggested additional considerations The model files can be edited with methods and descriptions. how many phone numbers does this person have) use either (any criteria can be added): $this->modx->getCount('extPhones'. Any pre existing user. The extended class table(s) must be in the same database as the regular modx_users table 5. Noteworthy items 1.activedirectory. b. Take a look at much of the MODx / xPDO models and you will see this done extensively.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. MODX_CORE_PATH).. Double check the modx. Loading MODx Externally .". changing only the "class_key" field to reflect the extended class belonging to each respective user. For brevity sake. array('user' => $this->userID)). return the class_key to modUser.<?php $x = $modx->getService('extendeduser'.php http://github. core.com Class Reference modX The modX Class This is the base class of MODx Revolution. etc).MODX_CONFIG_KEY. It extends the xPDO class.Loading the MODx Object Using the MODx object (and all of its respective classes) is quite simple.php'. From there. $modx->initialize('web').php'. Syntax API Doc: http://api.'. if you want to access it under a different context (and thereby changing its access permissions. See Also Developer Introduction xPDO.getChunk modX::getChunk Processes and returns the output from an HTML chunk by name. Now. require_once MODX_CORE_PATH. or classes. functions.'config/'. the db-layer for Revolution API Reference The MODx Revolution API Documentation can be found here: http://api.class. All you need is this code: require_once '/absolute/path/to/modx/config. This will initialize the MODx object into the 'web' Context.modxcms. Methods See Also modX. $modx = new modX(). you'll just need to change 'web' to whatever Context you want to load.inc. it is used for many of the main processing methods of MODx.com/modx/modX.'model/modx/modx. you can use any MODx methods. policies.modxcms.html#getChunk .php'. require_once MODX_CORE_PATH. modxcms. for the current or specified context. See Also Contexts modX. called "WelcomeChunk": <p>Welcome [[+name]]!</p> We'll put this in our Snippet: $output = $modx->getChunk('WelcomeChunk'. Syntax API Doc: http://api.com/modx/modX.string getChunk (string $chunkName. $username = $modx->getLoginUserName().com/modx/modX.getPlaceholder modX::getPlaceholder Get a placeholder value by key.html#getLoginUserName string getLoginUserName ([string $context = '']) Example Grab the user's username in the current Context.array( 'name' => 'John'.modxcms. return $output.getLoginUserName modX::getLoginUserName Returns the current user name. Syntax API Doc: http://api. )).html#getPlaceholder . [array $properties = array ()]) Example Lets process this chunk and output its value. This code outputs this: <p>Welcome John!</p> See Also Chunks modX. We have this Chunk. ' to their key.setPlaceholders modX.getPlaceholder modX. to isolate the collection of placeholders.toPlaceholders modX.setPlaceholder modX::setPlaceholder Sets a Placeholder value.modxcms. Use toPlaceholders() when working with multi-dimensional arrays or objects with variables other than scalars so each level gets delimited by a separator.setPlaceholder modX.toPlaceholder modX. Syntax API Doc: http://api.com/modx/modX. this function does not add separators between the namespace and the placeholder key.toPlaceholders. Syntax API Doc: http://api.toPlaceholders modX.toPlaceholder modX.html#setPlaceholders void setPlaceholders (array|object $placeholders. See Also modX.setPlaceholders modX. See Also modX. [string $namespace = '']) Example Add an array of placeholders.html#setPlaceholder void setPlaceholder (string $key.modxcms. An optional namespace parameter can be prepended to each placeholder key in the collection. and prefix 'my.mixed getPlaceholder (string $key) Example $value = $modx->getPlaceholder('MyPlaceholder').com/modx/modX.setPlaceholders modX::setPlaceholders Sets a collection of placeholders stored in an array or as object vars.'Barry'). Note that unlike modX. mixed $value) Example $modx->setPlaceholder('name'. . com/modx/modX.html#addEventListener boolean addEventListener (string $event.setPlaceholder modX.modxcms. ).toPlaceholder modX. string $action.com'.getPlaceholder modX.modxcms. if ($modx->checkForLocks($modx->getLoginUserID(). Syntax API Doc: http://api.'my. 'email' => 'jdoe@gmail.'edit_chunk'.html#checkForLocks void checkForLocks (integer $id.12).'). See Also modX Plugins modX. A page is "locked" if another user is already viewing it. This prevents collisions.com/modx/modX. See Also modX.toPlaceholders modX. integer $pluginId) Example Add a Plugin with ID 2 to the Event 'OnChunkPrerender': $modx->addEventListener('OnChunkPrerender'. string $type) Example Check for locks on the edit_chunk action.$modx->setPlaceholders(array( 'name' => 'John'.addEventListener modX::addEventListener Add a plugin to the eventMap within the current execution cycle. See Also modX .'edit').checkForLocks modX::checkForLocks Checks for locking on a page. Syntax API Doc: http://api. executeProcessor modX::executeProcessor Executes a specific processor. will prepend to the action parameter. See Also modX modX.getAuthenticatedUser modX::getAuthenticatedUser . )).html#executeProcessor mixed executeProcessor (array $options) Example Execute the Context getList processor: $modx->executeProcessor(array( 'location' => 'context'. The only argument is an array. will override the default MODx processors path.modX.com/modx/modX.If specified. location . $modx->checkSession('sports'). 'action' => 'getList'.The action to take.modxcms. processors_path . Syntax API Doc: http://api. See Also modX modX. which can take the following values: action .modxcms.com/modx/modX.checkSession modX::checkSession Checks to see if the user has a session in the specified context.html#checkSession boolean checkSession ([string $sessionContext = 'web']) Example Check to see if the user has a session in the 'sports' context. similar to connector handling.A prefix to load processor files from. Syntax API Doc: http://api. $cacheManager->set('testcachefile'. Syntax API Doc: http://api.modxcms.getChildIds modX::getChildIds Gets all of the child resource ids for a given resource.com/modx/modX.'test123').modxcms. Overrides xPDO::getCacheManager. $cacheManager = $modx->getCacheManager(). Syntax API Doc: http://api.html#getChildIds array getChildIds ([integer $id = null]. Syntax API Doc: http://api.modxcms. [integer $depth = 10]) Example .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.com/modx/modX.com/modx/modX.Gets the user authenticated in the specified context.html#getCacheManager object getCacheManager() Example Get the Cache Manager to set a dummy cache file. See Also modX modX. See Also modX modX. Syntax API Doc: http://api.html#getContext &$modContext getContext (string $contextKey) Example Get the 'sports' Context.com/modx/modX. Syntax API Doc: http://api.com/modx/modX. contexts retrieved using this function will cache the context data into the modX::$contexts array to avoid loading the same context multiple times.html#getConfig array getConfig () Example Get the site config into an array.getContext modX::getContext Retrieve a context by name without initializing it.6).getConfig modX::getConfig Get the configuration for the site. Within a request. $config = $modx->getConfig().modxcms.getParentIds modX.modxcms. Limit to 6 levels deep. $array_ids = $modx->getChildIds(23. $ctx = $modx->getContext('sports'). See Also modX modX. See Also Page: Contexts Page: modX .Get all the children IDs for the Resource 23. modxcms.Page: modX. .getParentIds modX::getParentIds Gets all of the parent resource ids for a given resource. See Also modX modX.getContext Page: Contexts Page: modX Page: modX.html#getEventMap array getEventMap (string $contextKey) Example Get the event map for the current Context.html#getLoginUserID string getLoginUserID ([string $context = '']) Example Get the current login user ID for the 'sports' context. Syntax API Doc: http://api.modxcms.com/modx/modX.getContext modX. $map = $modx->getEventMap(). $id = $modx->getLoginUserID('sports'). Syntax API Doc: http://api.com/modx/modX.getLoginUserID modX::getLoginUserID Returns the current user ID.getEventMap modX::getEventMap Gets a map of events and registered plugins for the specified context. for the current or specified context. See Also modX modX. getChildIds modX. See Also modX modX. Syntax API Doc: http://api. $parser = $modx->getParser().modxcms.modxcms. See Also modX modX. performing actions.html#getRegisteredClientScripts string getRegisteredClientScripts () Example .com/modx/modX.com/modx/modX.html#getParentIds array getParentIds ([integer $id = null]. $parentIds = $modx->getParentIds(23).html#getParser object getParser() Example Get the MODx Parser object. returning content and/or sending other responses in the process. [integer $height = 10]) Example Get all of the parent IDs for the Resource with ID 23.Syntax API Doc: http://api. Syntax API Doc: http://api.com/modx/modX. Returns an instance of modParser responsible for parsing tags in element content.getParser modX::getParser Gets the MODx parser.getRegisteredClientScripts modX::getRegisteredClientScripts Returns all registered JavaScript and HTML blocks.modxcms. See Also modX modX.getResponse . Defaults to modRequest.Get all registered scripts into an array. See Also modX modX. or HTML blocks.html#getRequest boolean getRequest ([$string $class = 'modRequest'].com/modx/modX.php': $modx->getRequest('myRequest'. [$path $path = '']) Example Load a custom Request handler class called 'myRequest' from '/path/to/myrequest. if not already loaded.'/path/to/').html#getRegisteredClientStartupScripts string getRegisteredClientStartupScripts () Example Get all registered startup scripts into an array.com/modx/modX. Syntax API Doc: http://api.modxcms. See Also modX modX. JavaScript.getRegisteredClientStartupScripts modX::getRegisteredClientStartupScripts Returns all registered startup CSS. Syntax API Doc: http://api.class. $scripts = $modx->getRegisteredClientScripts().getRequest modX::getRequest Attempt to load the request handler class. $startupScripts = $modx->getRegisteredClientStartupScripts().modxcms. if not already loaded.class.com/modx/modX. $modx->getService('twitter'. $modx->getService('smarty'.'/path/to/'. user-defined service called 'modTwitter' from a custom path ('/path/to/modtwitter. See Also modX MODx Services modX. Defaults to modResponse.php').class. Syntax API Doc: http://api. Syntax API Doc: http://api. and pass in some custom parameters.getSessionState modX::getSessionState .'/path/to/').php': $modx->getRequest('myResponse'. Get a custom. See Also modX modX.modxcms. [string $path = ''].com/modx/modX.modSmarty'). $modx->twitter->tweet('Success!').modxcms. )).array( 'api_key' => 3212423. [string $class = ''].'modTwitter'. [array $params = array ()]) Examples Get the modSmarty service.html#getResponse boolean getResponse ([$string $class = 'modResponse'].'smarty.modX::getResponse Attempt to load the response handler class. [$path $path = '']) Example Load a custom Response handler class called 'myResponse' from '/path/to/myresponse.getService modX::getService Load and return a named service class instance.html#getService object getService (string $name. 5). Syntax API Doc: http://api. Only go 5 levels deep. [int $depth = 10]) Example Get a tree for the Resource with ID 12.getTree modX::getTree Get a site tree from a single or multiple modResource instances. See Also modX modX.modxcms.com/modx/modX.html#getUser modUser getUser ([string $contextKey = '']) .modxcms. 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.com/modx/modX. $treeArray = $modx->getTree(12. Syntax API Doc: http://api.modxcms.com/modx/modX. See Also modX modX.html#getSessionState integer getSessionState () Example $state = $modx->getSessionState().html#getTree array getTree ([int|array $id = null].Returns the state of the SESSION being used by modX.getUser modX::getUser Get the current authenticated User and assigns it to the modX instance. echo $user->get('username').com/modx/modX. eg: 0 patch_level .The current version number.The current release level.modxcms.Example Get the current auth'ed user and print out its username.0-beta-3"): version .html#handleRequest mixed handleRequest () Example Handle the current request. eg: '2. See Also modX modX.0. See Also modX modX. Syntax API Doc: http://api.The entire version name. The array contains the following keys (examples for version "MODx Revolution 2. and process a request made to a modX site.0.A compiled full version name.0-beta-3' full_appname .getVersionData modX::getVersionData Gets the modX core version data. eg: 'beta-3' code_name .html#getVersionData array getVersionData () Example Print out the current full version: $vers = $modx->getVersionData(). cleanse.modxcms.handleRequest modX::handleRequest Initialize. eg: 0 minor_version .The current minor version number.0.0-beta-3' Syntax API Doc: http://api. $user = $modx->getUser(). echo $vers['full_version'].The code name for the product.com/modx/modX. eg: 'Revolution' full_version . eg: 'MODx Revolution 2.The current major version number. eg: 2 major_version . . error handling.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. Syntax API Doc: http://api. Syntax API Doc: http://api. if (!$modx->hasPermission('edit_chunk')) die('Access Denied!'). See Also modX modX.initialize modX::initialize Initializes the modX engine into a Context. pre-loading some common classes and objects.$modx->handleRequest().com/modx/modX. or other initialization classes.html#initialize void initialize ([string $contextKey = 'web']) Example Initialize the 'sports' Context.hasPermission modX::hasPermission Returns true if user has the specified policy permission. See Also Policies modX.modxcms. This includes preparing the session.invokeEvent . See Also Contexts modX. extension packages used to override session handling. $modx->initialize('sports'). the current site and context settings. array('name' => 'John')). and sets the 'name' Placeholder in it. The scheme indicates in what format the URL is generated.com/modx/modX. if the Lexicon has been loaded.lexicon modX::lexicon Grabs a processed Lexicon Entry. This may also be a modLexicon object as well.array( 'id' => $chunk->get('id'). PHP supports having objects and methods with the same name. [array $params = array ()]) Example Invoke the OnChunkRender event: $modx->invokeEvent('OnChunkRender'. echo $modx->lexicon('welcome_message'. -1 : (default value) URL is relative to site_url .modX::invokeEvent Invokes a specified Event with an optional array of parameters.html#invokeEvent void invokeEvent (string $eventName. See Also modX modX. )).makeUrl modX::makeUrl Generates a URL representing a specified resource. Syntax API Doc: http://api. [array $params = array()]) Example Output the translation of the 'welcome_message' Entry. Syntax API Doc: http://api.html#lexicon void lexicon (string $key. See Also Internationalization modX.modxcms.com/modx/modX.modxcms. html#regClientCSS void regClientCSS (string $src) .''.modxcms.parseChunk modX::parseChunk Parse a chunk using an associative array of replacement variables. See Also modX modX. modX. $url = $modx->makeUrl(4).com/modx/modX. array $chunkArr. prepended with base_url from config http : URL is absolute. prepended with site_url from config abs : URL is absolute. [string $context = '']. Make a URL for the Resource with ID 12. [mixed $scheme = -1]) Examples Make a URL for the Resource with ID 4.modxcms.html#parseChunk string parseChunk (string $chunkName.modxcms.html#makeUrl string makeUrl (integer $id. Syntax API Doc: http://api. Syntax API Doc: http://api. $url = $modx->makeUrl(12. forced to http scheme https : URL is absolute.getChunk modX.com/modx/modX. [string $prefix = '[[+'].'https').''.0 : see http 1 : see https full : URL is absolute.regClientCSS modX::regClientCSS Register CSS to be injected inside the HEAD tag of a resource. but make sure it's in HTTPS.com/modx/modX. [string $args = '']. forced to https scheme Syntax API Doc: http://api.array('name' => 'John')). [string $suffix = ']]']) Example $output = $modx->parseChunk('myChunk'. com/modx/modX.modxcms. Syntax API Doc: http://api.modxcms.getRegisteredClientStartupScripts modX. Syntax API Doc: http://api.regClientHTMLBlock modX.regClientStartupScript modX. See Also modX modX.regClientHTMLBlock modX::regClientHTMLBlock Register HTML to be injected before the closing BODY tag.css').getRegisteredClientScripts modX. $modx->regClientHTMLBlock('<div id="footer">(c) 2009 MODx</div>').regClientScript modX.getRegisteredClientScripts modX.regClientStartupHTMLBlock modX.regClientCSS modX.html#regClientScript void regClientScript (string $src.com/modx/modX.getRegisteredClientStartupScripts modX.Example Register a CSS file to the HEAD tag: $modx->regClientCSS('assets/css/style. [boolean $plaintext = false]) Example .regClientStartupHTMLBlock modX.regClientStartupScript modX.html#regClientHTMLBlock void regClientHTMLBlock (string $html) Example Inject a footer into the page. See Also modX modX.regClientScript modX::regClientScript Register JavaScript to be injected before the closing BODY tag.regClientScript modX. $modx->regClientStartupHTMLBlock('<tag></tag>').Add some JS to the end of the page.getRegisteredClientScripts modX.regClientHTMLBlock modX.regClientStartupScript modX.html#regClientStartupScript void regClientStartupScript (string $src.regClientStartupHTMLBlock modX.regClientHTMLBlock modX.regClientCSS modX.com/modx/modX.modxcms.getRegisteredClientStartupScripts modX.regClientStartupHTMLBlock modX::regClientStartupHTMLBlock Register HTML to be injected before the closing HEAD tag. $modx->regClientScript('assets/js/footer.com/modx/modX. See Also modX modX.regClientStartupScript modX.regClientStartupScript modX::regClientStartupScript Register JavaScript to be injected inside the HEAD tag of a resource.regClientCSS modX. [boolean $plaintext = false]) Example Register some JS to the start of the page: .regClientScript modX. See Also modX modX.modxcms.js').html#regClientStartupHTMLBlock void regClientStartupHTMLBlock (string $html) Example Render a faux tag element before the end of the HEAD.getRegisteredClientScripts modX.getRegisteredClientStartupScripts modX. Syntax API Doc: http://api. Syntax API Doc: http://api. getRegisteredClientStartupScripts modX. Useful in cases where you've loaded some Settings dynamically.html#removeAllEventListener void removeAllEventListener () Example Eliminate any other events from firing: $modx->removeAllEventListener().com/modx/modX. Syntax API Doc: http://api. See Also modX .$modx->regClientStartupScript('assets/js/onload.modxcms.getRegisteredClientScripts modX.modxcms.html#reloadConfig array reloadConfig () Example $modx->reloadConfig().regClientStartupHTMLBlock modX. Syntax API Doc: http://api. See Also modX Settings System Settings modX.regClientCSS modX.com/modx/modX.regClientHTMLBlock modX. See Also modX modX.reloadConfig modX::reloadConfig Reload the config settings.regClientScript modX.js').removeAllEventListener modX::removeAllEventListener Remove all registered events for the current request. echo $output.modxcms. Syntax API Doc: http://api. The parameter 'type' can be any field. // prints 'Welcome John!' See Also modX Snippets modX.modxcms. .removeEventListener modX::removeEventListener Remove an event from the eventMap so it will not be invoked. See Also modX modX. and 'fatal'. these are 'unavailable' (the default).sendError modX::sendError Send the user to a type-specific core error page and halt PHP execution. which will load the template file in core/error.html#removeEventListener boolean removeEventListener (string $event) Example Prevent any Events from firing on 'OnChunkRender': $modx->removeEventListener('OnChunkRender').runSnippet modX::runSnippet Process and return the output from a PHP snippet by name.html#runSnippet string runSnippet (string $snippetName.array( 'name' => 'John' )). MODx comes prepackaged with 2 default error pages. Syntax API Doc: http://api. [array $params = array ()]) Example Run the 'Welcome' snippet with some custom parameters: $output = $modx->runSnippet('Welcome'.com/modx/modX.modX.com/modx/modX. See Also modX modX.com/modx/modX.sendRedirect modX.modxcms. sends to a 404 Error page. Syntax API Doc: http://api. [array $options = array()]) Examples Send an Unavailable 503 error page.sendForward modX. See Also modX modX.sendForward modX::sendForward Forwards the request to another resource without changing the URL.modxcms. $modx->sendErrorPage().sendUnauthorizedPage modX.sendErrorPage modX::sendErrorPage Send the user to a MODx virtual error page. If the ID provided does not exist.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.html#sendError void sendError ([string $type = '']. Send a Fatal 500 error page. $modx->sendError('unavailable').html#sendForward . Syntax API Doc: http://api.com/modx/modX. $modx->sendError('fatal'). sendRedirect modX::sendRedirect Sends a redirect to the specified URL using the specified method.com/modx/modX.void sendForward (integer $id.REDIRECT_META). . Send a redirection request to modxcms. See Also modX modX. [integer $count_attempts = 0]. $url = $modx->makeUrl(54). [string $type = '']) Examples Send a redirection request to the Resource with ID 54. $modx->sendForward(234). [string $options = null]) Example Send the user to Resource ID 234 without actually changing the URL. Do so via the META HTTP-EQUIV refresh tag.com. $modx->sendRedirect($url).html#sendRedirect void sendRedirect (string $url.0.Uses the header location method REDIRECT_HEADER is the default.sendRedirect modX.sendErrorPage modX. Valid $type values include: REDIRECT_REFRESH . See Also modX modX.Uses the header refresh method REDIRECT_META .com'.Sends a a META HTTP-EQUIV="Refresh" tag to the output REDIRECT_HEADER .sendErrorPage modX.sendForward modX.modxcms. Syntax API Doc: http://api.sendUnauthorizedPage modX::sendUnauthorizedPage Send the user to the MODx unauthorized page. $modx->sendRedirect('http://modxcms. See Also modX modX.Syntax API Doc: http://api. Be aware that switching contexts does not allow custom session handling classes to be loaded.modxcms.sendRedirect modX.html#switchContext boolean switchContext (string $contextKey) .modxcms.html#sendUnauthorizedPage void sendUnauthorizedPage ([array $options = null]) Example Send the user to the unauth page.setDebug Page: modX. Syntax API Doc: http://api.setDebug modX::setDebug Sets the debugging features of the modX instance.com/modx/modX.switchContext modX::switchContext Switches the primary Context for the modX instance.sendForward modX.setDebug modX. The gateway defines the session handling that is applied to a single request. [boolean $stopOnNotice = false]) Example Turn debug mode on.html#setDebug boolean|int setDebug ([boolean|int $debug = true].modxcms.sendErrorPage modX. and tell the process to stop if Notices occur: $modx->setDebug(true).com/modx/modX. See Also Page: modX. 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.com/modx/modX. $modx->sendUnauthorizedPage(). $modx->switchContext('sports'). mixed $value. Syntax API Doc: http://api.toPlaceholders modX::toPlaceholders Sets placeholders from values stored in arrays and objects.modxcms.setPlaceholders modX.com/modx/modX.getPlaceholder modX.toPlaceholders modX. building an access path using an optional separator. See Also modX. Each recursive level adds to the prefix.html#toPlaceholder void toPlaceholder (string $key.'my').'John'. [string $prefix = ''].modxcms.setPlaceholder modX.Example Switch to the 'sports' Context.com/modx/modX. [string $prefix = '']. See Also Contexts modX.']) Example Set an array of placeholders and prefix with 'my.toPlaceholder modX::toPlaceholder Recursively validates and sets placeholders appropriate to the value type passed.']) Example Set a placeholder and prefix its key with 'my.' . [string $separator = '. [string $separator = '. Syntax API Doc: http://api.html#toPlaceholders void toPlaceholders (array|object $subject.' $modx->toPlaceholder('name'. 'email' => '[email protected] modX.toPlaceholders modX.unsetPlaceholders modX.toPlaceholder modX.com/modx/modX.unsetPlaceholder modX::unsetPlaceholder Unsets a placeholder value by key.toPlaceholder modX.setPlaceholder modX. See Also modX. Syntax API Doc: http://api.unsetPlaceholders modX::unsetPlaceholders Unset multiple placeholders. Syntax API Doc: http://api.modxcms.html#unsetPlaceholder void unsetPlaceholder (string $key) Example $modx->unsetPlaceholder('myPlaceholder').modxcms.setPlaceholders modX.getPlaceholder modX.name' and 'my.' .getPlaceholder modX.'my.email' Placeholders. either by prefix or an array of keys. ).setPlaceholder modX. Unset all Placeholders that are prefixed with 'my.'my').$modx->toPlaceholders(array( 'name' => 'John'.email')).com/modx/modX. See Also modX. $modx->unsetPlaceholders(array('my.name'.html#unsetPlaceholders void unsetPlaceholders (string|array $keys) Example Unset the 'my.com'. setContent Page: modChunk Page: (at)CHUNK Page: modChunk Page: modChunk.com/modx/modChunk.getContent modChunk::getContent Get the source content of this chunk. Syntax API Doc: http://api.html#getContent void getContent ([ $options = array()]) Example $chunk = $modx->getObject('modChunk'.getContent Page: modChunk. Methods See Also Page: (at)CHUNK Page: modChunk. } See Also Page: (at)CHUNK .setContent modChunk.$modx->unsetPlaceholders('my.unsetPlaceholder modX.setPlaceholder modX.modxcms.toPlaceholders modChunk The modChunk Class This is the Chunk base class for MODx Revolution. See Also modX. if ($chunk) { $content = $chunk->getContent().getContent Page: modChunk.toPlaceholder modX.array('name' => 'MyChunk')).').setPlaceholders modX. See Also Page: (at)CHUNK Page: modChunk.com/modx/modChunk. Syntax API Doc: http://api.getContent Page: modChunk.Page: modChunk.getContent Page: modChunk. [ $options = array()]) Example $chunk->setContent('<h2>Hello!</h2>').setContent modUser The modUser Class This is the base User class for MODx Revolution.modxcms.getContent Page: modChunk. Methods See Also Page: Users Page: modUser Page: Users .setContent Page: modChunk Page: (at)CHUNK Page: modChunk Page: modChunk.setContent modChunk::setContent Sets the content of this Chunk.html# void setContent ( $content.setContent modChunk.setContent Page: modChunk Page: (at)CHUNK Page: modChunk Page: modChunk.getContent Page: modChunk. See Also Page: Users Page: modUser Page: Users Page: modUser .com/modx/modUser.modxcms. deny access and send to Unauthorized Page.Page: modUser modUser.modxcms. if (!$modx->isAuthenticated('web')) { $modx->sendUnauthorizedPage().addSessionContext modUser::addSessionContext Adds a new context to the user session context array. Syntax API Doc: http://api.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). } See Also Page: Users Page: modUser Page: Users Page: modUser modUser.com/modx/modUser.html#addSessionContext void addSessionContext (string $context) Example Add a 'sports' Context session to the user. If not.html#isAuthenticated boolean isAuthenticated ([string $sessionContext = 'web']) Example See if the User is logged into the 'web' context. $modx->addSessionContext('sports'). Syntax API Doc: http://api. getSessionContexts modUser::getSessionContexts Returns an array of user session context keys. $user->endSession().com/modx/modUser.html#endSession void endSession () Example End the user's session.com/modx/modUser. See Also modUser modUser.html#changePassword boolean changePassword (string $newPassword.modxcms. Syntax API Doc: http://api.changePassword modUser::changePassword Change the User password. including all contexts. 'boo123'). .modUser. See Also Page: Users Page: modUser Page: Users Page: modUser modUser.modxcms. string $oldPassword) Example Change the password from 'boo123' to 'b33r4me' $modx->changePassword('b33r4me'.endSession modUser::endSession Ends a user session completely. Syntax API Doc: http://api. // prints Array ( 'web'.getSettings modUser::getSettings Gets all user settings in array format.modxcms. is "logged into" a certain context. Syntax API Doc: http://api. See Also modUser Settings modUser. print_r($keys). Syntax API Doc: http://api.com/modx/modUser.html#getSettings array getSettings () Example Get all the User Settings for this User.com/modx/modUser.hasSessionContext modUser::hasSessionContext Checks if the user has a specific session context.com/modx/modUser. $settings = $user->getSettings().modxcms.Syntax API Doc: http://api.modxcms. or in other words. 'mgr' ).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(). See Also modUser modUser.html#hasSessionContext boolean hasSessionContext (mixed $context) Example See if the User has a Session for the 'sports' Context: . com/modx/modUser.'Investors')).modxcms. See Also modUser modUser. You may specify either a string name of the group.isMember modUser::isMember States whether a user is a member of a group or groups.'sports'. $user->isMember(array('Staff'. See Also . Syntax API Doc: http://api.if ($user->hasSessionContext('sports')) { // do code here } See Also modUser Contexts modUser. Syntax API Doc: http://api.true). See if the User is a member of either the 'Staff' or 'Investors' groups. [ $reload = false]) Example Load attributes for the 'sports' context and the modResource target.com/modx/modUser. [ $context = ''].loadAttributes modUser::loadAttributes Loads the principal attributes that define a modUser security profile.modxcms.html#isMember boolean isMember (mixed $groups) Example See if the User is a member of the 'Staff' group: $user->isMember('Staff').html#loadAttributes void loadAttributes ( $target. $user->loadAttributes('modResource'. or an array of names. removeSessionContextVars modUser::removeSessionContextVars Removes the session vars associated with a specific context.removeSessionCookie modUser::removeSessionCookie Removes a session cookie for a user.com/modx/modUser.html#removeSessionContextVars void removeSessionContextVars (string $context) Example Remove all session vars for the User in the 'sports' Context.modxcms. See Also modUser Contexts modUser.modUser modUser.removeSessionContext modUser::removeSessionContext Removes a user session context. Syntax API Doc: http://api. Syntax . See Also modUser Contexts modUser.modxcms.com/modx/modUser. Syntax API Doc: http://api.html#removeSessionContext void removeSessionContext (string|array $context) Example Remove the session for the User in the 'sports' Context. $user->removeSessionContext('sports'). $user->removeSessionContextVars('sports'). You've got some data that uses a table in your MODx database. done in Revolution setup. and you've run into a dilemma.) 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 . This tutorial will walk you through the process of creating your own custom schema. Currently we'll have one table for this: (note the prefix "modx_" .html#removeSessionCookie void removeSessionCookie (string $context) Example Remove the Session Cookie for the User in the 'sports' Context. 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. 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. adding it as an xPDO model package.modxcms. 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. but you want a way to use xPDO's object model to access it.this is specific to your DB connection. $user->removeSessionCookie('sports'). and querying it.API Doc: http://api.com/modx/modUser. which are array representations of the schema and its relationships. let's make the schema file that defines the model. and put some attributes into it. platform .The name of the xPDO package (note this is different than a "transport package". baseClass . xPDO only supports mysql. we'll create a file called "storefinder. Here. Let's add a table tag as the next line. But for now.yes. we'll use "sfStore".schema. we're going to do this in MySQL. Note that "mysql" is in there . it's best to leave it at the default. Great! Now we've got our model definition.These are custom attributes we're going to use in our map and class files. Note that instead of just "Store". let's create a "schema" subfolder. defaultEngine . <object class="sfStore" table="sfinder_stores" extends="xPDOSimpleObject"> "Object" is our representation of a table. They are: package . let's start out with the first few lines: <?xml version="1. Unless you're planning on creating a custom xPDOObject extension. At this time.This is the base class from which all your class definitions will extend. This isn't always how you have to do it .The default engine of the database tables. They're not standard xPDO attributes.This is the name of the class we want to be generated from the table. Then. from there. especially with the core/components/storefinder/ structures. .this one is specifically built this way for SVN. Next. on to the model file.So now that we've got an idea of what's in our tables. eventually xPDO and Revolution will support other database platforms. MODx recommends using MyISAM. which will generate into an xPDOObject class when we're through. we prefixed it with "sf" to prevent collisions with any other packages we might install that might also have Store tables. In our XML file. let's take a quick look at our directory structure.xml". In our _build directory. phpdoc-package & phpdoc-subpackage . but show that you can put whatever you want as attributes. usually either MyISAM or InnoDB.The database platform you're using.mysql. but it's definitely recommended. This is how xPDO separates different models and manages them. This "schema" file is an XML representation of our database table(s). we're going to create a "model" tag. a Revolution term). since that makes creating the transport package (should we want to distribute this via Package Management) much easier. There are some attributes to note here: class . Our Model First off. Now. It is then parsed by xPDO into PHP-format "maps".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. dbtype .This should point to the actual database table name. You'll see here that this table extends "xPDOSimpleObject". Some of those attribute properties are: key . If you moved the manager or core outside of that base path. Extended classes will inherit their parent class's fields.An optional field. define('MODX_MANAGER_PATH'. index . you'll need to adjust those defines as well.such as varchar. minus the prefix... define('MODX_ASSETS_PATH'. Some of the values are "pk". 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.The DB type . and "fk".php' file in your _build directory. attributes . MODX_BASE_PATH .The default starting value of the field should none be set. each column in our table has a field definition tag. It should contain this: <?php define('MODX_BASE_PATH'. will add a type of index to the field. precision . MODX_BASE_PATH . depending on the database type of the column. Usually this is the max number of characters.schema. rather than xPDOObject. MODX_BASE_PATH . . 'assets/'). 'manager/'). default . that is an auto-increment primary key. 'core/'). null . create a 'build. Let's move on to the schema build script. Note that you can make subclasses and extended classes straight from the XML. Most of these are optional. 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.Only applies to some DB types. when set. in integers you can set to "unsigned" to make sure that the value is always positive. '/MODxRevolution/'). we have attribute properties for each field. "index". extends . From there.If the field can be NULL or not. 'connectors/'). put this: . dirname(dirname(dirname(dirname(dirname(__FILE__))))) . MODX_BASE_PATH .The corresponding PHP type of the DB field type.config. Now that we've got a table definition for our stores table. Building the Schema Go ahead and create a 'build. At the top. This means that the table comes already with an "id" field. define('MODX_CONNECTORS_PATH'.php' file in your _build directory. tinyint.table . etc.This is the class that it extends. From here.The precision of the field. int.The key name of the column. text. phptype . define('MODX_CORE_PATH'.where obviously MODX_BASE_PATH will need to point to where you installed MODx Revolution. . Secondly. $mtime= $mtime[1] + $mtime[0].php file to tell the schema script where to find MODx Revolution.'storefinder.'assets/components/storefinder/'. This block of code executes the schema parsing method. These lines will load xPDOManager and xPDOGenerator. two classes we'll need to build our schema map files.false. $modx->setLogTarget(XPDO_CLI_MODE ? 'ECHO' : 'HTML'). '/build.php' -. First off. and then outputs the total time the script took to execute. set_time_limit(0).php'. $mtime). exit (). $modx->setLogLevel(modX::LOG_LEVEL_INFO). It includes our build. so we can see how long it takes to build the schema. include_once MODX_CORE_PATH . $tend= $mtime.'core/components/storefinder/model/'. 'model' => $root. $mtime = $mtime[1] + $mtime[0]. echo "\nExecution time: {$totalTime}\n". and we want a file on the file system for our build package.mysql. $totalTime). 'core' => $root. require_once dirname(__FILE__) . $totalTime= sprintf("%2. that's great. $generator= $manager->getGenerator(). And finally. true). otherwise your script wont run. we want to actually parse this into a file: $generator->parseSchema($sources['schema']. ).'_build/schema/'. 'schema' => $root. Thirdly. $modx->loadClass('transport. it starts up a nice execution time script for us.we're going to tie into a file on the file system because it's easier to edit it using a text editor. Let's first create our snippet file in our core/components/storefinder/ directory. initializes the modX object. $modx->initialize('mgr').''. Let's add a couple more lines to our schema build script: $manager= $modx->getManager().php'. $mtime).<?php /** * Build Schema script * * @package storefinder * @subpackage build */ $mtime = microtime(). Note that you'll want to make sure the $sources array has the correct paths defined. 'model/modx/modx. and call it 'snippet. 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. it sets some log levels and some base paths for our build script. $mtime= explode(" ".config. 'assets' => $root. $mtime= microtime(). it loads the necessary classes.schema. $sources = array( 'root' => $root.$tstart).'/'. "Okay.class.4f s". This will do a few things. Run it. Now how do I use these?" Well. $root = dirname(dirname(__FILE__)).'core/components/storefinder/'.storefinder. $sources['model']).xml'. $tstart = $mtime. $totalTime= ($tend . xPDO makes it incredibly simple.modPackageBuilder'. $mtime = explode(" ".config. And finally. $modx= new modX(). and loads the modPackageBuilder class. Above the getCollection call. After running. if (file_exists($f)) { $o = include $f.'/MODx Components/tutorials/storefinder/trunk/core/components/storefinder/'. } return $o.php'. it might throw an error. If you don't want to build a transport package (we recommend doing so. This will add the package to xPDO. echo 'Total: '. You've got your snippet working. You'll see here that we're setting a $base_path variable if and only if it's not already set. 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. Why? Well.$f). you're in an easy development environment. it makes upgrades FAR easier!).php' in your editor. This is because xPDO is dynamically creating your database table from your schema. add: . and install our package. this allows us to do development outside our target directory (like we're doing now). You can find more about building packages by going here.Before we proceed. let's enable testing of this snippet directly from MODx.'StoreFinder not found at: '. $f = $base_path.storefinder. First off. 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->log(modX::LOG_LEVEL_ERROR. then we simply point it to where the component will be installed: core/components/storefinder/ Now on to the brunt of the code.$base_path.'components/storefinder/'. Since we're developing this in a separate directory from our MODx install. Let's add a few records into the database for testing. it should show "Total: 0". Note the first time you run this.storefinder. } else { $modx->setLogTarget('ECHO'). and allow you to use all of xPDO's functions with your model. you can simply just copy the files to their proper directories in the manager. back to our snippet. /* change above line to your path */ $o = ''. let's create a snippet called 'StoreFinder' in our MODx Revolution instance. Open up 'snippet. Then we'll simply delete this 'StoreFinder' snippet from our MODx Revolution instance. Let's test it out: $stores = $modx->getCollection('sfStore'). and add this code: <?php /** * @package storefinder */ $base_path = !empty($base_path) ? $base_path : $modx->getOption('core_path').'model/'). add these lines: $modx->addPackage('storefinder'.count($stores). and now you're ready to get that model working. If no base_path is set. Okay.'snippet. )). . powerful blogging solution in MODx Revolution. 'city' => 'London'. You can stop reading if you want. 'country' => 'England'. 'address' => '12 Grimmauld Place'. $store->save().com/docs/display/revolution/PHP+Coding+in+MODx+Revolution%2C+Pt. After you've run it. and then output 'Total: 2'. 'phone' => '555-2134-543'. 'zip' => '53491'. 'zip' => '12345'. Since MODx Revolution is not blogging software. the second part of this tutorial will be dealing with finishing our Component's scenario. Part 2 Coming Soon.+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.. 'address' => '4 Privet Drive'. That should populate your table with some data. go ahead and erase those lines. 'city' => 'London'. assuming you didn't remove the getCollection lines. $store->fromArray(array( 'name' => 'Store 2'.$store = $modx->newObject('sfStore'). now we've got our model running smoothly! For those of you who are already familiar with component development. $store->fromArray(array( 'name' => 'Store 1'. Okay. $store->save(). Run this only once (unless you want duplicate data).modxcms. 'country' => 'England'. $store = $modx->newObject('sfStore').. See Also Generating the xPDO Model Code http://svn. 'phone' => '555-2011-978'. )). pages and other Resources. Getting the Needed Extras First off. again.For adding a simple search box to your site. If you'd like a demo before reading. It's recommended that you're familiar with Revolution's Tag Syntax before we start.For managing photo Galleries. The following is a list of most commonly-used Extras: Needed Extras getResources . You'll need to setup your blog how you want it.but rather a full-blown Content Application Platform. it doesn't come pre-packaged with a cookie-cutter blogging solution. such as a Twitter feed.For displaying a breadcrumb navigation trail. you'll need this.we'll reference that later on as 'BaseTemplate'. One thing before we start. Gallery .For anything and everything in commenting. Our content looks something like this: . you'll want to have a Template that's geared just for Blog Posts.com. Fortunately. and will show you how to set up a powerful blog with posting. Archivist . tagging. try there. Quip . We'll create one called 'BlogPostTemplate'. MODx is modular. If you don't need any specific part.For listing posts. Why? Well. the best route is to setup your own blog post template.For managing tags and doing tag-based navigation. So. and your blog can function in any scope you like. getFeed .For managing your Archives section. the tools to do so are already there for your taking. This tutorial already assumes you have a base Template for your normal pages on the site . though . Login . This tutorial will walk you through how to set them up. Creating your Blog Post Template First off. getPage .this tutorial is extensive. SimpleSearch . Optional Extras Breadcrumbs .For pagination of listings. And. archiving. this is only one way to do it . if you want comments and special formatting or page displays for your blog.there are tons of ways to setup a blog in MODx Revolution. you'll probably not want to have to do that for each Blog Post. commenting and more. you'll want to go ahead and download and install some Extras that we'll be using in our Blog. just skip that part.If you want to restrict commenting to logged in users only. tagLister .If you want to grab other feeds in your site. This tutorial was based on the blog setup at splittingred. I can just do it in one chunk. so I can use them in different templates. This is solely a base structure . move them there! MODx doesn't restrict you from doing that.[[$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. Header and Footer First off. and make it a link that takes you to the same page. %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> . for instance.you can move any of these pieces around. Let's look in detail: <p class="post-info"> Posted on [[*publishedon:strtotime:date=`%b %d. I put the pagetitle of my Resource.com` &moderate=`1` &moderatorGroup=`Moderators` &closeAfter=`30` ]] </div> </div> [[$pageFooter]] So let's examine the Template. After that. These chunks contain my common HTML tags that I would put in the footer and header across my site. shall we? As we go. Useful if I don't want to have to update any header/footer changes in each of my Templates .if you want your tags at the bottom.basically the author and tags for the post. change their parameters. %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. remember this . you'll notice that I have two chunks: "pageHeader" and "pageFooter". and adjust their placing. The Post Info Next we get into the "info" of the post . we want to specify . Our comments are going to be threaded (the default). Adding Comments to Posts Okay. This tells Quip to automatically close commenting on these threads after 30 days of the thread creation (when we load it). For this tutorial. The tolinks snippet comes with tagLister. . Neat! The Post Content Okay. Next. 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. In our Quip snippet call. And finally. 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. which you can find out how to do in the Quip docs. now we're in the comments part of BlogPostTemplate. that will show up on our main page when we're listing the latest blog posts. Note we have two Snippet calls here . We're in the content section now . Note how our 'thread' property in the QuipCount snippet call (and later on in the Quip call) uses 'blog-post-[*id]'.we haven't created this just yet.The first part takes the publishedon Resource field. You could feel free to use another system.note how we start with [[*introtext]].a 'closeAfter' property. but we'll leave you to further customization." Next. or our home page. along with a clickable anchor tag link to load them.think of it like a beginning excerpt for a blog post. This is a useful MODx Resource field . we load a quick count of the number of comments. In our QuipReply call. cool. make sure it has access to the 'BlogPostTemplate' Template we created earlier. This means our tags become clickable! We've specified a 'target' Resource of 1. we'll go with Quip. such as Disqus. so dont worry . and another for displaying the reply form (QuipReply).one for displaying the comments for this thread ( Quip). You can see how we reference a "tags" Template Variable . and translates delimited tags into links. This means that MODx will automatically create a new thread for each new Blog Post we create. and give it a description of "Comma delimited tags for the current Resource. 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). you'd change the ID number there. There's a whole bunch of other Quip settings we could change. we then display a Tag listing for this Blog Post. we're using Quip for our commenting system.in both the Quip and Quip Reply calls .) with the 'replyResourceId' property. pretty date. Setting up Tagging Now that we've got our Template all setup. back to our Template. We recommend reading there for how to set it up. Secondly. here if you choose. and then set some settings. we need to setup the 'tags' Template Variable that we'll be using for our Tagging. we've specified a thread ID in the manner we've described above. As you can see here.and then pass it as a property to the 'tolinks' snippet. If your blog was in another page besides home. and formats it into a nice. Go ahead and create a TV called 'tags'. we want to tell Quip to moderate all posts. That's it! Now you'll be able to add tags to any blog post we create. Make sure you don't use the BlogPostTemplate on these. and use instead your own Base Template. for reference. go ahead and put the following: [[!getResourcesTag? &element=`getResources` &elementClass=`modSnippet` &tpl=`blogPost` &hideContainers=`1` &pageVarKey=`page` &parents=`[[*id]]` &includeTVs=`1` &includeContent=`1` ]] [[!+page. For this tutorial's purpose. we want to grab all published Resources within this section (and we can also filter by tag should we pass a '?tag=TagName' parameter . we'll create 2 sections: "Personal" and "Technology". getResourcesTag a wrapper snippet for getResources and getPage that automatically filters results by a 'tags' TV. so your blog post URLs turn up nicely.nav]] </ul> </div> `]] Okay. We'll say from here on out that our two Section Resources have IDs of 34 and 35. simply when editing your Resource by specifying a comma-separated list of tags. So basically. Creating the Sections If you want your blog to have 'Sections' (also called Categories). you'll first need to create those Resources. and make them 'containers'. Go ahead and create 2 Resources in the root of your site. You'll want to have their alias be 'personal' and 'technology'. In the content of these Resources. These pages will end up being a way to browse all posts within a certain Section.nav:notempty=` <div class="paging"> <ul class="pageList"> [[!+page. let's explain this. Setting up the blogPost Chunk In that call. go ahead and instead just grab the first 400 characters of the content field.which we store in the 'introtext' field on the content.) to it if it's more than 400. This is our Chunk that shows each result of our blog post listings. Below the getResourcesTag call. since by default getResourcesTag only shows 10 posts per page. which we've got in Resource ID 1 .. and then our comments and publishedon date. we also have a property called 'tpl' which we set to 'blogPost'. Then. we put our pagination links. and add an ellipsis (. We start out by making a clickable link to the post with the pagetitle as the title.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. we have a nice little 'read more' link which links to the post.tags:notempty=` | <span class="tags">Tags: [[!tolinks? &items=`[[+tv.our site start . Next.into the URL. That's it! Setting up Your Blog Home In our home page for our blog. we set our 'posted by' part and tag listing (similar to how we did it earlier in BlogPostTemplate). After that.we've got this: . %Y`]]</span> </span> </p> </div> Cool .let's dive in. We're also going to say that if introtext is empty. we show some of the excerpt of the content . 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.. now that our structure is all setup. and give it an alias of 'archives'. make sure they have Hide from Menus checked.35` &limit=`5` &includeContent=`1` &includeTVs=`1` &showHidden=`0` &hideContainers=`1` &cache=`0` &pageVarKey=`page` ]] [[!+page. we have automatic tagging! You could easily make this another page than your site_start (or ID 1) .[[!getResourcesTag? &elementClass=`modSnippet` &element=`getResources` &tpl=`blogPost` &parents=`34. It's totally up to you. you've got it so you can browse it in Sections as well. now we're ready to actually add blog posts. and set it's Template to 'BlogPostTemplate'.nav]] </ul> </div> `]] This allows us to show all posts from the two sections we've made. place this: . In other words. it's important to note that how you structure your posts within the section is totally up to you. Adding a New Blog Post Okay . you're going to want to set up some way of browsing old blog posts. by putting our getResourcesTag call here. Adding Posts Okay. This is where 'Archvist' comes into play. It also allows us to filter by tag (since all our 'tolinks' and 'tagLister' calls have a target of 1 (this Resource's ID). is the URL of your blog posts. though. or just post them directly within the section. And finally. Then inside the content. when you're done. If you choose to have date/year or sub-containers. make sure to specify the tags for your post in your newly created 'tags' TV! Setting up Your Archives Great . so that they wont show up in your getResources calls. and then write the full body in the content field. You can specify in the 'introtext' field the excerpt for the blog post. Remember. So have fun. You can add year and month container Resources to put these posts in.you have your first blog post! And. that whatever structure you build under the sections. though.just make sure to change the 'target' properties in your tagLister and tolinks Snippet calls to reflect that. Creating the Archives Resource Go ahead and place a Resource in your root called 'Archives'. Then you can start writing your post.go ahead and create a new Resource.nav:notempty=` <div class="paging"> <ul class="pageList"> [[!+page. Now. Page Structure Within the Sections Before we start. What it will determine.Archivist will handle that. however. in Resources 34 and 35. that's not going to determine your navigation . we add a few placeholders that show the current browsing month and year. Setting up the Archivist Widget Okay. where archives/2009/ will show all posts in 2009.[[!getPage? &element=`getArchives` &elementClass=`modSnippet` &tpl=`blogPost` &hideContainers=`1` &pageVarKey=`page` &parents=`34. or click on links directly in the emails to approve or reject the comments. We'll set the result to a placeholder called 'archives' which we reference later.35` &includeTVs=`1` &toPlaceholder=`archives` &limit=`10` &cache=`0` ]] <h3>[[+arc_month_name]] [[+arc_year]] Archives</h3> [[+archives]] [[!+page. for reference purposes. Pretty sweet. in our QuipReply call. basically) that gives this user group access in the 'mgr' context. we have our pagination. and create a new User Group called 'Moderators'. We are saying we want its links to go to our Archives Resource (30). so now you've got a Resource to browse archives in. but see it's documentation for that). Then. Let's go ahead and create that User Group now. They can then either login to the manager to moderate comments. And finally. and to only grab posts in the Resources 34 and 35 (our Section Resources). and saying that we want to grab posts in Resources 34 and 35 (our Section pages). huh? Advanced Options Adding a Moderator Group So earlier. Your ACL should look something like this: . described above in our Section page. and also notifies them via email when new posts are made. Our Resource.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. and the Access Policy of 'QuipModeratorPolicy'. we specified a moderatorGroup of 'Moderators'. What this does is allow anyone in the 'Moderators' usergroup to moderate posts in your threads. below that.somewhere on your site (say. This time. go to the Context Access tab. with a minimum role of Member (9999). That's actually pretty simple . Cool! We're done with that.nav]] </ul> </div> `]] Look familiar? It's very similar to getResourcesTag. Add an ACL (a row.archives/2010/05/ will show all the posts within May 2010. Go to Security -> Access Controls. That's it! Archivist will actually automatically handle the rest . in your footer. Then. Add any users you want in the group (including yourself!) and give them whatever role you want.including all your URL generation for archives . getPage is wrapping the getArchives snippet. but you need some way of generating the months that lists posts. put this nice little bit: <h3>Archives</h3> <ul> [[!Archivist? &target=`30` &parents=`34. we'll say has an ID of 30.nav:notempty=` <div class="paging"> <ul class="pageList"> [[!+page. but Quip will handle the rest.Quip packages a nice little snippet called QuipLatestComments that can handle this easily. create the `latestPostsTpl` chunk. which you've specified with the 'tpl' call in the getResources snippet call.adding it is quite easy.Save your User Group. First off.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.[[+publishedon:strtotime:date=`%b %d. and that's it! You might have to flush sessions (Security -> Flush Sessions) and re-login to reload your permissions. you'll want to place this call wherever you want the list to appear: [[!getResources? &parents=`34. Put this as the chunk's content: <li> <a href="[[~[[+id]]]]">[[+pagetitle]]</a> [[+publishedon:notempty=`<br /> .35). Place the call wherever you want the comment list to show: . and no fear . Adding a "Latest Posts" widget You're probably going to want a "Latest Posts" somewhere on the site. Then. and sort by their publishedon date. %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 . 34 minutes' (or two other time metrics. The result: You can see the documentation for the snippet for more configuration options. such as min/sec. Adding a "Most Used Tags" widget This part is ridiculously easy. year/mo. tagLister does this for you. pretty 'two hours. Secondly. There's a ton more configuration options.[[!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. but we'll leave you with this. there's a few things to note . Resource ID 1) with the top 10 tags being used. and translates a timestamp into a nice.QuipLatestComments will automatically truncate the comment and add an ellipsis past the &bodyLimit property passed into it. Conclusion So we've got a full blog setup! It should look something like this in our tree now: . and create links that go to the target (here. note the 'ago' Output Filter we used here. Note also that it will default to showing the 5 latest. which defaults to 30 characters. Just place this wherever you want: [[!tagLister? &tv=`tags` &target=`1`]] And tagLister will check the TV 'tags'. This filter is built into MODx Revolution. mo/week) format. if ($modx->db->getRecordCount($res)) { while ($row = $modx->db->getRow($res)) { array_push($users. // or even more convoluted $res = $modx->db->select('id.'. Things are much simpler.$table_prefix. $modx->getDocument(45. used to. = $modx->getChunk('chunkName').6/Evolution? Does it support OOP projects? Is it faster? Will it be easy to learn? In these tutorials. an object relational bridge modeling tool built by Jason Coward. "yes. Lets look at some examples: . there's far more customization and things you could add to your blog. we plan to answer those questions with a resounding. tweak and scale any solution: including a blog! Remember.the great part about MODx is that you can very easily customize. how does one actually get an object in the new modx? Well.username'.com. Is it coder-friendly? Will it be a big deviation from 0.modx_manager_users')." The codebase in Revolution has switched to xPDO. } } return $users. you had to do and remember a myriad of different functions: // The $doc = $doc = $chunk old way of doing things in MODx 1. but feel free to customize and add things to your liking . snippets by 'modSnippet' objects and so on. The Simple How So. if you'd like to see a full-scale demo of it in action. I The Simple How The Model See Also So. Pt. PHP Coding in MODx Revolution.x and earlier $modx->getDocument(23). This tutorial is meant as a starting point. In layman's terms. Chunks are represented by 'modChunk' objects. $users = array().'pagetitle. a lot of people have been asking about the new codebase.introtext').9. this means that all the database tables are now represented by PHP objects (which you'd expect with any ORM). and there's really only a few functions you'll need.Again. Not anymore.$row). this tutorial was based off of splittingred. if you were to delete this chunk. 'alias' => 'test'.mysql. An Aggregate relationship is a relationship where. it wouldn't delete the Category that it's related to. then outputting their names $chunks = $modx->getCollection('modChunk'). templates.array( 'published' => 1. and extends shows what object it extends.more on that in a future article.43).snippets. } // getting a resource (i.xml". For example. in laymans terms. foreach ($chunks as $chunk) { echo $chunk->get('name').e."<br />\n". Most of these attributes are pretty straightforward. with a alias of 'test' $document = $modx->getObject('modResource'. chunks. // getting a chunk with name 'TestChunk' $chunk = $modx->getObject('modChunk'. <aggregate alias="modCategory" class="modCategory" key="id" local="category" foreign="id" cardinality= "one" owner="foreign" /> Okay. 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.array( 'name' => 'TestChunk' )). 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 . // getting a collection of chunk objects. Where is the list of table names to object names map? It can be found in "core/model/schema/modx. a page) that is published. 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. The table property shows the actual MySQL table. you're probably asking. There is "dependence" in the Composite . it would. )). The Model So. modElement is a base class for all Elements in MODx .// getting a chunk with ID 43 $chunk = $modx->getObject('modChunk'. If it were a Composite relationship. <field key="name" dbtype="varchar" precision="50" phptype="string" null="false" default="" index=" unique" /> This tag represents a column in the database. this is where we get into DB relationships. (You'll note the 'mysql' . etc.yes.schema. modules. Done. ). as well as more complex queries. let's get all the modContextSettings for a modContext: $context = $modx->getObject('modContext'. Pt. It's important to note. That's it. // now. now we're ready.$setting->get('value'). It will also remove any composite relationships . II Creating Objects Removing an Object More Complex Queries See Also In this article.'). lets save some data into the fields $template->set('templatename'. $template->fromArray($data). we'll talk about creating and removing objects (and their respective rows in the database). } It's that simple.$setting->get('key'). .' <br />'. however. See Also xPDO: Defining a Schema xPDO: Related Objects PHP Coding in MODx Revolution. limits. $template->set('description'.'.with modTemplates. Removing an Object Okay. sorting and others. Yes. foreach ($settings as $setting) { echo 'Setting name: '.' <br />'. 'description' => 'A test template. such as inner joins.'A test template. So. as well as more complex queries.relationship that is related to the other object. let's save. in the next article. // okay. of course).'web'). the code is simply: $template->remove(). on to the code: // let's create a Template $template = $modx->newObject('modTemplate'). which map Templates to TVs. that a row is never actually added to the database until the object's save() command is run. } Pretty easy. these are the modTemplateVarTemplate objects. so assuming we have the same previous object and now want to remove it (you could also grab another object. For an example. if ($template->save() === false) { die('An error occurred while saving!'). Creating Objects Creating objects is pretty simple. $settings = $context->getMany('ContextSettings').'TestTemplate'). echo 'Setting value: '. // we could have also done it like this: $data = array( 'templatename' => 'TestTemplate'. Those will cascade and be removed. huh? We'll get into creating and removing objects. One. and select.you could also have used $modx->getCollectionGraph for this as well. leftJoin. We'll look in-depth at the processor for creating a Chunk.php variable.published' => 1. you can go pretty wild here with complex queries. $c->leftJoin('modUser'. Obviously. Pt. xPDOQuery supports the the methods: join. so obviously you are going to need to do some more complex queries than we've dealt with. $c->limit(10. note that innerJoin first passes the class name. bindGraphNode. asynchronous requests to processors in this method. loading the proper create. You can also do multiple. In the JS. which resolves to (in our default setup): /modx/connectors/element/chunk.php From there the connector will verify the request. that are either 1) published and searchable.null.searchable' => 1. $c = $modx->newQuery('modResource').php. sortby. This allows you to build abstract query objects that emulate more advanced SQL commands. innerJoin. First off. rightJoin. the 3rd parameter is the group number. The nice thing about xPDO in MODx is that there's really a ton of different ways to do most things . then the alias. which handle AJAX requests from the User Interface (UI). )). They are accessed through 'Connectors'.$c). 'modResource.config. Processors are sent the sanitized REQUEST data. and then when finished respond with a JSON message back to the browser. the 3rd in another). lets try to grab the third 10 resources (so 21-30). which has the REQUEST "action" variable set to 'create'. ). or 2) created by the user with username 'george123'. III In MODx. $c->sortby('menuindex'. In the next article.1). So. $resources = $modx->getCollection('modResource'.More Complex Queries Okay.'PublishedBy').php And now on to the processor: . See Also xPDO: Creating Objects xPDOObject::remove xPDOQuery PHP Coding in MODx Revolution. form processing is handled by 'Processors'. limit. ordered by menuindex. andCondition. groupby. $c->orCondition(array( 'PublishedBy. That's where the xPDOQuery object comes in. and show you how MODx processors work.20). $c->where(array( 'modResource. the connector is MODx. A couple of things to note.'ASC'). we'll talk about how this is used in the context of MODx processors with JSON. bindGraph. easy requests that reduce the load on the server and the browser.username' => 'george123'. orCondition. which are isolated files located in the MODx core directory. And in orCondition. let's assume that we're sending the following data into the POST array to the connector. This allows for quick. which require a REQUEST variable named 'action' that specifies which processor to send to. and then send it to the proper processor.connectors_url+'element/chunk. at: /modx/core/model/modx/processors/element/chunk/create. which effectively groups the conditions into proper parenthesis (the first 2 in the first parenthetical group. $_POST['name'] = str_replace('<'. we allow dynamic Category creation. } } Okay.$category->get('id')). The response is a string message translated via the lexicon. which is a term we've coined for 'focus area'. if ($chunk->save() === false) { return $modx->error->failure($modx->lexicon('chunk_err_save')). and checking to make sure there already isn't a Chunk with this name. then it sends a failure response back to the browser via $modx->error->failure(). we load the proper lexicon foci. we include the root index. // invoke OnBeforeChunkFormSave event $modx->invokeEvent('OnBeforeChunkFormSave'. // if the name already exists for this chunk.array( 'mode' => modSystemEvent::MODE_NEW. here. // default values if ($_POST['name'] == '') $_POST['name'] = $modx->lexicon('chunk_untitled'). $chunk = $modx->newObject('modChunk'.''.php file for the processors. If not. This checks to make sure the user has the correct permissions to run this processor.element. // category $category = $modx->getObject('modCategory'.0).<?php /** * @package modx * @subpackage processors. $category->save(). In MODx Revolution.$_POST['chunk']). $chunk->set('category'. 'id' => $_POST['id'].array('id' => $_POST['category'])). if (!$modx->hasPermission('new_chunk')) $modx->error->failure($modx->lexicon('permission_denied')). which does some slight variable checking and includes licensing.''. Events are pretty much the same invoke-wise in Revolution as they were in 096 . // get rid of invalid chars $_POST['name'] = str_replace('>'.$_POST['name']). if ($category == null) { $category = $modx->newObject('modCategory'). } . it will later assign it to that category. If not.isset($_POST['locked'])).$_POST['category']). $_POST). then it creates the category in the database and prepares it for later association to the Chunk. If the category specified exists. if ($name_exists != null) return $modx->error->failure($modx->lexicon('chunk_err_exists_name')). $chunk->set('locked'. )). This saves processing power by only loading relevant i18n strings. } else { $category->set('category'. Note now how we're sanitizing variables.array('name' => $_POST['name'])).$_POST['name']). if (empty($_POST['category'])) { $category->set('id'. send back an error $name_exists = $modx->getObject('modChunk'. $chunk->set('snippet'. Then. First off. i18n language files are separated into smaller files by their 'foci'. we want all language strings with foci 'chunk'. Here.however they are more optimized in their loading.chunk */ $modx->lexicon->load('chunk'). The code would look something like this (pulled from jquery UI's docs): .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.A string message to send back. This will allow the UI to handle the creation properly. $cacheManager= $modx->getCacheManager(). more event invoking.$chunk->get(array('id'. 'category'))).minus the content. This will make sure that we're not loading anything besides the wanted material . how manager actions work in Revolution is a little different.Important: note the 2nd parameter of the newObject() method. 'locked'. The parameters of $modx->error->success() are as follows: 1: $message .it allows you to specify an array of key-value pairs to assign to the new object. Next. This is basically the same as $obj->fromArray() . Let's simply and easily clear the cache. Used to report details about a success (or failure). // log manager action $modx->logManagerAction('chunk_create'. huh? return $modx->error->success(''. $cacheManager->clearCache(). 2: $object . 'id' => $chunk->get('id'). send a success response back to the browser. So basically. and the actual ID of the object. without having to modify the core.An xPDOObject or array of data fields to convert into JSON and send back to the browser.array( 'mode' => modSystemEvent::MODE_NEW. Now. we're sending back the Chunk information . Again. )). Pretty easy. 'description'. This allows for more detailed manager action reporting.$chunk->get('id')). 'name'. the class key of the object being modified. This stores a lexicon string key ('chunk_create'). which could be big and unnecessary and complicated to send. we'll talk about how to create your own schemas and add them dynamically into the MODx framework. How do we do that in MODx? This tutorial will show you just how easy it is to accomplish this in MODx Revolution. Loading Pages in the Front-End via AJAX and jQuery Tabs The Problem Creating the Resources Doing the Front-End Loading Wait. here. 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. 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). Now.'modChunk'. // invoke OnChunkFormSave event $modx->invokeEvent('OnChunkFormSave'. Creating the Resources In the Resources you want to load via the tabs. </script> <div id="tabs"> <ul> [[Wayfinder? &startId=`123` &level=`1` &rowTpl=`myRowTpl`]] </ul> </div> Using a getField Snippet Or. make sure you use the 'tpl' property.link]]" title="[[+wf.tabs().tabs().<script type="text/javascript"> $(function() { $("#tabs"). Wait. one.tabs(). which you can create as a Chunk named 'myRowTpl' (or whatever you want). }).title]]">[[+wf.id]][[+wf.linktext]]</a></li> and in our tabs page: <script type="text/javascript"> $(function() { $("#tabs"). make sure your rowTpl template. }).classes]]><a href="[[+wf. or use a getField snippet. I want the Page Titles as the tab headers! There are a few ways you can do this. </script> <div id="tabs"> <ul> [[getResources? &parents=`123` &depth=`1` &tpl=`myRowTpl` &includeContent=`1` &includeTVs=`1`]] </ul> </div> Using Wayfinder For Wayfinder. Using getResources For getResources. you can use getResources. you can use a Snippet such as this one to grab the pagetitle: . 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"). Wayfinder. </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. }). looks like this: <li[[+wf. which you can create as a Chunk named 'myRowTpl' (or whatever you want). This will successfully load your MODx Resources into jQuery tabs. however. ?> Call this Snippet getField like so in our tabs page: <script type="text/javascript"> $(function() { $("#tabs"). $field = $modx->getOption('field'. } /* return the field value */ return $resource->get($field). the solution in MODx Revolution is quite simple. stores its data in the database.'pagetitle'). } else { /* if no id specified. Managing Resources and Elements via SVN The Problem When working in collaboration. since it has to make a query every tab.$id). However. use current doc */ $resource =& $modx->resource. . it's simple. Just use Static Resources. MODx.false). The code: if (!file_exists($file)) return ''. the getField solution is not as fast or elegant as the Wayfinder solution. but DB-stored code cannot be version-controlled via SVN. if ($id) { /* grab the resource object */ $resource = $modx->getObject('modResource'. </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. return $o. The Solution For Resources. if ($resource == null) return ''. For Elements. teams of developers and designers often collaborate via Subversion (SVN) to make development easier between multiple people. 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.<?php /** * Grabs a field for a specified Resource */ /* setup some default properties */ $id = $modx->getOption('id'. This has many benefits generally.tabs(). $o = include $file.$scriptProperties. and point the content to a file in your SVN checkout. all you need is a simple "include" snippet.$scriptProperties. }). Conclusion Note that all you're doing is pointing the href tags to the actual document IDs. each table is defined as an object. In this schema file. 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. if a column is a foreign key.mysql. Although xPDO is built to be database agnostic.all table objects are defined in here --> </model> Also note that each object defines a table attribute.js`]] Conclusion This allows you to easily manage content via SVN.php`]] And you're done. 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.You can then call it like so in your Static Resources: [[include? &file=`/path/to/my/svn/checkout/snippet. just plop the include snippet wherever you need filesystem-based files. for the point of familiar comparison. it has no composite or aggregate relations to worry about. The xPDO XML: . and we're using it as a reference for all the tables mentioned on this page. A simple table: session The session table is a good table for a simple example because it contains no foreign keys. You can also use tags within the 'file' parameter. and in the future it will support other databases. such as this: [[include? &file=`[[++assets_path]]/js/myscript.schema. xPDO XML Schema File vs. the XML definition will reflect that in the definition of that object. but this attribute does NOT include any table prefix you may have defined for your site. It can be achieved with Templates and TVs as well. and all objects are wrapped in the following model tag: <model package="modx" baseClass="xPDOObject" platform="mysql" defaultEngine="MyISAM" phpdoc-package= "modx" phpdoc-subpackage=""> <!-. this isn't the case with every ORM out there. MySQL is used in these comparisons. in other words. this dependency will noted in both tables.xml schema file contains definitions of many tables. Before you Start Note that the core/model/schema/modx. Also note that xPDO defines foreign relationships in both directions. but if an id from a table is used as a foreign key in another table. `editedon` timestamp NOT NULL default '0000-00-00 00:00:00' on update CURRENT_TIMESTAMP. `namespace` varchar(40) NOT NULL default 'core'. E. `data` text. The XML precision matches up exactly to MySQL precision defined after each datatype: it reflects how many characters are visible in a column. 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. PRIMARY KEY (`id`) ) ENGINE=MyISAM DEFAULT CHARSET=latin1 Things to Note 1. 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. 2. `area` varchar(255) NOT NULL default ''. `xtype` varchar(75) NOT NULL default 'textfield'. int(2) could store any 2 digit number. PRIMARY KEY (`key`) ) ENGINE=MyISAM DEFAULT CHARSET=latin1 user_group_roles . 3.<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 ''.g. `access` int(20) unsigned default NULL. The null="false" in the XML corresponds to the NOT NULL definition in MySQL. such as the Create Chunk..in MODx Revolution is fairly straightforward. 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 . actually. XML CREATE TABLE `modx_user_group_roles` ( `id` int(10) unsigned NOT NULL auto_increment. Our code would look like this: . UNIQUE KEY `name` (`name`).. its id is used as a foreign key by another table. the foreign key relationships are defined in both directions. we'd create a Plugin and associate it to the OnDocFormRender and OnDocFormSave events. We want to add a field called 'Home' that puts an address field into the manager interface. This is a good reminder that in xPDO.This table includes a single foreign key. but let's go along with it for tutorial purposes ). You just use the On*FormRender Plugin events. `authority` int(10) unsigned NOT NULL default '9999'. `description` mediumtext. Update Resource. To do so. PRIMARY KEY (`id`). etc . `name` varchar(255) NOT NULL. and then stores it into the longtitle value (this is not the best place to store it. com/community/register/ Filing Bug Reports . break. } /* now do the HTML */ $fields = ' <div class="x-form-item x-tab-item"> <label class="x-form-item-label" style="width:150px. so set the default */ $profile = $modx->user->getOne('Profile'). case 'OnDocFormRender': $v = ''. Simply signup here: http://modxcms.$_POST['home']). break. css. Those are unnecessary. case 'OnDocFormSave': /* do processing logic here. $resource->save(). } else { /* on the create screen.'" class="x-form-text x-form-field" /> </div> </div> '. $v = $profile->get('address'). } return. Getting a MODx Account This section is under construction. register them here */ break. */ $resource =& $scriptProperties['resource'].$v. etc. so set the value */ $v = $scriptProperties['resource']->get('longtitle'). if (isset($scriptProperties['resource'])) { /* on the update screen. Note the CSS classes and styling in the form HTML.<?php /** * Register a form field to forms */ switch ($modx->event->name) { case 'OnDocFormPrerender': /* if you want to add custom scripts. $resource->set('longtitle'. but will make the form "match" the styling of the rest of the fields.">Home</label> <div class="x-form-element"> <input type="text" name="home" value="'. $modx->event->output($fields). MODx Community Information This section is currently under construction. Please. and be able to submit bugs and review recent commits. you've seen MODx Revolution. After an unspecified. However.modx. and it's based on—more like copied directly from—the same one used by Apache and the Dojo Foundation. At least we don't do the glue-on-the-keyboard initiation anymore (darn lawyers). or actually commit some code. A CLA protects your contributions. no political jokes hidden in the comments. and before each patch approval he staples them to the wall and we choose the one he hits while blindfolded. so that's not true. you only need 2 . But how do I get a sign-on? Simple.be it an active coder or simply a tester who submits patches. and the other for the MODx forums. so no need for multiple logins. there is a level of relationship that we like to develop with people before they become core devs. 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 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. All new bugs will be submitted via JIRA. All resources use an SSO-driven authentication interface. you can use SVN's "Create Patch" to create the patch and submit it to JIRA. For the current time. you're confused on how to start that process. For now. and you'll be able to quickly start submitting Jira patches and more! If you want to contribute to Confluence. now what? I want to commit! My patch was rejected! What?! See Also So. MODx is on SVN for development. Then. Fisheye/Crucible . You've got access to Jira now. From there you'll be able to submit commits just like the rest of the team. He's not blindfolded. Simply checkout MODx Revolution and start working on your patch. to your dismay. So use it! Submit those bugs and ideas to Jira. and include in Jira patches for them. This article will help you get to your level of commitment in development .one for the Atlassian apps. In all honesty. Really. (And we're working on getting the forums to SSO!) JIRA . 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. Honestly. and Confluence.com Becoming a Core Contributor The Resources But how do I get a sign-on? I've submitted my CLA. comprised of Jira.This section is under construction. You can submit a patch by using Subversion (SVN).we usually do that to the newbies.) Sometimes a patch you submit wont make it in. Don't worry if your first Crucible review is scathing . MODx team administrators will then grant you access according to team needs. Fisheye/Crucible. although this will soon be moving to Git.This is a detailed SVN reporting and reviewing interface that allows for peer reviews of committed code. Confluence . 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 . work on bugs. MODx needs eager developers.MODx's new bugtracker. the first step is to fill out and send in a Contributor License Agreement (CLA) right after creating a JIRA account. and are itching to get in on the development. The Resources MODx has moved to an Atlassian-driven development environment. and have those commits reviewed by fellow team members in Crucible. a patch submitted doesn't make it into the core. no joke. Jason loves darts. and probably have noticed some bugs or have some feature requests. now what? I want to commit! We applaud your eagerness. Just register for a modxcms. It combines detailed tracking with Fisheye SVN integration.com SSO (single-sign on) login and you're there! You'll instantly have access to Jira and Fisheye. I've submitted my CLA. we do.The new wiki for MODx. It's not because we don't like you. (Okay. But. My patch was rejected! What?! Every so once in a while. Bug reports can be filed in JIRA at http://bugs. the official documentation wiki. 6.x) PHPEclipse 1. However.18 (http://spket.IDE for Linux / KDE Development Server Environments We also MacPorts. and the following tools/libraries in the development of MODx Revolution: . the MODx Team has found the following environments invaluable: Netbeans Netbeans 6.IDE TortoiseSVN .5. We really appreciate any and all contributions to MODx.SVN client svnX . which although we had a hoot reading. as it relates to the URL Paste the URL Click OK Repeat for each of the links above as necessary Individual notes: WST .net/update/nightly) Spket IDE 1.5 (http://subclipse.8 Netbeans Subversion and JIRA plugins Eclipse Eclipse 3.SVN client Kate . Subclipse. some things just wont match with the MODx vision and design philosophy. PHPEclipse.1) Web Standard Tools Project (WST) 2.IDE Coda . A ton.Install everything offered Other IDEs For Mac: TextMate . so be patient with us.1 (http://download.org/update_1.SVN client For PC: UltraEdit .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.select the latest Web Tools Platform (takes quite a while) Subclipse .org/webtools/updates/) Subclipse 1. and know we really like people who submit patches.6.simply install the Subclipse option PHPEclipse . and we seriously consider everything that this wonderful community gives to it.eclipse.IDE Versions .phpeclipse.3 (http://update. Did we mention we really like patch submitters? See Also Development Environments Recommended Development Tools and Environments for MODx Revolution In developing MODx Revolution.2.install everything offered Spket .2.IDE E . MODx has thrived because of this community.+ (recommend latest 3. was pretty useless in the end So don't take offense. XAMPP and MAMP.0. or Spket.The patch caused too many other issues to arise The patch was submitted in lolcode.tigris.6. All private methods and variables must be prefixed with the underscore _ character. and we'll be adding a test harness to MODx soon PHPDocumentor . Example: if ($test) { } while ($test == $other) { } array_push($one. etc.PHPUnit .this drives the PHP 4/5. private function _privateFunc() { } public function publicFunc() { } } Variables Note these are not function arguments. Classes All ''core'' classnames. Put a space between. Parenthesis Do not put parenthesis next to keywords.x unit testing framework for xPDO. and require_once. class modFactor { public $publicVar. will be prefixed with the "mod" prefix: ie. true and false should always be lowercase. Avoid using global variables if at all possible. Separate words with the underscore. Avoid embedded assignments (ex: $d = ($a = $b + $c) is bad). and we'll be developing tutorials and other extended documentation for inclusion in the PHPDocs in DocBook XML format Phing . and we'll be adding a test harness to MODx soon SimpleTest .will be used to allow automation of nightly builds.$two). Create class methods to do so. unless stated otherwise for special conditions. modChunk. Do put parenthesis next to function names. Do not do any real logic in object constructors.this drives the PHP 5. They start one space after the end parenthesis. require. as according to traditional Unix policy.1+ unit testing framework for xPDO.all of the classes in MODx Revolution are documented in PHPDoc format. Do not use parenthesis when using include. Do not use parenthesis in return statements when it's not necessary. Document EVERYTHING. Never use extract(). All method names will be camelCase and will start with a lowercase letter. null. include_once. modTemplate. Use all lowercase letters. 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. unit testing.0. Function Arguments and Class Variables . private $_privateVar. return $test. various distribution builds. eg: 'finStatement'. Use only if absolutely necessary. File Structure Always name PHP class files in name. Example: $_lang['chunk_create_text'] = 'Test'. Array index names are always encapsulated with single quotes. Spaces are represented by an underscore.php format. 'finTransaction'. eg: 'finBank'. Always prefix Chunk names.welcome_message'] = 'Welcome!'. } } Arrays Array index names use the underscore _. array &$anotherTest = array()) { $this->_privateVar = $testVar. See Git Installation. This prevents errors with magic_quotes. Always prefix class names.class. $local_variable =& $anotherTest. Example: class modFactor { public function testFunc($testVar. . etc. rest are camelCase. Prefixing Lexicon strings for Components need to be prefixed: $_lang['mycomponent.The first letter is lowercase. Constants Constants must be in all UPPERCASE letters. not the dash as their separator. Array index names are always lowercase. 'finDeposit' Using GitHub This section is under construction.
Copyright © 2024 DOKUMEN.SITE Inc.