├── assets ├── post-columns.css ├── post-edit.css └── post-edit.js ├── changelog.txt ├── fx-updater.php ├── includes ├── admin-scripts.php ├── api │ ├── api.php │ └── templates │ │ ├── list_plugins.php │ │ ├── plugin_information.php │ │ ├── query_plugins.php │ │ └── query_themes.php ├── functions.php ├── get-code │ ├── get-code.php │ └── settings.php ├── library │ └── markdown.php ├── repo-group │ ├── manage-columns.php │ ├── register-taxonomy.php │ └── repo-group.php ├── repo-plugin │ ├── manage-columns.php │ ├── meta-box-data.php │ ├── register-post-type.php │ └── repo-plugin.php ├── repo-theme │ ├── manage-columns.php │ ├── meta-box-data.php │ ├── register-post-type.php │ └── repo-theme.php └── updater.php ├── languages └── fx-updater.pot └── license.txt /assets/post-columns.css: -------------------------------------------------------------------------------- 1 | .up-status-active{ 2 | display: inline-block; 3 | padding: 1px 5px; 4 | background: #d9ffe2; 5 | border: 1px solid #63fd87; 6 | border-radius: 3px; 7 | } 8 | .up-status-inactive{ 9 | display: inline-block; 10 | padding: 1px 5px; 11 | background: #ffd4ae; 12 | border: 1px solid #ffa04b; 13 | border-radius: 3px; 14 | } 15 | .group-status-active{ 16 | display: inline-block; 17 | padding: 0 5px; 18 | border-bottom: 2px solid #63fd87; 19 | } 20 | .group-status-inactive{ 21 | display: inline-block; 22 | padding: 0 5px; 23 | border-bottom: 2px solid #ffa04b; 24 | } 25 | .updater-info strong{ 26 | display: inline-block; 27 | } 28 | .updater-info p{ 29 | padding-left: 30px; 30 | position: relative; 31 | } 32 | .updater-info .dashicons{ 33 | position: absolute; 34 | left: 0; 35 | top: 0; 36 | color: #bbb; 37 | } 38 | .updater-info p:hover .dashicons{ 39 | color: #aaa; 40 | } -------------------------------------------------------------------------------- /assets/post-edit.css: -------------------------------------------------------------------------------- 1 | .fx-upmb-fields:after{ 2 | content:".";display:block;height:0;clear:both;visibility:hidden; 3 | } 4 | .fx-upmb-field{ 5 | padding-bottom: 10px; 6 | margin-bottom: 10x; 7 | border-bottom: 1px solid #eee; 8 | } 9 | .fx-upmb-field:after{ 10 | content:".";display:block;height:0;clear:both;visibility:hidden; 11 | } 12 | .fx-upmb-field:last-child{ 13 | border-bottom: none; 14 | } 15 | .fx-upmb-field-label{ 16 | width: 250px; 17 | float: left; 18 | } 19 | .fx-upmb-field-label label{ 20 | font-size: 16px; 21 | font-weight: 600; 22 | } 23 | .fx-upmb-field-content{ 24 | float: left; 25 | max-width: 100%; 26 | } 27 | 28 | /* Slug */ 29 | #repo_slug{ 30 | width: 250px; 31 | max-width: 100%; 32 | } 33 | /* Theme Folder {ID} */ 34 | #theme_id{ 35 | width: 250px; 36 | max-width: 100%; 37 | } 38 | /* Upload */ 39 | #fxu_download_link{ 40 | width: 400px; 41 | max-width: 100%; 42 | } 43 | /* Version */ 44 | #fxu_version{ 45 | width: 80px; 46 | max-width: 100%; 47 | } 48 | .fx-upmb-desc{ 49 | color: #666; 50 | display: inline-block; 51 | font-style: italic; 52 | font-size: 13px; 53 | } 54 | 55 | /* === PLUGIN === */ 56 | 57 | /* Plugin ID */ 58 | #plugin_id{ 59 | width: 400px; 60 | max-width: 100%; 61 | } 62 | /* Month */ 63 | .fx-upmb-month{ 64 | width: 80px; 65 | padding: 1px 1px 1px 1px; 66 | } 67 | /* Day */ 68 | .fx-upmb-day{ 69 | position: relative; 70 | top: 2px; 71 | padding: 3px 6px 4px 6px; 72 | } 73 | /* Year */ 74 | .fx-upmb-year{ 75 | position: relative; 76 | top: 2px; 77 | padding: 3px 6px 4px 6px; 78 | } 79 | /* WP Version */ 80 | #wp_requires, 81 | #wp_tested{ 82 | width: 80px; 83 | max-width: 100%; 84 | } 85 | /* Changelog */ 86 | #fxu_changelog{ 87 | width: 400px; 88 | max-width: 100%; 89 | } 90 | .fx-upmb-changelog-area{ 91 | font-size: 13px; 92 | line-height: 1.5; 93 | margin: 1em 0; 94 | } 95 | /* Upgrade Notice */ 96 | #upgrade_notice{ 97 | width: 400px; 98 | max-width: 100%; 99 | } 100 | 101 | /* Media Queries */ 102 | @media screen and ( max-width: 1200px ) { 103 | .fx-upmb-field-label{ 104 | width: 100%; 105 | float: none; 106 | } 107 | .fx-upmb-field-content{ 108 | width: 100%; 109 | float: none; 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /assets/post-edit.js: -------------------------------------------------------------------------------- 1 | jQuery(document).ready(function($){ 2 | 3 | /* the vars */ 4 | var file_frame; 5 | 6 | /* Click the upload button */ 7 | $( document.body ).on( 'click', '.upload-zip', function(e){ 8 | e.preventDefault(); 9 | 10 | /* Open the frame if already loaded. */ 11 | if ( file_frame ) { 12 | file_frame.open(); 13 | return; 14 | } 15 | 16 | var this_button = $( this ); 17 | 18 | /* If media frame doesn't exist, create it with some options. */ 19 | file_frame = wp.media.frames.file_frame = wp.media({ 20 | className: 'media-frame fx-media-frame', 21 | frame: 'select', 22 | title: fx_upmb_upload.title, 23 | library: { type: 'application/zip' }, 24 | button: { text: fx_upmb_upload.button }, 25 | multiple: false, 26 | }); 27 | 28 | /* insert */ 29 | file_frame.on( 'select', function(){ 30 | 31 | /* Insert */ 32 | var this_attachment = file_frame.state().get('selection').first().toJSON(); 33 | this_button.parents( '.fx-upmb-upload' ).find( '.fx-upmb-upload-url' ).val( this_attachment.url ); 34 | 35 | /* Enable remove button */ 36 | this_button.siblings( '.remove-zip' ).removeClass( 'disabled' ); 37 | 38 | }); 39 | 40 | // Now that everything has been set, let's open up the frame. 41 | file_frame.open(); 42 | }); 43 | 44 | /* === Disabled Button === */ 45 | $( '.remove-zip' ).each( function(i){ 46 | var url_input = $( this ).parents( '.fx-upmb-upload' ).find( '.fx-upmb-upload-url' ).val(); 47 | if( '' == url_input ){ 48 | $( this ).addClass( 'disabled' ); 49 | } 50 | else{ 51 | $( this ).removeClass( 'disabled' ); 52 | } 53 | }); 54 | $( ".fx-upmb-upload-url" ).change( function(){ 55 | var url_input = $( this ).val(); 56 | var remove_zip = $( this ).parents( '.fx-upmb-upload' ).find( '.remove-zip' ); 57 | if( '' == url_input ){ 58 | remove_zip.addClass( 'disabled' ); 59 | } 60 | else{ 61 | remove_zip.removeClass( 'disabled' ); 62 | } 63 | }); 64 | 65 | /* === Remove File === */ 66 | $( document.body ).on( 'click', '.remove-zip', function(e){ 67 | e.preventDefault(); 68 | 69 | /* Remove url input */ 70 | $( this ).parents( '.fx-upmb-upload' ).find( '.fx-upmb-upload-url' ).val(''); 71 | 72 | /* Disabled */ 73 | $( this ).addClass( 'disabled' ); 74 | }); 75 | 76 | }); 77 | -------------------------------------------------------------------------------- /changelog.txt: -------------------------------------------------------------------------------- 1 | 1.0.1 (20 June 2016) 2 | - move menu after dashboard. (menu order:2) 3 | - fix typo: remove_cap on uninstall hook. -------------------------------------------------------------------------------- /fx-updater.php: -------------------------------------------------------------------------------- 1 | 15 | * @copyright Copyright (c) 2016, Genbu Media 16 | **/ 17 | 18 | /* Do not access this file directly */ 19 | if ( ! defined( 'WPINC' ) ) { die; } 20 | 21 | /* Constants 22 | ------------------------------------------ */ 23 | 24 | /* Set plugin version constant. */ 25 | define( 'FX_UPDATER_VERSION', '1.0.1' ); 26 | 27 | /* Set constant path to the plugin directory. */ 28 | define( 'FX_UPDATER_PATH', trailingslashit( plugin_dir_path(__FILE__) ) ); 29 | 30 | /* Set the constant path to the plugin directory URI. */ 31 | define( 'FX_UPDATER_URI', trailingslashit( plugin_dir_url( __FILE__ ) ) ); 32 | 33 | 34 | /* Includes 35 | ------------------------------------------ */ 36 | 37 | /* Load Utility Functions */ 38 | require_once( FX_UPDATER_PATH . 'includes/functions.php' ); 39 | 40 | /* Load Settings Functions */ 41 | require_once( FX_UPDATER_PATH . 'includes/admin-scripts.php' ); 42 | 43 | /* Load Query Functions */ 44 | require_once( FX_UPDATER_PATH . 'includes/api/api.php' ); 45 | 46 | /* Load Settings Functions */ 47 | require_once( FX_UPDATER_PATH . 'includes/get-code/settings.php' ); 48 | 49 | /* Load Group Repo Functions */ 50 | require_once( FX_UPDATER_PATH . 'includes/repo-group/repo-group.php' ); 51 | 52 | /* Load Plugin Repo Functions */ 53 | require_once( FX_UPDATER_PATH . 'includes/repo-plugin/repo-plugin.php' ); 54 | 55 | /* Load Theme Repo Functions */ 56 | require_once( FX_UPDATER_PATH . 'includes/repo-theme/repo-theme.php' ); 57 | 58 | /* Load Updater */ 59 | require_once( FX_UPDATER_PATH . 'includes/updater.php' ); 60 | 61 | 62 | /* Plugins Loaded 63 | ------------------------------------------ */ 64 | 65 | /* Load plugins file */ 66 | add_action( 'plugins_loaded', 'fx_base_plugins_loaded' ); 67 | 68 | /** 69 | * Load plugins file 70 | * @since 0.1.0 71 | */ 72 | function fx_base_plugins_loaded(){ 73 | 74 | /* Load Text Domain (Language Translation) */ 75 | load_plugin_textdomain( 'fx-updater', false, dirname( plugin_basename( __FILE__ ) ) . '/languages/' ); 76 | 77 | /* Plugin Action Link */ 78 | add_filter( 'plugin_action_links_' . plugin_basename( __FILE__ ), 'fx_updater_plugin_action_links' ); 79 | } 80 | 81 | /** 82 | * Add Action Link For Support 83 | * @since 1.0.0 84 | */ 85 | function fx_updater_plugin_action_links( $links ){ 86 | 87 | /* Get current user info */ 88 | if( function_exists( 'wp_get_current_user' ) ){ 89 | $current_user = wp_get_current_user(); 90 | } 91 | else{ 92 | global $current_user; 93 | get_currentuserinfo(); 94 | } 95 | 96 | /* Build support url */ 97 | $support_url = add_query_arg( 98 | array( 99 | 'about' => urlencode( 'f(x) Updater (v.' . FX_UPDATER_VERSION . ')' ), 100 | 'sp_name' => urlencode( $current_user->display_name ), 101 | 'sp_email' => urlencode( $current_user->user_email ), 102 | 'sp_website' => urlencode( home_url() ), 103 | ), 104 | 'http://genbumedia.com/contact/' 105 | ); 106 | 107 | /* Add support link */ 108 | $links[] = '' . __( 'Get Support', 'fx-base' ) . ''; 109 | 110 | return $links; 111 | } 112 | 113 | 114 | /* Activation and Uninstall 115 | ------------------------------------------ */ 116 | 117 | /* Register activation hook. */ 118 | register_activation_hook( __FILE__, 'fx_updater_plugin_activation' ); 119 | 120 | 121 | /** 122 | * Runs only when the plugin is activated. 123 | * @since 1.0.0 124 | */ 125 | function fx_updater_plugin_activation() { 126 | 127 | /* Get the administrator role. */ 128 | $role = get_role( 'administrator' ); 129 | 130 | /* If the administrator role exists, add required capabilities for the plugin. */ 131 | if ( !empty( $role ) ) { 132 | $role->add_cap( 'manage_fx_updaters' ); 133 | $role->add_cap( 'create_fx_updaters' ); 134 | $role->add_cap( 'edit_fx_updaters' ); 135 | } 136 | 137 | /* Temporary Data (5sec) to Add Activation Notice */ 138 | set_transient( 'fx_updater_activation_notice', '1', 5 ); 139 | 140 | /* uninstall plugin */ 141 | register_uninstall_hook( __FILE__, 'fx_updater_plugin_uninstall' ); 142 | } 143 | 144 | 145 | /** 146 | * Uninstall plugin 147 | * @since 0.1.0 148 | */ 149 | function fx_updater_plugin_uninstall(){ 150 | 151 | /* Get the administrator role. */ 152 | $role = get_role( 'administrator' ); 153 | 154 | /* If the administrator role exists, remove added capabilities for the plugin. */ 155 | if ( !empty( $role ) ) { 156 | $role->remove_cap( 'manage_fx_updaters' ); 157 | $role->remove_cap( 'create_fx_updaters' ); 158 | $role->remove_cap( 'edit_fx_updaters' ); 159 | } 160 | } 161 | 162 | 163 | /* Activation Notice 164 | ------------------------------------------ */ 165 | 166 | /* Add admin notice */ 167 | add_action( 'admin_notices', 'fx_updater_plugin_activation_notice' ); 168 | 169 | /** 170 | * Admin Notice on Activation. 171 | * @since 1.0.0 172 | */ 173 | function fx_updater_plugin_activation_notice(){ 174 | $transient = get_transient( 'fx_updater_activation_notice' ); 175 | if( $transient ){ 176 | ?> 177 |
s around 222 | # "paragraphs" that are wrapped in non-block-level tags, such as anchors, 223 | # phrase emphasis, and spans. The list of tags we're looking for is 224 | # hard-coded: 225 | # 226 | # * List "a" is made of tags which can be both inline or block-level. 227 | # These will be treated block-level when the start tag is alone on 228 | # its line, otherwise they're not matched here and will be taken as 229 | # inline later. 230 | # * List "b" is made of tags which are always block-level; 231 | # 232 | $block_tags_a_re = 'ins|del'; 233 | $block_tags_b_re = 'p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|address|'. 234 | 'script|noscript|form|fieldset|iframe|math|svg|'. 235 | 'article|section|nav|aside|hgroup|header|footer|'. 236 | 'figure'; 237 | 238 | # Regular expression for the content of a block tag. 239 | $nested_tags_level = 4; 240 | $attr = ' 241 | (?> # optional tag attributes 242 | \s # starts with whitespace 243 | (?> 244 | [^>"/]+ # text outside quotes 245 | | 246 | /+(?!>) # slash not followed by ">" 247 | | 248 | "[^"]*" # text inside double quotes (tolerate ">") 249 | | 250 | \'[^\']*\' # text inside single quotes (tolerate ">") 251 | )* 252 | )? 253 | '; 254 | $content = 255 | str_repeat(' 256 | (?> 257 | [^<]+ # content without tag 258 | | 259 | <\2 # nested opening tag 260 | '.$attr.' # attributes 261 | (?> 262 | /> 263 | | 264 | >', $nested_tags_level). # end of opening tag 265 | '.*?'. # last level nested tag content 266 | str_repeat(' 267 | \2\s*> # closing nested tag 268 | ) 269 | | 270 | <(?!/\2\s*> # other tags with a different name 271 | ) 272 | )*', 273 | $nested_tags_level); 274 | $content2 = str_replace('\2', '\3', $content); 275 | 276 | # First, look for nested blocks, e.g.: 277 | #
` blocks.
932 | #
933 | $text = preg_replace_callback('{
934 | (?:\n\n|\A\n?)
935 | ( # $1 = the code block -- one or more lines, starting with a space/tab
936 | (?>
937 | [ ]{'.$this->tab_width.'} # Lines must start with a tab or a tab-width of spaces
938 | .*\n+
939 | )+
940 | )
941 | ((?=^[ ]{0,'.$this->tab_width.'}\S)|\Z) # Lookahead for non-space at line-start, or end of doc
942 | }xm',
943 | array(&$this, '_doCodeBlocks_callback'), $text);
944 |
945 | return $text;
946 | }
947 | function _doCodeBlocks_callback($matches) {
948 | $codeblock = $matches[1];
949 |
950 | $codeblock = $this->outdent($codeblock);
951 | $codeblock = htmlspecialchars($codeblock, ENT_NOQUOTES);
952 |
953 | # trim leading newlines and trailing newlines
954 | $codeblock = preg_replace('/\A\n+|\n+\z/', '', $codeblock);
955 |
956 | $codeblock = "$codeblock\n
";
957 | return "\n\n".$this->hashBlock($codeblock)."\n\n";
958 | }
959 |
960 |
961 | function makeCodeSpan($code) {
962 | #
963 | # Create a code span markup for $code. Called from handleSpanToken.
964 | #
965 | $code = htmlspecialchars(trim($code), ENT_NOQUOTES);
966 | return $this->hashPart("$code
");
967 | }
968 |
969 |
970 | var $em_relist = array(
971 | '' => '(?:(? '(?<=\S|^)(? '(?<=\S|^)(? '(?:(? '(?<=\S|^)(? '(?<=\S|^)(? '(?:(? '(?<=\S|^)(? '(?<=\S|^)(?em_relist as $em => $em_re) {
993 | foreach ($this->strong_relist as $strong => $strong_re) {
994 | # Construct list of allowed token expressions.
995 | $token_relist = array();
996 | if (isset($this->em_strong_relist["$em$strong"])) {
997 | $token_relist[] = $this->em_strong_relist["$em$strong"];
998 | }
999 | $token_relist[] = $em_re;
1000 | $token_relist[] = $strong_re;
1001 |
1002 | # Construct master expression from list.
1003 | $token_re = '{('. implode('|', $token_relist) .')}';
1004 | $this->em_strong_prepared_relist["$em$strong"] = $token_re;
1005 | }
1006 | }
1007 | }
1008 |
1009 | function doItalicsAndBold($text) {
1010 | $token_stack = array('');
1011 | $text_stack = array('');
1012 | $em = '';
1013 | $strong = '';
1014 | $tree_char_em = false;
1015 |
1016 | while (1) {
1017 | #
1018 | # Get prepared regular expression for seraching emphasis tokens
1019 | # in current context.
1020 | #
1021 | $token_re = $this->em_strong_prepared_relist["$em$strong"];
1022 |
1023 | #
1024 | # Each loop iteration search for the next emphasis token.
1025 | # Each token is then passed to handleSpanToken.
1026 | #
1027 | $parts = preg_split($token_re, $text, 2, PREG_SPLIT_DELIM_CAPTURE);
1028 | $text_stack[0] .= $parts[0];
1029 | $token =& $parts[1];
1030 | $text =& $parts[2];
1031 |
1032 | if (empty($token)) {
1033 | # Reached end of text span: empty stack without emitting.
1034 | # any more emphasis.
1035 | while ($token_stack[0]) {
1036 | $text_stack[1] .= array_shift($token_stack);
1037 | $text_stack[0] .= array_shift($text_stack);
1038 | }
1039 | break;
1040 | }
1041 |
1042 | $token_len = strlen($token);
1043 | if ($tree_char_em) {
1044 | # Reached closing marker while inside a three-char emphasis.
1045 | if ($token_len == 3) {
1046 | # Three-char closing marker, close em and strong.
1047 | array_shift($token_stack);
1048 | $span = array_shift($text_stack);
1049 | $span = $this->runSpanGamut($span);
1050 | $span = "$span";
1051 | $text_stack[0] .= $this->hashPart($span);
1052 | $em = '';
1053 | $strong = '';
1054 | } else {
1055 | # Other closing marker: close one em or strong and
1056 | # change current token state to match the other
1057 | $token_stack[0] = str_repeat($token{0}, 3-$token_len);
1058 | $tag = $token_len == 2 ? "strong" : "em";
1059 | $span = $text_stack[0];
1060 | $span = $this->runSpanGamut($span);
1061 | $span = "<$tag>$span$tag>";
1062 | $text_stack[0] = $this->hashPart($span);
1063 | $$tag = ''; # $$tag stands for $em or $strong
1064 | }
1065 | $tree_char_em = false;
1066 | } else if ($token_len == 3) {
1067 | if ($em) {
1068 | # Reached closing marker for both em and strong.
1069 | # Closing strong marker:
1070 | for ($i = 0; $i < 2; ++$i) {
1071 | $shifted_token = array_shift($token_stack);
1072 | $tag = strlen($shifted_token) == 2 ? "strong" : "em";
1073 | $span = array_shift($text_stack);
1074 | $span = $this->runSpanGamut($span);
1075 | $span = "<$tag>$span$tag>";
1076 | $text_stack[0] .= $this->hashPart($span);
1077 | $$tag = ''; # $$tag stands for $em or $strong
1078 | }
1079 | } else {
1080 | # Reached opening three-char emphasis marker. Push on token
1081 | # stack; will be handled by the special condition above.
1082 | $em = $token{0};
1083 | $strong = "$em$em";
1084 | array_unshift($token_stack, $token);
1085 | array_unshift($text_stack, '');
1086 | $tree_char_em = true;
1087 | }
1088 | } else if ($token_len == 2) {
1089 | if ($strong) {
1090 | # Unwind any dangling emphasis marker:
1091 | if (strlen($token_stack[0]) == 1) {
1092 | $text_stack[1] .= array_shift($token_stack);
1093 | $text_stack[0] .= array_shift($text_stack);
1094 | }
1095 | # Closing strong marker:
1096 | array_shift($token_stack);
1097 | $span = array_shift($text_stack);
1098 | $span = $this->runSpanGamut($span);
1099 | $span = "$span";
1100 | $text_stack[0] .= $this->hashPart($span);
1101 | $strong = '';
1102 | } else {
1103 | array_unshift($token_stack, $token);
1104 | array_unshift($text_stack, '');
1105 | $strong = $token;
1106 | }
1107 | } else {
1108 | # Here $token_len == 1
1109 | if ($em) {
1110 | if (strlen($token_stack[0]) == 1) {
1111 | # Closing emphasis marker:
1112 | array_shift($token_stack);
1113 | $span = array_shift($text_stack);
1114 | $span = $this->runSpanGamut($span);
1115 | $span = "$span";
1116 | $text_stack[0] .= $this->hashPart($span);
1117 | $em = '';
1118 | } else {
1119 | $text_stack[0] .= $token;
1120 | }
1121 | } else {
1122 | array_unshift($token_stack, $token);
1123 | array_unshift($text_stack, '');
1124 | $em = $token;
1125 | }
1126 | }
1127 | }
1128 | return $text_stack[0];
1129 | }
1130 |
1131 |
1132 | function doBlockQuotes($text) {
1133 | $text = preg_replace_callback('/
1134 | ( # Wrap whole match in $1
1135 | (?>
1136 | ^[ ]*>[ ]? # ">" at the start of a line
1137 | .+\n # rest of the first line
1138 | (.+\n)* # subsequent consecutive lines
1139 | \n* # blanks
1140 | )+
1141 | )
1142 | /xm',
1143 | array(&$this, '_doBlockQuotes_callback'), $text);
1144 |
1145 | return $text;
1146 | }
1147 | function _doBlockQuotes_callback($matches) {
1148 | $bq = $matches[1];
1149 | # trim one level of quoting - trim whitespace-only lines
1150 | $bq = preg_replace('/^[ ]*>[ ]?|^[ ]+$/m', '', $bq);
1151 | $bq = $this->runBlockGamut($bq); # recurse
1152 |
1153 | $bq = preg_replace('/^/m', " ", $bq);
1154 | # These leading spaces cause problem with content,
1155 | # so we need to fix that:
1156 | $bq = preg_replace_callback('{(\s*.+?
)}sx',
1157 | array(&$this, '_doBlockQuotes_callback2'), $bq);
1158 |
1159 | return "\n". $this->hashBlock("\n$bq\n
")."\n\n";
1160 | }
1161 | function _doBlockQuotes_callback2($matches) {
1162 | $pre = $matches[1];
1163 | $pre = preg_replace('/^ /m', '', $pre);
1164 | return $pre;
1165 | }
1166 |
1167 |
1168 | function formParagraphs($text) {
1169 | #
1170 | # Params:
1171 | # $text - string to process with html tags
1172 | #
1173 | # Strip leading and trailing lines:
1174 | $text = preg_replace('/\A\n+|\n+\z/', '', $text);
1175 |
1176 | $grafs = preg_split('/\n{2,}/', $text, -1, PREG_SPLIT_NO_EMPTY);
1177 |
1178 | #
1179 | # Wrap
tags and unhashify HTML blocks
1180 | #
1181 | foreach ($grafs as $key => $value) {
1182 | if (!preg_match('/^B\x1A[0-9]+B$/', $value)) {
1183 | # Is a paragraph.
1184 | $value = $this->runSpanGamut($value);
1185 | $value = preg_replace('/^([ ]*)/', "
", $value);
1186 | $value .= "
";
1187 | $grafs[$key] = $this->unhash($value);
1188 | }
1189 | else {
1190 | # Is a block.
1191 | # Modify elements of @grafs in-place...
1192 | $graf = $value;
1193 | $block = $this->html_hashes[$graf];
1194 | $graf = $block;
1195 | // if (preg_match('{
1196 | // \A
1197 | // ( # $1 = tag
1198 | // ]*
1200 | // \b
1201 | // markdown\s*=\s* ([\'"]) # $2 = attr quote char
1202 | // 1
1203 | // \2
1204 | // [^>]*
1205 | // >
1206 | // )
1207 | // ( # $3 = contents
1208 | // .*
1209 | // )
1210 | // () # $4 = closing tag
1211 | // \z
1212 | // }xs', $block, $matches))
1213 | // {
1214 | // list(, $div_open, , $div_content, $div_close) = $matches;
1215 | //
1216 | // # We can't call Markdown(), because that resets the hash;
1217 | // # that initialization code should be pulled into its own sub, though.
1218 | // $div_content = $this->hashHTMLBlocks($div_content);
1219 | //
1220 | // # Run document gamut methods on the content.
1221 | // foreach ($this->document_gamut as $method => $priority) {
1222 | // $div_content = $this->$method($div_content);
1223 | // }
1224 | //
1225 | // $div_open = preg_replace(
1226 | // '{\smarkdown\s*=\s*([\'"]).+?\1}', '', $div_open);
1227 | //
1228 | // $graf = $div_open . "\n" . $div_content . "\n" . $div_close;
1229 | // }
1230 | $grafs[$key] = $graf;
1231 | }
1232 | }
1233 |
1234 | return implode("\n\n", $grafs);
1235 | }
1236 |
1237 |
1238 | function encodeAttribute($text) {
1239 | #
1240 | # Encode text for a double-quoted HTML attribute. This function
1241 | # is *not* suitable for attributes enclosed in single quotes.
1242 | #
1243 | $text = $this->encodeAmpsAndAngles($text);
1244 | $text = str_replace('"', '"', $text);
1245 | return $text;
1246 | }
1247 |
1248 |
1249 | function encodeAmpsAndAngles($text) {
1250 | #
1251 | # Smart processing for ampersands and angle brackets that need to
1252 | # be encoded. Valid character entities are left alone unless the
1253 | # no-entities mode is set.
1254 | #
1255 | if ($this->no_entities) {
1256 | $text = str_replace('&', '&', $text);
1257 | } else {
1258 | # Ampersand-encoding based entirely on Nat Irons's Amputator
1259 | # MT plugin:
1260 | $text = preg_replace('/&(?!#?[xX]?(?:[0-9a-fA-F]+|\w+);)/',
1261 | '&', $text);;
1262 | }
1263 | # Encode remaining <'s
1264 | $text = str_replace('<', '<', $text);
1265 |
1266 | return $text;
1267 | }
1268 |
1269 |
1270 | function doAutoLinks($text) {
1271 | $text = preg_replace_callback('{<((https?|ftp|dict):[^\'">\s]+)>}i',
1272 | array(&$this, '_doAutoLinks_url_callback'), $text);
1273 |
1274 | # Email addresses:
1275 | $text = preg_replace_callback('{
1276 | <
1277 | (?:mailto:)?
1278 | (
1279 | (?:
1280 | [-!#$%&\'*+/=?^_`.{|}~\w\x80-\xFF]+
1281 | |
1282 | ".*?"
1283 | )
1284 | \@
1285 | (?:
1286 | [-a-z0-9\x80-\xFF]+(\.[-a-z0-9\x80-\xFF]+)*\.[a-z]+
1287 | |
1288 | \[[\d.a-fA-F:]+\] # IPv4 & IPv6
1289 | )
1290 | )
1291 | >
1292 | }xi',
1293 | array(&$this, '_doAutoLinks_email_callback'), $text);
1294 |
1295 | return $text;
1296 | }
1297 | function _doAutoLinks_url_callback($matches) {
1298 | $url = $this->encodeAttribute($matches[1]);
1299 | $link = "$url";
1300 | return $this->hashPart($link);
1301 | }
1302 | function _doAutoLinks_email_callback($matches) {
1303 | $address = $matches[1];
1304 | $link = $this->encodeEmailAddress($address);
1305 | return $this->hashPart($link);
1306 | }
1307 |
1308 |
1309 | function encodeEmailAddress($addr) {
1310 | #
1311 | # Input: an email address, e.g. "foo@example.com"
1312 | #
1313 | # Output: the email address as a mailto link, with each character
1314 | # of the address encoded as either a decimal or hex entity, in
1315 | # the hopes of foiling most address harvesting spam bots. E.g.:
1316 | #
1317 | #
1321 | #
1322 | # Based by a filter by Matthew Wickline, posted to BBEdit-Talk.
1323 | # With some optimizations by Milian Wolff.
1324 | #
1325 | $addr = "mailto:" . $addr;
1326 | $chars = preg_split('/(? $char) {
1330 | $ord = ord($char);
1331 | # Ignore non-ascii chars.
1332 | if ($ord < 128) {
1333 | $r = ($seed * (1 + $key)) % 100; # Pseudo-random function.
1334 | # roughly 10% raw, 45% hex, 45% dec
1335 | # '@' *must* be encoded. I insist.
1336 | if ($r > 90 && $char != '@') /* do nothing */;
1337 | else if ($r < 45) $chars[$key] = ''.dechex($ord).';';
1338 | else $chars[$key] = ''.$ord.';';
1339 | }
1340 | }
1341 |
1342 | $addr = implode('', $chars);
1343 | $text = implode('', array_slice($chars, 7)); # text without `mailto:`
1344 | $addr = "$text";
1345 |
1346 | return $addr;
1347 | }
1348 |
1349 |
1350 | function parseSpan($str) {
1351 | #
1352 | # Take the string $str and parse it into tokens, hashing embeded HTML,
1353 | # escaped characters and handling code spans.
1354 | #
1355 | $output = '';
1356 |
1357 | $span_re = '{
1358 | (
1359 | \\\\'.$this->escape_chars_re.'
1360 | |
1361 | (?no_markup ? '' : '
1364 | |
1365 | # comment
1366 | |
1367 | <\?.*?\?> | <%.*?%> # processing instruction
1368 | |
1369 | <[!$]?[-a-zA-Z0-9:_]+ # regular tags
1370 | (?>
1371 | \s
1372 | (?>[^"\'>]+|"[^"]*"|\'[^\']*\')*
1373 | )?
1374 | >
1375 | |
1376 | <[-a-zA-Z0-9:_]+\s*/> # xml-style empty tag
1377 | |
1378 | [-a-zA-Z0-9:_]+\s*> # closing tag
1379 | ').'
1380 | )
1381 | }xs';
1382 |
1383 | while (1) {
1384 | #
1385 | # Each loop iteration seach for either the next tag, the next
1386 | # openning code span marker, or the next escaped character.
1387 | # Each token is then passed to handleSpanToken.
1388 | #
1389 | $parts = preg_split($span_re, $str, 2, PREG_SPLIT_DELIM_CAPTURE);
1390 |
1391 | # Create token from text preceding tag.
1392 | if ($parts[0] != "") {
1393 | $output .= $parts[0];
1394 | }
1395 |
1396 | # Check if we reach the end.
1397 | if (isset($parts[1])) {
1398 | $output .= $this->handleSpanToken($parts[1], $parts[2]);
1399 | $str = $parts[2];
1400 | }
1401 | else {
1402 | break;
1403 | }
1404 | }
1405 |
1406 | return $output;
1407 | }
1408 |
1409 |
1410 | function handleSpanToken($token, &$str) {
1411 | #
1412 | # Handle $token provided by parseSpan by determining its nature and
1413 | # returning the corresponding value that should replace it.
1414 | #
1415 | switch ($token{0}) {
1416 | case "\\":
1417 | return $this->hashPart("". ord($token{1}). ";");
1418 | case "`":
1419 | # Search for end marker in remaining text.
1420 | if (preg_match('/^(.*?[^`])'.preg_quote($token).'(?!`)(.*)$/sm',
1421 | $str, $matches))
1422 | {
1423 | $str = $matches[2];
1424 | $codespan = $this->makeCodeSpan($matches[1]);
1425 | return $this->hashPart($codespan);
1426 | }
1427 | return $token; // return as text since no ending marker found.
1428 | default:
1429 | return $this->hashPart($token);
1430 | }
1431 | }
1432 |
1433 |
1434 | function outdent($text) {
1435 | #
1436 | # Remove one level of line-leading tabs or spaces
1437 | #
1438 | return preg_replace('/^(\t|[ ]{1,'.$this->tab_width.'})/m', '', $text);
1439 | }
1440 |
1441 |
1442 | # String length function for detab. `_initDetab` will create a function to
1443 | # hanlde UTF-8 if the default function does not exist.
1444 | var $utf8_strlen = 'mb_strlen';
1445 |
1446 | function detab($text) {
1447 | #
1448 | # Replace tabs with the appropriate amount of space.
1449 | #
1450 | # For each line we separate the line in blocks delemited by
1451 | # tab characters. Then we reconstruct every line by adding the
1452 | # appropriate number of space between each blocks.
1453 |
1454 | $text = preg_replace_callback('/^.*\t.*$/m',
1455 | array(&$this, '_detab_callback'), $text);
1456 |
1457 | return $text;
1458 | }
1459 | function _detab_callback($matches) {
1460 | $line = $matches[0];
1461 | $strlen = $this->utf8_strlen; # strlen function for UTF-8.
1462 |
1463 | # Split in blocks.
1464 | $blocks = explode("\t", $line);
1465 | # Add each blocks to the line.
1466 | $line = $blocks[0];
1467 | unset($blocks[0]); # Do not add first block twice.
1468 | foreach ($blocks as $block) {
1469 | # Calculate amount of space, insert spaces, insert block.
1470 | $amount = $this->tab_width -
1471 | $strlen($line, 'UTF-8') % $this->tab_width;
1472 | $line .= str_repeat(" ", $amount) . $block;
1473 | }
1474 | return $line;
1475 | }
1476 | function _initDetab() {
1477 | #
1478 | # Check for the availability of the function in the `utf8_strlen` property
1479 | # (initially `mb_strlen`). If the function is not available, create a
1480 | # function that will loosely count the number of UTF-8 characters with a
1481 | # regular expression.
1482 | #
1483 | if (function_exists($this->utf8_strlen)) return;
1484 | $this->utf8_strlen = create_function('$text', 'return preg_match_all(
1485 | "/[\\\\x00-\\\\xBF]|[\\\\xC0-\\\\xFF][\\\\x80-\\\\xBF]*/",
1486 | $text, $m);');
1487 | }
1488 |
1489 |
1490 | function unhash($text) {
1491 | #
1492 | # Swap back in all the tags hashed by _HashHTMLBlocks.
1493 | #
1494 | return preg_replace_callback('/(.)\x1A[0-9]+\1/',
1495 | array(&$this, '_unhash_callback'), $text);
1496 | }
1497 | function _unhash_callback($matches) {
1498 | return $this->html_hashes[$matches[0]];
1499 | }
1500 |
1501 | }
1502 |
1503 |
1504 | #
1505 | # Markdown Extra Parser Class
1506 | #
1507 |
1508 | class fx_Updater_MarkdownExtra_Parser extends fx_Updater_Markdown_Parser {
1509 |
1510 | ### Configuration Variables ###
1511 |
1512 | # Prefix for footnote ids.
1513 | var $fn_id_prefix = "";
1514 |
1515 | # Optional title attribute for footnote links and backlinks.
1516 | var $fn_link_title = "";
1517 | var $fn_backlink_title = "";
1518 |
1519 | # Optional class attribute for footnote links and backlinks.
1520 | var $fn_link_class = "";
1521 | var $fn_backlink_class = "";
1522 |
1523 | # Optional class prefix for fenced code block.
1524 | var $code_class_prefix = "";
1525 | # Class attribute for code blocks goes on the `code` tag;
1526 | # setting this to true will put attributes on the `pre` tag instead.
1527 | var $code_attr_on_pre = false;
1528 |
1529 | # Predefined abbreviations.
1530 | var $predef_abbr = array();
1531 |
1532 |
1533 | ### Parser Implementation ###
1534 |
1535 | function fx_Updater_MarkdownExtra_Parser() {
1536 | #
1537 | # Constructor function. Initialize the parser object.
1538 | #
1539 | # Add extra escapable characters before parent constructor
1540 | # initialize the table.
1541 | $this->escape_chars .= ':|';
1542 |
1543 | # Insert extra document, block, and span transformations.
1544 | # Parent constructor will do the sorting.
1545 | $this->document_gamut += array(
1546 | "doFencedCodeBlocks" => 5,
1547 | "stripFootnotes" => 15,
1548 | "stripAbbreviations" => 25,
1549 | "appendFootnotes" => 50,
1550 | );
1551 | $this->block_gamut += array(
1552 | "doFencedCodeBlocks" => 5,
1553 | "doTables" => 15,
1554 | "doDefLists" => 45,
1555 | );
1556 | $this->span_gamut += array(
1557 | "doFootnotes" => 5,
1558 | "doAbbreviations" => 70,
1559 | );
1560 |
1561 | parent::Markdown_Parser();
1562 | }
1563 |
1564 |
1565 | # Extra variables used during extra transformations.
1566 | var $footnotes = array();
1567 | var $footnotes_ordered = array();
1568 | var $footnotes_ref_count = array();
1569 | var $footnotes_numbers = array();
1570 | var $abbr_desciptions = array();
1571 | var $abbr_word_re = '';
1572 |
1573 | # Give the current footnote number.
1574 | var $footnote_counter = 1;
1575 |
1576 |
1577 | function setup() {
1578 | #
1579 | # Setting up Extra-specific variables.
1580 | #
1581 | parent::setup();
1582 |
1583 | $this->footnotes = array();
1584 | $this->footnotes_ordered = array();
1585 | $this->footnotes_ref_count = array();
1586 | $this->footnotes_numbers = array();
1587 | $this->abbr_desciptions = array();
1588 | $this->abbr_word_re = '';
1589 | $this->footnote_counter = 1;
1590 |
1591 | foreach ($this->predef_abbr as $abbr_word => $abbr_desc) {
1592 | if ($this->abbr_word_re)
1593 | $this->abbr_word_re .= '|';
1594 | $this->abbr_word_re .= preg_quote($abbr_word);
1595 | $this->abbr_desciptions[$abbr_word] = trim($abbr_desc);
1596 | }
1597 | }
1598 |
1599 | function teardown() {
1600 | #
1601 | # Clearing Extra-specific variables.
1602 | #
1603 | $this->footnotes = array();
1604 | $this->footnotes_ordered = array();
1605 | $this->footnotes_ref_count = array();
1606 | $this->footnotes_numbers = array();
1607 | $this->abbr_desciptions = array();
1608 | $this->abbr_word_re = '';
1609 |
1610 | parent::teardown();
1611 | }
1612 |
1613 |
1614 | ### Extra Attribute Parser ###
1615 |
1616 | # Expression to use to catch attributes (includes the braces)
1617 | var $id_class_attr_catch_re = '\{((?:[ ]*[#.][-_:a-zA-Z0-9]+){1,})[ ]*\}';
1618 | # Expression to use when parsing in a context when no capture is desired
1619 | var $id_class_attr_nocatch_re = '\{(?:[ ]*[#.][-_:a-zA-Z0-9]+){1,}[ ]*\}';
1620 |
1621 | function doExtraAttributes($tag_name, $attr) {
1622 | #
1623 | # Parse attributes caught by the $this->id_class_attr_catch_re expression
1624 | # and return the HTML-formatted list of attributes.
1625 | #
1626 | # Currently supported attributes are .class and #id.
1627 | #
1628 | if (empty($attr)) return "";
1629 |
1630 | # Split on components
1631 | preg_match_all('/[#.][-_:a-zA-Z0-9]+/', $attr, $matches);
1632 | $elements = $matches[0];
1633 |
1634 | # handle classes and ids (only first id taken into account)
1635 | $classes = array();
1636 | $id = false;
1637 | foreach ($elements as $element) {
1638 | if ($element{0} == '.') {
1639 | $classes[] = substr($element, 1);
1640 | } else if ($element{0} == '#') {
1641 | if ($id === false) $id = substr($element, 1);
1642 | }
1643 | }
1644 |
1645 | # compose attributes as string
1646 | $attr_str = "";
1647 | if (!empty($id)) {
1648 | $attr_str .= ' id="'.$id.'"';
1649 | }
1650 | if (!empty($classes)) {
1651 | $attr_str .= ' class="'.implode(" ", $classes).'"';
1652 | }
1653 | return $attr_str;
1654 | }
1655 |
1656 |
1657 | function stripLinkDefinitions($text) {
1658 | #
1659 | # Strips link definitions from text, stores the URLs and titles in
1660 | # hash references.
1661 | #
1662 | $less_than_tab = $this->tab_width - 1;
1663 |
1664 | # Link defs are in the form: ^[id]: url "optional title"
1665 | $text = preg_replace_callback('{
1666 | ^[ ]{0,'.$less_than_tab.'}\[(.+)\][ ]?: # id = $1
1667 | [ ]*
1668 | \n? # maybe *one* newline
1669 | [ ]*
1670 | (?:
1671 | <(.+?)> # url = $2
1672 | |
1673 | (\S+?) # url = $3
1674 | )
1675 | [ ]*
1676 | \n? # maybe one newline
1677 | [ ]*
1678 | (?:
1679 | (?<=\s) # lookbehind for whitespace
1680 | ["(]
1681 | (.*?) # title = $4
1682 | [")]
1683 | [ ]*
1684 | )? # title is optional
1685 | (?:[ ]* '.$this->id_class_attr_catch_re.' )? # $5 = extra id & class attr
1686 | (?:\n+|\Z)
1687 | }xm',
1688 | array(&$this, '_stripLinkDefinitions_callback'),
1689 | $text);
1690 | return $text;
1691 | }
1692 | function _stripLinkDefinitions_callback($matches) {
1693 | $link_id = strtolower($matches[1]);
1694 | $url = $matches[2] == '' ? $matches[3] : $matches[2];
1695 | $this->urls[$link_id] = $url;
1696 | $this->titles[$link_id] =& $matches[4];
1697 | $this->ref_attr[$link_id] = $this->doExtraAttributes("", $dummy =& $matches[5]);
1698 | return ''; # String that will replace the block
1699 | }
1700 |
1701 |
1702 | ### HTML Block Parser ###
1703 |
1704 | # Tags that are always treated as block tags:
1705 | var $block_tags_re = 'p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|address|form|fieldset|iframe|hr|legend|article|section|nav|aside|hgroup|header|footer|figcaption';
1706 |
1707 | # Tags treated as block tags only if the opening tag is alone on its line:
1708 | var $context_block_tags_re = 'script|noscript|ins|del|iframe|object|source|track|param|math|svg|canvas|audio|video';
1709 |
1710 | # Tags where markdown="1" default to span mode:
1711 | var $contain_span_tags_re = 'p|h[1-6]|li|dd|dt|td|th|legend|address';
1712 |
1713 | # Tags which must not have their contents modified, no matter where
1714 | # they appear:
1715 | var $clean_tags_re = 'script|math|svg';
1716 |
1717 | # Tags that do not need to be closed.
1718 | var $auto_close_tags_re = 'hr|img|param|source|track';
1719 |
1720 |
1721 | function hashHTMLBlocks($text) {
1722 | #
1723 | # Hashify HTML Blocks and "clean tags".
1724 | #
1725 | # We only want to do this for block-level HTML tags, such as headers,
1726 | # lists, and tables. That's because we still want to wrap s around
1727 | # "paragraphs" that are wrapped in non-block-level tags, such as anchors,
1728 | # phrase emphasis, and spans. The list of tags we're looking for is
1729 | # hard-coded.
1730 | #
1731 | # This works by calling _HashHTMLBlocks_InMarkdown, which then calls
1732 | # _HashHTMLBlocks_InHTML when it encounter block tags. When the markdown="1"
1733 | # attribute is found within a tag, _HashHTMLBlocks_InHTML calls back
1734 | # _HashHTMLBlocks_InMarkdown to handle the Markdown syntax within the tag.
1735 | # These two functions are calling each other. It's recursive!
1736 | #
1737 | if ($this->no_markup) return $text;
1738 |
1739 | #
1740 | # Call the HTML-in-Markdown hasher.
1741 | #
1742 | list($text, ) = $this->_hashHTMLBlocks_inMarkdown($text);
1743 |
1744 | return $text;
1745 | }
1746 | function _hashHTMLBlocks_inMarkdown($text, $indent = 0,
1747 | $enclosing_tag_re = '', $span = false)
1748 | {
1749 | #
1750 | # Parse markdown text, calling _HashHTMLBlocks_InHTML for block tags.
1751 | #
1752 | # * $indent is the number of space to be ignored when checking for code
1753 | # blocks. This is important because if we don't take the indent into
1754 | # account, something like this (which looks right) won't work as expected:
1755 | #
1756 | #
1757 | #
1758 | # Hello World. <-- Is this a Markdown code block or text?
1759 | # <-- Is this a Markdown code block or a real tag?
1760 | #
1761 | #
1762 | # If you don't like this, just don't indent the tag on which
1763 | # you apply the markdown="1" attribute.
1764 | #
1765 | # * If $enclosing_tag_re is not empty, stops at the first unmatched closing
1766 | # tag with that name. Nested tags supported.
1767 | #
1768 | # * If $span is true, text inside must treated as span. So any double
1769 | # newline will be replaced by a single newline so that it does not create
1770 | # paragraphs.
1771 | #
1772 | # Returns an array of that form: ( processed text , remaining text )
1773 | #
1774 | if ($text === '') return array('', '');
1775 |
1776 | # Regex to check for the presense of newlines around a block tag.
1777 | $newline_before_re = '/(?:^\n?|\n\n)*$/';
1778 | $newline_after_re =
1779 | '{
1780 | ^ # Start of text following the tag.
1781 | (?>[ ]*)? # Optional comment.
1782 | [ ]*\n # Must be followed by newline.
1783 | }xs';
1784 |
1785 | # Regex to match any tag.
1786 | $block_tag_re =
1787 | '{
1788 | ( # $2: Capture whole tag.
1789 | ? # Any opening or closing tag.
1790 | (?> # Tag name.
1791 | '.$this->block_tags_re.' |
1792 | '.$this->context_block_tags_re.' |
1793 | '.$this->clean_tags_re.' |
1794 | (?!\s)'.$enclosing_tag_re.'
1795 | )
1796 | (?:
1797 | (?=[\s"\'/a-zA-Z0-9]) # Allowed characters after tag name.
1798 | (?>
1799 | ".*?" | # Double quotes (can contain `>`)
1800 | \'.*?\' | # Single quotes (can contain `>`)
1801 | .+? # Anything but quotes and `>`.
1802 | )*?
1803 | )?
1804 | > # End of tag.
1805 | |
1806 | # HTML Comment
1807 | |
1808 | <\?.*?\?> | <%.*?%> # Processing instruction
1809 | |
1810 | # CData Block
1811 | |
1812 | # Code span marker
1813 | `+
1814 | '. ( !$span ? ' # If not in span.
1815 | |
1816 | # Indented code block
1817 | (?: ^[ ]*\n | ^ | \n[ ]*\n )
1818 | [ ]{'.($indent+4).'}[^\n]* \n
1819 | (?>
1820 | (?: [ ]{'.($indent+4).'}[^\n]* | [ ]* ) \n
1821 | )*
1822 | |
1823 | # Fenced code block marker
1824 | (?<= ^ | \n )
1825 | [ ]{0,'.($indent+3).'}~{3,}
1826 | [ ]*
1827 | (?:
1828 | \.?[-_:a-zA-Z0-9]+ # standalone class name
1829 | |
1830 | '.$this->id_class_attr_nocatch_re.' # extra attributes
1831 | )?
1832 | [ ]*
1833 | \n
1834 | ' : '' ). ' # End (if not is span).
1835 | )
1836 | }xs';
1837 |
1838 |
1839 | $depth = 0; # Current depth inside the tag tree.
1840 | $parsed = ""; # Parsed text that will be returned.
1841 |
1842 | #
1843 | # Loop through every tag until we find the closing tag of the parent
1844 | # or loop until reaching the end of text if no parent tag specified.
1845 | #
1846 | do {
1847 | #
1848 | # Split the text using the first $tag_match pattern found.
1849 | # Text before pattern will be first in the array, text after
1850 | # pattern will be at the end, and between will be any catches made
1851 | # by the pattern.
1852 | #
1853 | $parts = preg_split($block_tag_re, $text, 2,
1854 | PREG_SPLIT_DELIM_CAPTURE);
1855 |
1856 | # If in Markdown span mode, add a empty-string span-level hash
1857 | # after each newline to prevent triggering any block element.
1858 | if ($span) {
1859 | $void = $this->hashPart("", ':');
1860 | $newline = "$void\n";
1861 | $parts[0] = $void . str_replace("\n", $newline, $parts[0]) . $void;
1862 | }
1863 |
1864 | $parsed .= $parts[0]; # Text before current tag.
1865 |
1866 | # If end of $text has been reached. Stop loop.
1867 | if (count($parts) < 3) {
1868 | $text = "";
1869 | break;
1870 | }
1871 |
1872 | $tag = $parts[1]; # Tag to handle.
1873 | $text = $parts[2]; # Remaining text after current tag.
1874 | $tag_re = preg_quote($tag); # For use in a regular expression.
1875 |
1876 | #
1877 | # Check for: Code span marker
1878 | #
1879 | if ($tag{0} == "`") {
1880 | # Find corresponding end marker.
1881 | $tag_re = preg_quote($tag);
1882 | if (preg_match('{^(?>.+?|\n(?!\n))*?(?.*\n)*?[ ]{'.($fence_indent).'}'.$fence_re.'[ ]*(?:\n|$)}', $text,
1902 | $matches))
1903 | {
1904 | # End marker found: pass text unchanged until marker.
1905 | $parsed .= $tag . $matches[0];
1906 | $text = substr($text, strlen($matches[0]));
1907 | }
1908 | else {
1909 | # No end marker: just skip it.
1910 | $parsed .= $tag;
1911 | }
1912 | }
1913 | #
1914 | # Check for: Indented code block.
1915 | #
1916 | else if ($tag{0} == "\n" || $tag{0} == " ") {
1917 | # Indented code block: pass it unchanged, will be handled
1918 | # later.
1919 | $parsed .= $tag;
1920 | }
1921 | #
1922 | # Check for: Opening Block level tag or
1923 | # Opening Context Block tag (like ins and del)
1924 | # used as a block tag (tag is alone on it's line).
1925 | #
1926 | else if (preg_match('{^<(?:'.$this->block_tags_re.')\b}', $tag) ||
1927 | ( preg_match('{^<(?:'.$this->context_block_tags_re.')\b}', $tag) &&
1928 | preg_match($newline_before_re, $parsed) &&
1929 | preg_match($newline_after_re, $text) )
1930 | )
1931 | {
1932 | # Need to parse tag and following text using the HTML parser.
1933 | list($block_text, $text) =
1934 | $this->_hashHTMLBlocks_inHTML($tag . $text, "hashBlock", true);
1935 |
1936 | # Make sure it stays outside of any paragraph by adding newlines.
1937 | $parsed .= "\n\n$block_text\n\n";
1938 | }
1939 | #
1940 | # Check for: Clean tag (like script, math)
1941 | # HTML Comments, processing instructions.
1942 | #
1943 | else if (preg_match('{^<(?:'.$this->clean_tags_re.')\b}', $tag) ||
1944 | $tag{1} == '!' || $tag{1} == '?')
1945 | {
1946 | # Need to parse tag and following text using the HTML parser.
1947 | # (don't check for markdown attribute)
1948 | list($block_text, $text) =
1949 | $this->_hashHTMLBlocks_inHTML($tag . $text, "hashClean", false);
1950 |
1951 | $parsed .= $block_text;
1952 | }
1953 | #
1954 | # Check for: Tag with same name as enclosing tag.
1955 | #
1956 | else if ($enclosing_tag_re !== '' &&
1957 | # Same name as enclosing tag.
1958 | preg_match('{^?(?:'.$enclosing_tag_re.')\b}', $tag))
1959 | {
1960 | #
1961 | # Increase/decrease nested tag count.
1962 | #
1963 | if ($tag{1} == '/') $depth--;
1964 | else if ($tag{strlen($tag)-2} != '/') $depth++;
1965 |
1966 | if ($depth < 0) {
1967 | #
1968 | # Going out of parent element. Clean up and break so we
1969 | # return to the calling function.
1970 | #
1971 | $text = $tag . $text;
1972 | break;
1973 | }
1974 |
1975 | $parsed .= $tag;
1976 | }
1977 | else {
1978 | $parsed .= $tag;
1979 | }
1980 | } while ($depth >= 0);
1981 |
1982 | return array($parsed, $text);
1983 | }
1984 | function _hashHTMLBlocks_inHTML($text, $hash_method, $md_attr) {
1985 | #
1986 | # Parse HTML, calling _HashHTMLBlocks_InMarkdown for block tags.
1987 | #
1988 | # * Calls $hash_method to convert any blocks.
1989 | # * Stops when the first opening tag closes.
1990 | # * $md_attr indicate if the use of the `markdown="1"` attribute is allowed.
1991 | # (it is not inside clean tags)
1992 | #
1993 | # Returns an array of that form: ( processed text , remaining text )
1994 | #
1995 | if ($text === '') return array('', '');
1996 |
1997 | # Regex to match `markdown` attribute inside of a tag.
1998 | $markdown_attr_re = '
1999 | {
2000 | \s* # Eat whitespace before the `markdown` attribute
2001 | markdown
2002 | \s*=\s*
2003 | (?>
2004 | (["\']) # $1: quote delimiter
2005 | (.*?) # $2: attribute value
2006 | \1 # matching delimiter
2007 | |
2008 | ([^\s>]*) # $3: unquoted attribute value
2009 | )
2010 | () # $4: make $3 always defined (avoid warnings)
2011 | }xs';
2012 |
2013 | # Regex to match any tag.
2014 | $tag_re = '{
2015 | ( # $2: Capture whole tag.
2016 | ? # Any opening or closing tag.
2017 | [\w:$]+ # Tag name.
2018 | (?:
2019 | (?=[\s"\'/a-zA-Z0-9]) # Allowed characters after tag name.
2020 | (?>
2021 | ".*?" | # Double quotes (can contain `>`)
2022 | \'.*?\' | # Single quotes (can contain `>`)
2023 | .+? # Anything but quotes and `>`.
2024 | )*?
2025 | )?
2026 | > # End of tag.
2027 | |
2028 | # HTML Comment
2029 | |
2030 | <\?.*?\?> | <%.*?%> # Processing instruction
2031 | |
2032 | # CData Block
2033 | )
2034 | }xs';
2035 |
2036 | $original_text = $text; # Save original text in case of faliure.
2037 |
2038 | $depth = 0; # Current depth inside the tag tree.
2039 | $block_text = ""; # Temporary text holder for current text.
2040 | $parsed = ""; # Parsed text that will be returned.
2041 |
2042 | #
2043 | # Get the name of the starting tag.
2044 | # (This pattern makes $base_tag_name_re safe without quoting.)
2045 | #
2046 | if (preg_match('/^<([\w:$]*)\b/', $text, $matches))
2047 | $base_tag_name_re = $matches[1];
2048 |
2049 | #
2050 | # Loop through every tag until we find the corresponding closing tag.
2051 | #
2052 | do {
2053 | #
2054 | # Split the text using the first $tag_match pattern found.
2055 | # Text before pattern will be first in the array, text after
2056 | # pattern will be at the end, and between will be any catches made
2057 | # by the pattern.
2058 | #
2059 | $parts = preg_split($tag_re, $text, 2, PREG_SPLIT_DELIM_CAPTURE);
2060 |
2061 | if (count($parts) < 3) {
2062 | #
2063 | # End of $text reached with unbalenced tag(s).
2064 | # In that case, we return original text unchanged and pass the
2065 | # first character as filtered to prevent an infinite loop in the
2066 | # parent function.
2067 | #
2068 | return array($original_text{0}, substr($original_text, 1));
2069 | }
2070 |
2071 | $block_text .= $parts[0]; # Text before current tag.
2072 | $tag = $parts[1]; # Tag to handle.
2073 | $text = $parts[2]; # Remaining text after current tag.
2074 |
2075 | #
2076 | # Check for: Auto-close tag (like
)
2077 | # Comments and Processing Instructions.
2078 | #
2079 | if (preg_match('{^?(?:'.$this->auto_close_tags_re.')\b}', $tag) ||
2080 | $tag{1} == '!' || $tag{1} == '?')
2081 | {
2082 | # Just add the tag to the block as if it was text.
2083 | $block_text .= $tag;
2084 | }
2085 | else {
2086 | #
2087 | # Increase/decrease nested tag count. Only do so if
2088 | # the tag's name match base tag's.
2089 | #
2090 | if (preg_match('{^?'.$base_tag_name_re.'\b}', $tag)) {
2091 | if ($tag{1} == '/') $depth--;
2092 | else if ($tag{strlen($tag)-2} != '/') $depth++;
2093 | }
2094 |
2095 | #
2096 | # Check for `markdown="1"` attribute and handle it.
2097 | #
2098 | if ($md_attr &&
2099 | preg_match($markdown_attr_re, $tag, $attr_m) &&
2100 | preg_match('/^1|block|span$/', $attr_m[2] . $attr_m[3]))
2101 | {
2102 | # Remove `markdown` attribute from opening tag.
2103 | $tag = preg_replace($markdown_attr_re, '', $tag);
2104 |
2105 | # Check if text inside this tag must be parsed in span mode.
2106 | $this->mode = $attr_m[2] . $attr_m[3];
2107 | $span_mode = $this->mode == 'span' || $this->mode != 'block' &&
2108 | preg_match('{^<(?:'.$this->contain_span_tags_re.')\b}', $tag);
2109 |
2110 | # Calculate indent before tag.
2111 | if (preg_match('/(?:^|\n)( *?)(?! ).*?$/', $block_text, $matches)) {
2112 | $strlen = $this->utf8_strlen;
2113 | $indent = $strlen($matches[1], 'UTF-8');
2114 | } else {
2115 | $indent = 0;
2116 | }
2117 |
2118 | # End preceding block with this tag.
2119 | $block_text .= $tag;
2120 | $parsed .= $this->$hash_method($block_text);
2121 |
2122 | # Get enclosing tag name for the ParseMarkdown function.
2123 | # (This pattern makes $tag_name_re safe without quoting.)
2124 | preg_match('/^<([\w:$]*)\b/', $tag, $matches);
2125 | $tag_name_re = $matches[1];
2126 |
2127 | # Parse the content using the HTML-in-Markdown parser.
2128 | list ($block_text, $text)
2129 | = $this->_hashHTMLBlocks_inMarkdown($text, $indent,
2130 | $tag_name_re, $span_mode);
2131 |
2132 | # Outdent markdown text.
2133 | if ($indent > 0) {
2134 | $block_text = preg_replace("/^[ ]{1,$indent}/m", "",
2135 | $block_text);
2136 | }
2137 |
2138 | # Append tag content to parsed text.
2139 | if (!$span_mode) $parsed .= "\n\n$block_text\n\n";
2140 | else $parsed .= "$block_text";
2141 |
2142 | # Start over with a new block.
2143 | $block_text = "";
2144 | }
2145 | else $block_text .= $tag;
2146 | }
2147 |
2148 | } while ($depth > 0);
2149 |
2150 | #
2151 | # Hash last block text that wasn't processed inside the loop.
2152 | #
2153 | $parsed .= $this->$hash_method($block_text);
2154 |
2155 | return array($parsed, $text);
2156 | }
2157 |
2158 |
2159 | function hashClean($text) {
2160 | #
2161 | # Called whenever a tag must be hashed when a function inserts a "clean" tag
2162 | # in $text, it passes through this function and is automaticaly escaped,
2163 | # blocking invalid nested overlap.
2164 | #
2165 | return $this->hashPart($text, 'C');
2166 | }
2167 |
2168 |
2169 | function doAnchors($text) {
2170 | #
2171 | # Turn Markdown link shortcuts into XHTML tags.
2172 | #
2173 | if ($this->in_anchor) return $text;
2174 | $this->in_anchor = true;
2175 |
2176 | #
2177 | # First, handle reference-style links: [link text] [id]
2178 | #
2179 | $text = preg_replace_callback('{
2180 | ( # wrap whole match in $1
2181 | \[
2182 | ('.$this->nested_brackets_re.') # link text = $2
2183 | \]
2184 |
2185 | [ ]? # one optional space
2186 | (?:\n[ ]*)? # one optional newline followed by spaces
2187 |
2188 | \[
2189 | (.*?) # id = $3
2190 | \]
2191 | )
2192 | }xs',
2193 | array(&$this, '_doAnchors_reference_callback'), $text);
2194 |
2195 | #
2196 | # Next, inline-style links: [link text](url "optional title")
2197 | #
2198 | $text = preg_replace_callback('{
2199 | ( # wrap whole match in $1
2200 | \[
2201 | ('.$this->nested_brackets_re.') # link text = $2
2202 | \]
2203 | \( # literal paren
2204 | [ \n]*
2205 | (?:
2206 | <(.+?)> # href = $3
2207 | |
2208 | ('.$this->nested_url_parenthesis_re.') # href = $4
2209 | )
2210 | [ \n]*
2211 | ( # $5
2212 | ([\'"]) # quote char = $6
2213 | (.*?) # Title = $7
2214 | \6 # matching quote
2215 | [ \n]* # ignore any spaces/tabs between closing quote and )
2216 | )? # title is optional
2217 | \)
2218 | (?:[ ]? '.$this->id_class_attr_catch_re.' )? # $8 = id/class attributes
2219 | )
2220 | }xs',
2221 | array(&$this, '_doAnchors_inline_callback'), $text);
2222 |
2223 | #
2224 | # Last, handle reference-style shortcuts: [link text]
2225 | # These must come last in case you've also got [link text][1]
2226 | # or [link text](/foo)
2227 | #
2228 | $text = preg_replace_callback('{
2229 | ( # wrap whole match in $1
2230 | \[
2231 | ([^\[\]]+) # link text = $2; can\'t contain [ or ]
2232 | \]
2233 | )
2234 | }xs',
2235 | array(&$this, '_doAnchors_reference_callback'), $text);
2236 |
2237 | $this->in_anchor = false;
2238 | return $text;
2239 | }
2240 | function _doAnchors_reference_callback($matches) {
2241 | $whole_match = $matches[1];
2242 | $link_text = $matches[2];
2243 | $link_id =& $matches[3];
2244 |
2245 | if ($link_id == "") {
2246 | # for shortcut links like [this][] or [this].
2247 | $link_id = $link_text;
2248 | }
2249 |
2250 | # lower-case and turn embedded newlines into spaces
2251 | $link_id = strtolower($link_id);
2252 | $link_id = preg_replace('{[ ]?\n}', ' ', $link_id);
2253 |
2254 | if (isset($this->urls[$link_id])) {
2255 | $url = $this->urls[$link_id];
2256 | $url = $this->encodeAttribute($url);
2257 |
2258 | $result = "titles[$link_id] ) ) {
2260 | $title = $this->titles[$link_id];
2261 | $title = $this->encodeAttribute($title);
2262 | $result .= " title=\"$title\"";
2263 | }
2264 | if (isset($this->ref_attr[$link_id]))
2265 | $result .= $this->ref_attr[$link_id];
2266 |
2267 | $link_text = $this->runSpanGamut($link_text);
2268 | $result .= ">$link_text";
2269 | $result = $this->hashPart($result);
2270 | }
2271 | else {
2272 | $result = $whole_match;
2273 | }
2274 | return $result;
2275 | }
2276 | function _doAnchors_inline_callback($matches) {
2277 | $whole_match = $matches[1];
2278 | $link_text = $this->runSpanGamut($matches[2]);
2279 | $url = $matches[3] == '' ? $matches[4] : $matches[3];
2280 | $title =& $matches[7];
2281 | $attr = $this->doExtraAttributes("a", $dummy =& $matches[8]);
2282 |
2283 |
2284 | $url = $this->encodeAttribute($url);
2285 |
2286 | $result = "encodeAttribute($title);
2289 | $result .= " title=\"$title\"";
2290 | }
2291 | $result .= $attr;
2292 |
2293 | $link_text = $this->runSpanGamut($link_text);
2294 | $result .= ">$link_text";
2295 |
2296 | return $this->hashPart($result);
2297 | }
2298 |
2299 |
2300 | function doImages($text) {
2301 | #
2302 | # Turn Markdown image shortcuts into
tags.
2303 | #
2304 | #
2305 | # First, handle reference-style labeled images: ![alt text][id]
2306 | #
2307 | $text = preg_replace_callback('{
2308 | ( # wrap whole match in $1
2309 | !\[
2310 | ('.$this->nested_brackets_re.') # alt text = $2
2311 | \]
2312 |
2313 | [ ]? # one optional space
2314 | (?:\n[ ]*)? # one optional newline followed by spaces
2315 |
2316 | \[
2317 | (.*?) # id = $3
2318 | \]
2319 |
2320 | )
2321 | }xs',
2322 | array(&$this, '_doImages_reference_callback'), $text);
2323 |
2324 | #
2325 | # Next, handle inline images: 
2326 | # Don't forget: encode * and _
2327 | #
2328 | $text = preg_replace_callback('{
2329 | ( # wrap whole match in $1
2330 | !\[
2331 | ('.$this->nested_brackets_re.') # alt text = $2
2332 | \]
2333 | \s? # One optional whitespace character
2334 | \( # literal paren
2335 | [ \n]*
2336 | (?:
2337 | <(\S*)> # src url = $3
2338 | |
2339 | ('.$this->nested_url_parenthesis_re.') # src url = $4
2340 | )
2341 | [ \n]*
2342 | ( # $5
2343 | ([\'"]) # quote char = $6
2344 | (.*?) # title = $7
2345 | \6 # matching quote
2346 | [ \n]*
2347 | )? # title is optional
2348 | \)
2349 | (?:[ ]? '.$this->id_class_attr_catch_re.' )? # $8 = id/class attributes
2350 | )
2351 | }xs',
2352 | array(&$this, '_doImages_inline_callback'), $text);
2353 |
2354 | return $text;
2355 | }
2356 | function _doImages_reference_callback($matches) {
2357 | $whole_match = $matches[1];
2358 | $alt_text = $matches[2];
2359 | $link_id = strtolower($matches[3]);
2360 |
2361 | if ($link_id == "") {
2362 | $link_id = strtolower($alt_text); # for shortcut links like ![this][].
2363 | }
2364 |
2365 | $alt_text = $this->encodeAttribute($alt_text);
2366 | if (isset($this->urls[$link_id])) {
2367 | $url = $this->encodeAttribute($this->urls[$link_id]);
2368 | $result = "
titles[$link_id])) {
2370 | $title = $this->titles[$link_id];
2371 | $title = $this->encodeAttribute($title);
2372 | $result .= " title=\"$title\"";
2373 | }
2374 | if (isset($this->ref_attr[$link_id]))
2375 | $result .= $this->ref_attr[$link_id];
2376 | $result .= $this->empty_element_suffix;
2377 | $result = $this->hashPart($result);
2378 | }
2379 | else {
2380 | # If there's no such link ID, leave intact:
2381 | $result = $whole_match;
2382 | }
2383 |
2384 | return $result;
2385 | }
2386 | function _doImages_inline_callback($matches) {
2387 | $whole_match = $matches[1];
2388 | $alt_text = $matches[2];
2389 | $url = $matches[3] == '' ? $matches[4] : $matches[3];
2390 | $title =& $matches[7];
2391 | $attr = $this->doExtraAttributes("img", $dummy =& $matches[8]);
2392 |
2393 | $alt_text = $this->encodeAttribute($alt_text);
2394 | $url = $this->encodeAttribute($url);
2395 | $result = "
encodeAttribute($title);
2398 | $result .= " title=\"$title\""; # $title already quoted
2399 | }
2400 | $result .= $attr;
2401 | $result .= $this->empty_element_suffix;
2402 |
2403 | return $this->hashPart($result);
2404 | }
2405 |
2406 |
2407 | function doHeaders($text) {
2408 | #
2409 | # Redefined to add id and class attribute support.
2410 | #
2411 | # Setext-style headers:
2412 | # Header 1 {#header1}
2413 | # ========
2414 | #
2415 | # Header 2 {#header2 .class1 .class2}
2416 | # --------
2417 | #
2418 | $text = preg_replace_callback(
2419 | '{
2420 | (^.+?) # $1: Header text
2421 | (?:[ ]+ '.$this->id_class_attr_catch_re.' )? # $3 = id/class attributes
2422 | [ ]*\n(=+|-+)[ ]*\n+ # $3: Header footer
2423 | }mx',
2424 | array(&$this, '_doHeaders_callback_setext'), $text);
2425 |
2426 | # atx-style headers:
2427 | # # Header 1 {#header1}
2428 | # ## Header 2 {#header2}
2429 | # ## Header 2 with closing hashes ## {#header3.class1.class2}
2430 | # ...
2431 | # ###### Header 6 {.class2}
2432 | #
2433 | $text = preg_replace_callback('{
2434 | ^(\#{1,6}) # $1 = string of #\'s
2435 | [ ]*
2436 | (.+?) # $2 = Header text
2437 | [ ]*
2438 | \#* # optional closing #\'s (not counted)
2439 | (?:[ ]+ '.$this->id_class_attr_catch_re.' )? # $3 = id/class attributes
2440 | [ ]*
2441 | \n+
2442 | }xm',
2443 | array(&$this, '_doHeaders_callback_atx'), $text);
2444 |
2445 | return $text;
2446 | }
2447 | function _doHeaders_callback_setext($matches) {
2448 | if ($matches[3] == '-' && preg_match('{^- }', $matches[1]))
2449 | return $matches[0];
2450 | $level = $matches[3]{0} == '=' ? 1 : 2;
2451 | $attr = $this->doExtraAttributes("h$level", $dummy =& $matches[2]);
2452 | $block = "".$this->runSpanGamut($matches[1])." ";
2453 | return "\n" . $this->hashBlock($block) . "\n\n";
2454 | }
2455 | function _doHeaders_callback_atx($matches) {
2456 | $level = strlen($matches[1]);
2457 | $attr = $this->doExtraAttributes("h$level", $dummy =& $matches[3]);
2458 | $block = "".$this->runSpanGamut($matches[2])." ";
2459 | return "\n" . $this->hashBlock($block) . "\n\n";
2460 | }
2461 |
2462 |
2463 | function doTables($text) {
2464 | #
2465 | # Form HTML tables.
2466 | #
2467 | $less_than_tab = $this->tab_width - 1;
2468 | #
2469 | # Find tables with leading pipe.
2470 | #
2471 | # | Header 1 | Header 2
2472 | # | -------- | --------
2473 | # | Cell 1 | Cell 2
2474 | # | Cell 3 | Cell 4
2475 | #
2476 | $text = preg_replace_callback('
2477 | {
2478 | ^ # Start of a line
2479 | [ ]{0,'.$less_than_tab.'} # Allowed whitespace.
2480 | [|] # Optional leading pipe (present)
2481 | (.+) \n # $1: Header row (at least one pipe)
2482 |
2483 | [ ]{0,'.$less_than_tab.'} # Allowed whitespace.
2484 | [|] ([ ]*[-:]+[-| :]*) \n # $2: Header underline
2485 |
2486 | ( # $3: Cells
2487 | (?>
2488 | [ ]* # Allowed whitespace.
2489 | [|] .* \n # Row content.
2490 | )*
2491 | )
2492 | (?=\n|\Z) # Stop at final double newline.
2493 | }xm',
2494 | array(&$this, '_doTable_leadingPipe_callback'), $text);
2495 |
2496 | #
2497 | # Find tables without leading pipe.
2498 | #
2499 | # Header 1 | Header 2
2500 | # -------- | --------
2501 | # Cell 1 | Cell 2
2502 | # Cell 3 | Cell 4
2503 | #
2504 | $text = preg_replace_callback('
2505 | {
2506 | ^ # Start of a line
2507 | [ ]{0,'.$less_than_tab.'} # Allowed whitespace.
2508 | (\S.*[|].*) \n # $1: Header row (at least one pipe)
2509 |
2510 | [ ]{0,'.$less_than_tab.'} # Allowed whitespace.
2511 | ([-:]+[ ]*[|][-| :]*) \n # $2: Header underline
2512 |
2513 | ( # $3: Cells
2514 | (?>
2515 | .* [|] .* \n # Row content
2516 | )*
2517 | )
2518 | (?=\n|\Z) # Stop at final double newline.
2519 | }xm',
2520 | array(&$this, '_DoTable_callback'), $text);
2521 |
2522 | return $text;
2523 | }
2524 | function _doTable_leadingPipe_callback($matches) {
2525 | $head = $matches[1];
2526 | $underline = $matches[2];
2527 | $content = $matches[3];
2528 |
2529 | # Remove leading pipe for each row.
2530 | $content = preg_replace('/^ *[|]/m', '', $content);
2531 |
2532 | return $this->_doTable_callback(array($matches[0], $head, $underline, $content));
2533 | }
2534 | function _doTable_callback($matches) {
2535 | $head = $matches[1];
2536 | $underline = $matches[2];
2537 | $content = $matches[3];
2538 |
2539 | # Remove any tailing pipes for each line.
2540 | $head = preg_replace('/[|] *$/m', '', $head);
2541 | $underline = preg_replace('/[|] *$/m', '', $underline);
2542 | $content = preg_replace('/[|] *$/m', '', $content);
2543 |
2544 | # Reading alignement from header underline.
2545 | $separators = preg_split('/ *[|] */', $underline);
2546 | foreach ($separators as $n => $s) {
2547 | if (preg_match('/^ *-+: *$/', $s)) $attr[$n] = ' align="right"';
2548 | else if (preg_match('/^ *:-+: *$/', $s))$attr[$n] = ' align="center"';
2549 | else if (preg_match('/^ *:-+ *$/', $s)) $attr[$n] = ' align="left"';
2550 | else $attr[$n] = '';
2551 | }
2552 |
2553 | # Parsing span elements, including code spans, character escapes,
2554 | # and inline HTML tags, so that pipes inside those gets ignored.
2555 | $head = $this->parseSpan($head);
2556 | $headers = preg_split('/ *[|] */', $head);
2557 | $col_count = count($headers);
2558 | $attr = array_pad($attr, $col_count, '');
2559 |
2560 | # Write column headers.
2561 | $text = "\n";
2562 | $text .= "\n";
2563 | $text .= "\n";
2564 | foreach ($headers as $n => $header)
2565 | $text .= " ".$this->runSpanGamut(trim($header))." \n";
2566 | $text .= " \n";
2567 | $text .= "\n";
2568 |
2569 | # Split content by row.
2570 | $rows = explode("\n", trim($content, "\n"));
2571 |
2572 | $text .= "\n";
2573 | foreach ($rows as $row) {
2574 | # Parsing span elements, including code spans, character escapes,
2575 | # and inline HTML tags, so that pipes inside those gets ignored.
2576 | $row = $this->parseSpan($row);
2577 |
2578 | # Split row by cell.
2579 | $row_cells = preg_split('/ *[|] */', $row, $col_count);
2580 | $row_cells = array_pad($row_cells, $col_count, '');
2581 |
2582 | $text .= "\n";
2583 | foreach ($row_cells as $n => $cell)
2584 | $text .= " ".$this->runSpanGamut(trim($cell))." \n";
2585 | $text .= " \n";
2586 | }
2587 | $text .= "\n";
2588 | $text .= "
";
2589 |
2590 | return $this->hashBlock($text) . "\n";
2591 | }
2592 |
2593 |
2594 | function doDefLists($text) {
2595 | #
2596 | # Form HTML definition lists.
2597 | #
2598 | $less_than_tab = $this->tab_width - 1;
2599 |
2600 | # Re-usable pattern to match any entire dl list:
2601 | $whole_list_re = '(?>
2602 | ( # $1 = whole list
2603 | ( # $2
2604 | [ ]{0,'.$less_than_tab.'}
2605 | ((?>.*\S.*\n)+) # $3 = defined term
2606 | \n?
2607 | [ ]{0,'.$less_than_tab.'}:[ ]+ # colon starting definition
2608 | )
2609 | (?s:.+?)
2610 | ( # $4
2611 | \z
2612 | |
2613 | \n{2,}
2614 | (?=\S)
2615 | (?! # Negative lookahead for another term
2616 | [ ]{0,'.$less_than_tab.'}
2617 | (?: \S.*\n )+? # defined term
2618 | \n?
2619 | [ ]{0,'.$less_than_tab.'}:[ ]+ # colon starting definition
2620 | )
2621 | (?! # Negative lookahead for another definition
2622 | [ ]{0,'.$less_than_tab.'}:[ ]+ # colon starting definition
2623 | )
2624 | )
2625 | )
2626 | )'; // mx
2627 |
2628 | $text = preg_replace_callback('{
2629 | (?>\A\n?|(?<=\n\n))
2630 | '.$whole_list_re.'
2631 | }mx',
2632 | array(&$this, '_doDefLists_callback'), $text);
2633 |
2634 | return $text;
2635 | }
2636 | function _doDefLists_callback($matches) {
2637 | # Re-usable patterns to match list item bullets and number markers:
2638 | $list = $matches[1];
2639 |
2640 | # Turn double returns into triple returns, so that we can make a
2641 | # paragraph for the last item in a list, if necessary:
2642 | $result = trim($this->processDefListItems($list));
2643 | $result = "\n" . $result . "\n
";
2644 | return $this->hashBlock($result) . "\n\n";
2645 | }
2646 |
2647 |
2648 | function processDefListItems($list_str) {
2649 | #
2650 | # Process the contents of a single definition list, splitting it
2651 | # into individual term and definition list items.
2652 | #
2653 | $less_than_tab = $this->tab_width - 1;
2654 |
2655 | # trim trailing blank lines:
2656 | $list_str = preg_replace("/\n{2,}\\z/", "\n", $list_str);
2657 |
2658 | # Process definition terms.
2659 | $list_str = preg_replace_callback('{
2660 | (?>\A\n?|\n\n+) # leading line
2661 | ( # definition terms = $1
2662 | [ ]{0,'.$less_than_tab.'} # leading whitespace
2663 | (?!\:[ ]|[ ]) # negative lookahead for a definition
2664 | # mark (colon) or more whitespace.
2665 | (?> \S.* \n)+? # actual term (not whitespace).
2666 | )
2667 | (?=\n?[ ]{0,3}:[ ]) # lookahead for following line feed
2668 | # with a definition mark.
2669 | }xm',
2670 | array(&$this, '_processDefListItems_callback_dt'), $list_str);
2671 |
2672 | # Process actual definitions.
2673 | $list_str = preg_replace_callback('{
2674 | \n(\n+)? # leading line = $1
2675 | ( # marker space = $2
2676 | [ ]{0,'.$less_than_tab.'} # whitespace before colon
2677 | \:[ ]+ # definition mark (colon)
2678 | )
2679 | ((?s:.+?)) # definition text = $3
2680 | (?= \n+ # stop at next definition mark,
2681 | (?: # next term or end of text
2682 | [ ]{0,'.$less_than_tab.'} \:[ ] |
2683 | | \z
2684 | )
2685 | )
2686 | }xm',
2687 | array(&$this, '_processDefListItems_callback_dd'), $list_str);
2688 |
2689 | return $list_str;
2690 | }
2691 | function _processDefListItems_callback_dt($matches) {
2692 | $terms = explode("\n", trim($matches[1]));
2693 | $text = '';
2694 | foreach ($terms as $term) {
2695 | $term = $this->runSpanGamut(trim($term));
2696 | $text .= "\n" . $term . " ";
2697 | }
2698 | return $text . "\n";
2699 | }
2700 | function _processDefListItems_callback_dd($matches) {
2701 | $leading_line = $matches[1];
2702 | $marker_space = $matches[2];
2703 | $def = $matches[3];
2704 |
2705 | if ($leading_line || preg_match('/\n{2,}/', $def)) {
2706 | # Replace marker with the appropriate whitespace indentation
2707 | $def = str_repeat(' ', strlen($marker_space)) . $def;
2708 | $def = $this->runBlockGamut($this->outdent($def . "\n\n"));
2709 | $def = "\n". $def ."\n";
2710 | }
2711 | else {
2712 | $def = rtrim($def);
2713 | $def = $this->runSpanGamut($this->outdent($def));
2714 | }
2715 |
2716 | return "\n " . $def . " \n";
2717 | }
2718 |
2719 |
2720 | function doFencedCodeBlocks($text) {
2721 | #
2722 | # Adding the fenced code block syntax to regular Markdown:
2723 | #
2724 | # ~~~
2725 | # Code block
2726 | # ~~~
2727 | #
2728 | $less_than_tab = $this->tab_width;
2729 |
2730 | $text = preg_replace_callback('{
2731 | (?:\n|\A)
2732 | # 1: Opening marker
2733 | (
2734 | ~{3,} # Marker: three tilde or more.
2735 | )
2736 | [ ]*
2737 | (?:
2738 | \.?([-_:a-zA-Z0-9]+) # 2: standalone class name
2739 | |
2740 | '.$this->id_class_attr_catch_re.' # 3: Extra attributes
2741 | )?
2742 | [ ]* \n # Whitespace and newline following marker.
2743 |
2744 | # 4: Content
2745 | (
2746 | (?>
2747 | (?!\1 [ ]* \n) # Not a closing marker.
2748 | .*\n+
2749 | )+
2750 | )
2751 |
2752 | # Closing marker.
2753 | \1 [ ]* \n
2754 | }xm',
2755 | array(&$this, '_doFencedCodeBlocks_callback'), $text);
2756 |
2757 | return $text;
2758 | }
2759 | function _doFencedCodeBlocks_callback($matches) {
2760 | $classname =& $matches[2];
2761 | $attrs =& $matches[3];
2762 | $codeblock = $matches[4];
2763 | $codeblock = htmlspecialchars($codeblock, ENT_NOQUOTES);
2764 | $codeblock = preg_replace_callback('/^\n+/',
2765 | array(&$this, '_doFencedCodeBlocks_newlines'), $codeblock);
2766 |
2767 | if ($classname != "") {
2768 | if ($classname{0} == '.')
2769 | $classname = substr($classname, 1);
2770 | $attr_str = ' class="'.$this->code_class_prefix.$classname.'"';
2771 | } else {
2772 | $attr_str = $this->doExtraAttributes($this->code_attr_on_pre ? "pre" : "code", $attrs);
2773 | }
2774 | $pre_attr_str = $this->code_attr_on_pre ? $attr_str : '';
2775 | $code_attr_str = $this->code_attr_on_pre ? '' : $attr_str;
2776 | $codeblock = "$codeblock
";
2777 |
2778 | return "\n\n".$this->hashBlock($codeblock)."\n\n";
2779 | }
2780 | function _doFencedCodeBlocks_newlines($matches) {
2781 | return str_repeat("
empty_element_suffix",
2782 | strlen($matches[0]));
2783 | }
2784 |
2785 |
2786 | #
2787 | # Redefining emphasis markers so that emphasis by underscore does not
2788 | # work in the middle of a word.
2789 | #
2790 | var $em_relist = array(
2791 | '' => '(?:(? '(?<=\S|^)(? '(?<=\S|^)(? '(?:(? '(?<=\S|^)(? '(?<=\S|^)(? '(?:(? '(?<=\S|^)(? '(?<=\S|^)(? tags
2811 | #
2812 | # Strip leading and trailing lines:
2813 | $text = preg_replace('/\A\n+|\n+\z/', '', $text);
2814 |
2815 | $grafs = preg_split('/\n{2,}/', $text, -1, PREG_SPLIT_NO_EMPTY);
2816 |
2817 | #
2818 | # Wrap tags and unhashify HTML blocks
2819 | #
2820 | foreach ($grafs as $key => $value) {
2821 | $value = trim($this->runSpanGamut($value));
2822 |
2823 | # Check if this should be enclosed in a paragraph.
2824 | # Clean tag hashes & block tag hashes are left alone.
2825 | $is_p = !preg_match('/^B\x1A[0-9]+B|^C\x1A[0-9]+C$/', $value);
2826 |
2827 | if ($is_p) {
2828 | $value = "
$value
";
2829 | }
2830 | $grafs[$key] = $value;
2831 | }
2832 |
2833 | # Join grafs in one text, then unhash HTML tags.
2834 | $text = implode("\n\n", $grafs);
2835 |
2836 | # Finish by removing any tag hashes still present in $text.
2837 | $text = $this->unhash($text);
2838 |
2839 | return $text;
2840 | }
2841 |
2842 |
2843 | ### Footnotes
2844 |
2845 | function stripFootnotes($text) {
2846 | #
2847 | # Strips link definitions from text, stores the URLs and titles in
2848 | # hash references.
2849 | #
2850 | $less_than_tab = $this->tab_width - 1;
2851 |
2852 | # Link defs are in the form: [^id]: url "optional title"
2853 | $text = preg_replace_callback('{
2854 | ^[ ]{0,'.$less_than_tab.'}\[\^(.+?)\][ ]?: # note_id = $1
2855 | [ ]*
2856 | \n? # maybe *one* newline
2857 | ( # text = $2 (no blank lines allowed)
2858 | (?:
2859 | .+ # actual text
2860 | |
2861 | \n # newlines but
2862 | (?!\[\^.+?\]:\s)# negative lookahead for footnote marker.
2863 | (?!\n+[ ]{0,3}\S)# ensure line is not blank and followed
2864 | # by non-indented content
2865 | )*
2866 | )
2867 | }xm',
2868 | array(&$this, '_stripFootnotes_callback'),
2869 | $text);
2870 | return $text;
2871 | }
2872 | function _stripFootnotes_callback($matches) {
2873 | $note_id = $this->fn_id_prefix . $matches[1];
2874 | $this->footnotes[$note_id] = $this->outdent($matches[2]);
2875 | return ''; # String that will replace the block
2876 | }
2877 |
2878 |
2879 | function doFootnotes($text) {
2880 | #
2881 | # Replace footnote references in $text [^id] with a special text-token
2882 | # which will be replaced by the actual footnote marker in appendFootnotes.
2883 | #
2884 | if (!$this->in_anchor) {
2885 | $text = preg_replace('{\[\^(.+?)\]}', "F\x1Afn:\\1\x1A:", $text);
2886 | }
2887 | return $text;
2888 | }
2889 |
2890 |
2891 | function appendFootnotes($text) {
2892 | #
2893 | # Append footnote list to text.
2894 | #
2895 | $text = preg_replace_callback('{F\x1Afn:(.*?)\x1A:}',
2896 | array(&$this, '_appendFootnotes_callback'), $text);
2897 |
2898 | if (!empty($this->footnotes_ordered)) {
2899 | $text .= "\n\n";
2900 | $text .= "\n";
2901 | $text .= "
empty_element_suffix ."\n";
2902 | $text .= "\n\n";
2903 |
2904 | $attr = " rev=\"footnote\"";
2905 | if ($this->fn_backlink_class != "") {
2906 | $class = $this->fn_backlink_class;
2907 | $class = $this->encodeAttribute($class);
2908 | $attr .= " class=\"$class\"";
2909 | }
2910 | if ($this->fn_backlink_title != "") {
2911 | $title = $this->fn_backlink_title;
2912 | $title = $this->encodeAttribute($title);
2913 | $attr .= " title=\"$title\"";
2914 | }
2915 | $num = 0;
2916 |
2917 | while (!empty($this->footnotes_ordered)) {
2918 | $footnote = reset($this->footnotes_ordered);
2919 | $note_id = key($this->footnotes_ordered);
2920 | unset($this->footnotes_ordered[$note_id]);
2921 | $ref_count = $this->footnotes_ref_count[$note_id];
2922 | unset($this->footnotes_ref_count[$note_id]);
2923 | unset($this->footnotes[$note_id]);
2924 |
2925 | $footnote .= "\n"; # Need to append newline before parsing.
2926 | $footnote = $this->runBlockGamut("$footnote\n");
2927 | $footnote = preg_replace_callback('{F\x1Afn:(.*?)\x1A:}',
2928 | array(&$this, '_appendFootnotes_callback'), $footnote);
2929 |
2930 | $attr = str_replace("%%", ++$num, $attr);
2931 | $note_id = $this->encodeAttribute($note_id);
2932 |
2933 | # Prepare backlink, multiple backlinks if multiple references
2934 | $backlink = "↩";
2935 | for ($ref_num = 2; $ref_num <= $ref_count; ++$ref_num) {
2936 | $backlink .= " ↩";
2937 | }
2938 | # Add backlink to last paragraph; create new paragraph if needed.
2939 | if (preg_match('{$}', $footnote)) {
2940 | $footnote = substr($footnote, 0, -4) . " $backlink";
2941 | } else {
2942 | $footnote .= "\n\n$backlink
";
2943 | }
2944 |
2945 | $text .= "- \n";
2946 | $text .= $footnote . "\n";
2947 | $text .= "
\n\n";
2948 | }
2949 |
2950 | $text .= "
\n";
2951 | $text .= "";
2952 | }
2953 | return $text;
2954 | }
2955 | function _appendFootnotes_callback($matches) {
2956 | $node_id = $this->fn_id_prefix . $matches[1];
2957 |
2958 | # Create footnote marker only if it has a corresponding footnote *and*
2959 | # the footnote hasn't been used by another marker.
2960 | if (isset($this->footnotes[$node_id])) {
2961 | $num =& $this->footnotes_numbers[$node_id];
2962 | if (!isset($num)) {
2963 | # Transfer footnote content to the ordered list and give it its
2964 | # number
2965 | $this->footnotes_ordered[$node_id] = $this->footnotes[$node_id];
2966 | $this->footnotes_ref_count[$node_id] = 1;
2967 | $num = $this->footnote_counter++;
2968 | $ref_count_mark = '';
2969 | } else {
2970 | $ref_count_mark = $this->footnotes_ref_count[$node_id] += 1;
2971 | }
2972 |
2973 | $attr = " rel=\"footnote\"";
2974 | if ($this->fn_link_class != "") {
2975 | $class = $this->fn_link_class;
2976 | $class = $this->encodeAttribute($class);
2977 | $attr .= " class=\"$class\"";
2978 | }
2979 | if ($this->fn_link_title != "") {
2980 | $title = $this->fn_link_title;
2981 | $title = $this->encodeAttribute($title);
2982 | $attr .= " title=\"$title\"";
2983 | }
2984 |
2985 | $attr = str_replace("%%", $num, $attr);
2986 | $node_id = $this->encodeAttribute($node_id);
2987 |
2988 | return
2989 | "".
2990 | "$num".
2991 | "";
2992 | }
2993 |
2994 | return "[^".$matches[1]."]";
2995 | }
2996 |
2997 |
2998 | ### Abbreviations ###
2999 |
3000 | function stripAbbreviations($text) {
3001 | #
3002 | # Strips abbreviations from text, stores titles in hash references.
3003 | #
3004 | $less_than_tab = $this->tab_width - 1;
3005 |
3006 | # Link defs are in the form: [id]*: url "optional title"
3007 | $text = preg_replace_callback('{
3008 | ^[ ]{0,'.$less_than_tab.'}\*\[(.+?)\][ ]?: # abbr_id = $1
3009 | (.*) # text = $2 (no blank lines allowed)
3010 | }xm',
3011 | array(&$this, '_stripAbbreviations_callback'),
3012 | $text);
3013 | return $text;
3014 | }
3015 | function _stripAbbreviations_callback($matches) {
3016 | $abbr_word = $matches[1];
3017 | $abbr_desc = $matches[2];
3018 | if ($this->abbr_word_re)
3019 | $this->abbr_word_re .= '|';
3020 | $this->abbr_word_re .= preg_quote($abbr_word);
3021 | $this->abbr_desciptions[$abbr_word] = trim($abbr_desc);
3022 | return ''; # String that will replace the block
3023 | }
3024 |
3025 |
3026 | function doAbbreviations($text) {
3027 | #
3028 | # Find defined abbreviations in text and wrap them in elements.
3029 | #
3030 | if ($this->abbr_word_re) {
3031 | // cannot use the /x modifier because abbr_word_re may
3032 | // contain significant spaces:
3033 | $text = preg_replace_callback('{'.
3034 | '(?abbr_word_re.')'.
3036 | '(?![\w\x1A])'.
3037 | '}',
3038 | array(&$this, '_doAbbreviations_callback'), $text);
3039 | }
3040 | return $text;
3041 | }
3042 | function _doAbbreviations_callback($matches) {
3043 | $abbr = $matches[0];
3044 | if (isset($this->abbr_desciptions[$abbr])) {
3045 | $desc = $this->abbr_desciptions[$abbr];
3046 | if (empty($desc)) {
3047 | return $this->hashPart("$abbr");
3048 | } else {
3049 | $desc = $this->encodeAttribute($desc);
3050 | return $this->hashPart("$abbr");
3051 | }
3052 | } else {
3053 | return $matches[0];
3054 | }
3055 | }
3056 |
3057 | }
3058 |
3059 |
3060 | /*
3061 |
3062 | PHP Markdown Extra
3063 | ==================
3064 |
3065 | Description
3066 | -----------
3067 |
3068 | This is a PHP port of the original Markdown formatter written in Perl
3069 | by John Gruber. This special "Extra" version of PHP Markdown features
3070 | further enhancements to the syntax for making additional constructs
3071 | such as tables and definition list.
3072 |
3073 | Markdown is a text-to-HTML filter; it translates an easy-to-read /
3074 | easy-to-write structured text format into HTML. Markdown's text format
3075 | is mostly similar to that of plain text email, and supports features such
3076 | as headers, *emphasis*, code blocks, blockquotes, and links.
3077 |
3078 | Markdown's syntax is designed not as a generic markup language, but
3079 | specifically to serve as a front-end to (X)HTML. You can use span-level
3080 | HTML tags anywhere in a Markdown document, and you can use block level
3081 | HTML tags (like and as well).
3082 |
3083 | For more information about Markdown's syntax, see:
3084 |
3085 |
3086 |
3087 |
3088 | Bugs
3089 | ----
3090 |
3091 | To file bug reports please send email to:
3092 |
3093 |
3094 |
3095 | Please include with your report: (1) the example input; (2) the output you
3096 | expected; (3) the output Markdown actually produced.
3097 |
3098 |
3099 | Version History
3100 | ---------------
3101 |
3102 | See the readme file for detailed release notes for this version.
3103 |
3104 |
3105 | Copyright and License
3106 | ---------------------
3107 |
3108 | PHP Markdown & Extra
3109 | Copyright (c) 2004-2013 Michel Fortin
3110 |
3111 | All rights reserved.
3112 |
3113 | Based on Markdown
3114 | Copyright (c) 2003-2006 John Gruber
3115 |
3116 | All rights reserved.
3117 |
3118 | Redistribution and use in source and binary forms, with or without
3119 | modification, are permitted provided that the following conditions are
3120 | met:
3121 |
3122 | * Redistributions of source code must retain the above copyright notice,
3123 | this list of conditions and the following disclaimer.
3124 |
3125 | * Redistributions in binary form must reproduce the above copyright
3126 | notice, this list of conditions and the following disclaimer in the
3127 | documentation and/or other materials provided with the distribution.
3128 |
3129 | * Neither the name "Markdown" nor the names of its contributors may
3130 | be used to endorse or promote products derived from this software
3131 | without specific prior written permission.
3132 |
3133 | This software is provided by the copyright holders and contributors "as
3134 | is" and any express or implied warranties, including, but not limited
3135 | to, the implied warranties of merchantability and fitness for a
3136 | particular purpose are disclaimed. In no event shall the copyright owner
3137 | or contributors be liable for any direct, indirect, incidental, special,
3138 | exemplary, or consequential damages (including, but not limited to,
3139 | procurement of substitute goods or services; loss of use, data, or
3140 | profits; or business interruption) however caused and on any theory of
3141 | liability, whether in contract, strict liability, or tort (including
3142 | negligence or otherwise) arising in any way out of the use of this
3143 | software, even if advised of the possibility of such damage.
3144 |
3145 | */
--------------------------------------------------------------------------------
/includes/repo-group/manage-columns.php:
--------------------------------------------------------------------------------
1 | 'group_repo', 'term' => $term->slug ), admin_url( 'edit.php' ) );
31 | $theme_url = add_query_arg( 'post_type', 'theme_repo', $url );
32 | $plugin_url = add_query_arg( 'post_type', 'plugin_repo', $url );
33 | ?>
34 |
48 | false,
24 | 'show_ui' => true,
25 | 'show_in_nav_menus' => false,
26 | 'show_tagcloud' => false,
27 | 'show_admin_column' => true,
28 | 'hierarchical' => true,
29 | 'query_var' => false,
30 | 'capabilities' => array(
31 | 'manage_terms' => 'manage_fx_updaters',
32 | 'edit_terms' => 'manage_fx_updaters',
33 | 'delete_terms' => 'manage_fx_updaters',
34 | 'assign_terms' => 'edit_fx_updaters',
35 | ),
36 | 'rewrite' => false,
37 | 'labels' => array(
38 | 'name' => _x( 'Repo Groups', 'group', 'fx-updater' ),
39 | 'singular_name' => _x( 'Repo Group', 'group', 'fx-updater' ),
40 | 'menu_name' => _x( 'Groups', 'group', 'fx-updater' ),
41 | 'name_admin_bar' => _x( 'Groups', 'group', 'fx-updater' ),
42 | 'search_items' => _x( 'Search Groups', 'group', 'fx-updater' ),
43 | 'popular_items' => _x( 'Popular Groups', 'group', 'fx-updater' ),
44 | 'all_items' => _x( 'All Groups', 'group', 'fx-updater' ),
45 | 'edit_item' => _x( 'Edit Group', 'group', 'fx-updater' ),
46 | 'view_item' => _x( 'View Group', 'group', 'fx-updater' ),
47 | 'update_item' => _x( 'Update Group', 'group', 'fx-updater' ),
48 | 'add_new_item' => _x( 'Add New Group', 'group', 'fx-updater' ),
49 | 'new_item_name' => _x( 'New Group Name', 'group', 'fx-updater' ),
50 | 'separate_items_with_commas'=> _x( 'Separate groups with commas', 'group', 'fx-updater' ),
51 | 'add_or_remove_items' => _x( 'Add or remove groups', 'group', 'fx-updater' ),
52 | 'choose_from_most_used' => _x( 'Choose from the most used groups', 'group', 'fx-updater' ),
53 | )
54 | );
55 |
56 | /* Register Custom Taxonomy */
57 | $args = apply_filters( 'group_repo_taxonomy_args', $args );
58 | register_taxonomy( 'group_repo', array( 'plugin_repo', 'theme_repo' ), $args );
59 | }
60 |
61 |
62 |
63 | /* === ADD ADMIN MENU AS SUB MENU === */
64 |
65 | /* Admin Menu */
66 | add_action( 'admin_menu', 'fx_updater_group_repo_admin_menu' );
67 |
68 | /**
69 | * Add admin menu as sub menu in f(x) Updater Settings.
70 | * @since 1.0.0
71 | * @link https://shellcreeper.com/how-to-add-wordpress-cpt-admin-menu-as-sub-menu/
72 | */
73 | function fx_updater_group_repo_admin_menu(){
74 |
75 | $group_repo = get_taxonomy( 'group_repo' );
76 |
77 | add_submenu_page(
78 | 'fx_updater', // parent slug
79 | $group_repo->labels->name, // page title
80 | $group_repo->labels->menu_name, // menu title
81 | $group_repo->cap->manage_terms, // capability (edit_fx_updaters)
82 | 'edit-tags.php?taxonomy=group_repo' // menu slug
83 | );
84 | }
85 |
86 | /* Parent Menu Fix */
87 | add_filter( 'parent_file', 'fx_updater_group_repo_parent_file' );
88 |
89 | /**
90 | * Fix active parent admin menu to f(x) Updater settings
91 | * @since 1.0.0
92 | * @link https://shellcreeper.com/how-to-add-wordpress-cpt-admin-menu-as-sub-menu/
93 | */
94 | function fx_updater_group_repo_parent_file( $parent_file ){
95 | global $current_screen, $self;
96 | if ( in_array( $current_screen->base, array( 'edit-tags', 'term' ) ) && 'group_repo' == $current_screen->taxonomy ) {
97 | $parent_file = 'fx_updater';
98 | }
99 | return $parent_file;
100 | }
101 |
102 |
103 |
--------------------------------------------------------------------------------
/includes/repo-group/repo-group.php:
--------------------------------------------------------------------------------
1 | '',
21 | 'title' => _x( 'Plugins', 'plugins', 'fx-updater' ),
22 | 'updater_info' => _x( 'Info', 'plugins', 'fx-updater' ),
23 | );
24 |
25 | return array_merge( $new_columns, $columns );
26 | }
27 |
28 | /**
29 | * Custom Columns
30 | * @since 1.0.0
31 | */
32 | function fx_updater_plugin_custom_columns( $column, $post_id ){
33 | switch( $column ) {
34 | case 'updater_info' :
35 | /* Vars */
36 | $status = '' . _x( 'Active', 'plugins', 'fx-updater' ) . '';
37 | $version = get_post_meta( $post_id, 'version', true );
38 | if( !$version ){
39 | $status = '' . _x( 'Not Active', 'plugins', 'fx-updater' ) . '';
40 | $version = 'N/A';
41 | }
42 | $package = get_post_meta( $post_id, 'download_link', true );
43 | if( !$package ){
44 | $package = 'N/A';
45 | $status = '' . _x( 'Not Active', 'plugins', 'fx-updater' ) . '';
46 | }
47 | else{
48 | $package = '' . _x( 'Download ZIP', 'plugins', 'fx-updater' ) . '';
49 | }
50 | $plugin_id = get_post_meta( $post_id, 'id', true );
51 | if( !$plugin_id ){
52 | $plugin_id = 'N/A';
53 | $status = '' . _x( 'Not Active', 'plugins', 'fx-updater' ) . '';
54 | }
55 | $post_status = get_post_status( $post_id );
56 | if( 'publish' !== $post_status ){
57 | $status = '' . _x( 'Not Active', 'plugins', 'fx-updater' ) . '';
58 | }
59 | $wp = 'N/A';
60 | $wp_requires = get_post_meta( $post_id, 'requires', true );
61 | $wp_tested = get_post_meta( $post_id, 'tested', true );
62 | if( $wp_requires && $wp_tested ){
63 | $wp = $wp_requires . " - " . $wp_tested;
64 | }
65 | elseif( $wp_tested ){
66 | $wp = $wp_tested;
67 | }
68 | ?>
69 |
70 |
71 |
72 | :
73 |
74 |
75 |
76 | :
77 |
78 |
79 |
80 | :
81 |
82 |
83 |
84 | :
85 |
86 |
87 |
88 | :
89 |
90 |
91 | get_taxonomy( 'group_repo' )->labels->all_items,
132 | 'hide_empty' => 0,
133 | 'hierarchical' => 1,
134 | 'show_count' => 0,
135 | 'orderby' => 'name',
136 | 'selected' => $selected,
137 | 'taxonomy' => 'group_repo',
138 | 'name' => 'term',
139 | 'value_field' => 'slug',
140 | );
141 |
142 | echo '';
143 | wp_dropdown_categories( $dropdown_options );
144 | }
145 | }
146 |
147 |
--------------------------------------------------------------------------------
/includes/repo-plugin/meta-box-data.php:
--------------------------------------------------------------------------------
1 | ID;
38 |
39 | /* Plugin ID */
40 | $plugin_id = get_post_meta( $post_id, 'id', true );
41 |
42 | /* Download ZIP */
43 | $download_link = get_post_meta( $post_id, 'download_link', true );
44 |
45 | /* Version */
46 | $version = 'post-new.php' == $hook_suffix ? '1.0.0' : get_post_meta( $post_id, 'version', true );
47 |
48 | /* Last Updated */
49 | $last_updated = fx_updater_explode_date( get_post_meta( $post_id, 'last_updated', true ) );
50 | $year = date( 'Y' );
51 | $month = date( 'm' );
52 | $day = date( 'd' );
53 | if( $last_updated ){
54 | $year = 'post-new.php' == $hook_suffix ? $year : $last_updated['year'];
55 | $month = 'post-new.php' == $hook_suffix ? $month : $last_updated['month'];
56 | $day = 'post-new.php' == $hook_suffix ? $day : $last_updated['day'];
57 | }
58 |
59 | /* WP Version */
60 | $wp_requires = 'post-new.php' == $hook_suffix ? "" : get_post_meta( $post_id, 'requires', true );
61 | $wp_tested = 'post-new.php' == $hook_suffix ? $wp_version : get_post_meta( $post_id, 'tested', true );
62 |
63 | /* Changelog */
64 | $changelog = get_post_meta( $post_id, 'section_changelog', true );
65 |
66 | /* Upgrade Notice */
67 | $upgrade_notice = strip_tags( get_post_meta( $post_id, 'upgrade_notice', true ) );
68 | ?>
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
145 | ,
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 |
197 |
198 |
199 |
200 |
201 |
202 |
203 |
204 | post_type );
231 | if ( 'plugin_repo' != $post->post_type || !current_user_can( $post_type->cap->edit_post, $post_id ) ){
232 | return $post_id;
233 | }
234 |
235 | /* == PLUGIN ID == */
236 |
237 | /* Get (old) saved data */
238 | $old_data = get_post_meta( $post_id, 'id', true );
239 |
240 | /* Get new submitted data and sanitize it. */
241 | $new_data = isset( $request['id'] ) ? esc_attr( $request['id'] ) : '';
242 |
243 | /* New data submitted, No previous data, create it */
244 | if ( $new_data && '' == $old_data ){
245 | add_post_meta( $post_id, 'id', $new_data, true );
246 | }
247 | /* New data submitted, but it's different data than previously stored data, update it */
248 | elseif( $new_data && ( $new_data != $old_data ) ){
249 | update_post_meta( $post_id, 'id', $new_data );
250 | }
251 | /* New data submitted is empty, but there's old data available, delete it. */
252 | elseif ( empty( $new_data ) && $old_data ){
253 | delete_post_meta( $post_id, 'id' );
254 | }
255 |
256 | /* == ZIP FILE == */
257 |
258 | /* Get (old) saved data */
259 | $old_data = get_post_meta( $post_id, 'download_link', true );
260 |
261 | /* Get new submitted data and sanitize it. */
262 | $new_data = isset( $request['download_link'] ) ? esc_url_raw( $request['download_link'] ) : '';
263 |
264 | /* New data submitted, No previous data, create it */
265 | if ( $new_data && '' == $old_data ){
266 | add_post_meta( $post_id, 'download_link', $new_data, true );
267 | }
268 | /* New data submitted, but it's different data than previously stored data, update it */
269 | elseif( $new_data && ( $new_data != $old_data ) ){
270 | update_post_meta( $post_id, 'download_link', $new_data );
271 | }
272 | /* New data submitted is empty, but there's old data available, delete it. */
273 | elseif ( empty( $new_data ) && $old_data ){
274 | delete_post_meta( $post_id, 'download_link' );
275 | }
276 |
277 | /* == VERSION == */
278 |
279 | /* Get (old) saved data */
280 | $old_data = get_post_meta( $post_id, 'version', true );
281 |
282 | /* Get new submitted data and sanitize it. */
283 | $new_data = isset( $request['version'] ) ? fx_updater_sanitize_version( $request['version'] ) : '';
284 |
285 | /* New data submitted, No previous data, create it */
286 | if ( $new_data && '' == $old_data ){
287 | add_post_meta( $post_id, 'version', $new_data, true );
288 | }
289 | /* New data submitted, but it's different data than previously stored data, update it */
290 | elseif( $new_data && ( $new_data != $old_data ) ){
291 | update_post_meta( $post_id, 'version', $new_data );
292 | }
293 | /* New data submitted is empty, but there's old data available, delete it. */
294 | elseif ( empty( $new_data ) && $old_data ){
295 | delete_post_meta( $post_id, 'version' );
296 | }
297 |
298 | /* === RELEASE DATE === */
299 |
300 | /* Get (old) saved data */
301 | $old_data = get_post_meta( $post_id, 'last_updated', true );
302 |
303 | /* Get new submitted data and sanitize it. */
304 | $new_date = array(
305 | 'day' => isset( $request['last_updated_day'] ) ? $request['last_updated_day'] : date( 'd' ),
306 | 'month' => isset( $request['last_updated_month'] ) ? $request['last_updated_month'] : date( 'm' ),
307 | 'year' => isset( $request['last_updated_year'] ) ? $request['last_updated_year'] : date( 'Y' ),
308 | );
309 | $new_data = fx_updater_format_date( $new_date ); // YYYY-MM-DD
310 |
311 | /* New data submitted, No previous data, create it */
312 | if ( $new_data && '' == $old_data ){
313 | add_post_meta( $post_id, 'last_updated', $new_data, true );
314 | }
315 | /* New data submitted, but it's different data than previously stored data, update it */
316 | elseif( $new_data && ( $new_data != $old_data ) ){
317 | update_post_meta( $post_id, 'last_updated', $new_data );
318 | }
319 | /* New data submitted is empty, but there's old data available, delete it. */
320 | elseif ( empty( $new_data ) && $old_data ){
321 | delete_post_meta( $post_id, 'last_updated' );
322 | }
323 |
324 | /* === WP VERSION === */
325 |
326 | /* WP Version: Required */
327 |
328 | /* Get (old) saved data */
329 | $old_data = get_post_meta( $post_id, 'requires', true );
330 |
331 | /* Get new submitted data and sanitize it. */
332 | $new_data = isset( $request['requires'] ) ? fx_updater_sanitize_version( $request['requires'] ) : '';
333 |
334 | /* New data submitted, No previous data, create it */
335 | if ( $new_data && '' == $old_data ){
336 | add_post_meta( $post_id, 'requires', $new_data, true );
337 | }
338 | /* New data submitted, but it's different data than previously stored data, update it */
339 | elseif( $new_data && ( $new_data != $old_data ) ){
340 | update_post_meta( $post_id, 'requires', $new_data );
341 | }
342 | /* New data submitted is empty, but there's old data available, delete it. */
343 | elseif ( empty( $new_data ) && $old_data ){
344 | delete_post_meta( $post_id, 'requires' );
345 | }
346 |
347 | /* WP Version: Tested */
348 |
349 | /* Get (old) saved data */
350 | $old_data = get_post_meta( $post_id, 'tested', true );
351 |
352 | /* Get new submitted data and sanitize it. */
353 | $new_data = isset( $request['tested'] ) ? fx_updater_sanitize_version( $request['tested'] ) : '';
354 |
355 | /* New data submitted, No previous data, create it */
356 | if ( $new_data && '' == $old_data ){
357 | add_post_meta( $post_id, 'tested', $new_data, true );
358 | }
359 | /* New data submitted, but it's different data than previously stored data, update it */
360 | elseif( $new_data && ( $new_data != $old_data ) ){
361 | update_post_meta( $post_id, 'tested', $new_data );
362 | }
363 | /* New data submitted is empty, but there's old data available, delete it. */
364 | elseif ( empty( $new_data ) && $old_data ){
365 | delete_post_meta( $post_id, 'tested' );
366 | }
367 |
368 | /* === CHANGELOG === */
369 |
370 | /* Get (old) saved data */
371 | $old_data = get_post_meta( $post_id, 'section_changelog', true );
372 |
373 | /* Get new submitted data and sanitize it. */
374 | $new_data = isset( $request['section_changelog'] ) ? fx_updater_sanitize_plugin_section( $request['section_changelog'] ) : '';
375 |
376 | /* New data submitted, No previous data, create it */
377 | if ( $new_data && '' == $old_data ){
378 | add_post_meta( $post_id, 'section_changelog', $new_data, true );
379 | }
380 | /* New data submitted, but it's different data than previously stored data, update it */
381 | elseif( $new_data && ( $new_data != $old_data ) ){
382 | update_post_meta( $post_id, 'section_changelog', $new_data );
383 | }
384 | /* New data submitted is empty, but there's old data available, delete it. */
385 | elseif ( empty( $new_data ) && $old_data ){
386 | delete_post_meta( $post_id, 'section_changelog' );
387 | }
388 |
389 | /* == UPGRADE NOTICE == */
390 |
391 | /* Get (old) saved data */
392 | $old_data = get_post_meta( $post_id, 'upgrade_notice', true );
393 |
394 | /* Get new submitted data and sanitize it. */
395 | $new_data = isset( $request['upgrade_notice'] ) ? strip_tags( $request['upgrade_notice'] ) : '';
396 |
397 | /* New data submitted, No previous data, create it */
398 | if ( $new_data && '' == $old_data ){
399 | add_post_meta( $post_id, 'upgrade_notice', $new_data, true );
400 | }
401 | /* New data submitted, but it's different data than previously stored data, update it */
402 | elseif( $new_data && ( $new_data != $old_data ) ){
403 | update_post_meta( $post_id, 'upgrade_notice', $new_data );
404 | }
405 | /* New data submitted is empty, but there's old data available, delete it. */
406 | elseif ( empty( $new_data ) && $old_data ){
407 | delete_post_meta( $post_id, 'upgrade_notice' );
408 | }
409 |
410 | }
411 |
412 |
--------------------------------------------------------------------------------
/includes/repo-plugin/register-post-type.php:
--------------------------------------------------------------------------------
1 | '',
29 | 'public' => false,
30 | 'publicly_queryable' => false,
31 | 'show_in_nav_menus' => false,
32 | 'show_in_admin_bar' => false,
33 | 'exclude_from_search' => true,
34 | 'show_ui' => true,
35 | 'show_in_menu' => false,
36 | 'menu_position' => null,
37 | 'menu_icon' => 'dashicons-update',
38 | 'can_export' => true,
39 | 'delete_with_user' => false,
40 | 'hierarchical' => false,
41 | 'has_archive' => false,
42 | 'query_var' => false,
43 | 'rewrite' => false,
44 | 'capability_type' => 'plugin_repo',
45 | 'map_meta_cap' => true,
46 | 'capabilities' => array(
47 | 'edit_post' => 'edit_fx_updater', // don't assign these to roles
48 | 'read_post' => 'read_fx_updater', // don't assign these to roles
49 | 'delete_post' => 'delete_fx_updater', // don't assign these to roles
50 | 'create_posts' => 'create_fx_updaters', // primitive meta caps
51 | 'edit_posts' => 'edit_fx_updaters', // primitive caps outside map_meta_cap()
52 | 'edit_others_posts' => 'manage_fx_updaters', // primitive caps outside map_meta_cap()
53 | 'publish_posts' => 'manage_fx_updaters', // primitive caps outside map_meta_cap()
54 | 'read_private_posts' => 'read',
55 | 'read' => 'read',
56 | 'delete_posts' => 'manage_fx_updaters', // primitive caps inside map_meta_cap()
57 | 'delete_private_posts' => 'manage_fx_updaters', // primitive caps inside map_meta_cap()
58 | 'delete_published_posts' => 'manage_fx_updaters', // primitive caps inside map_meta_cap()
59 | 'delete_others_posts' => 'manage_fx_updaters', // primitive caps inside map_meta_cap()
60 | 'edit_private_posts' => 'edit_fx_updaters', // primitive caps inside map_meta_cap()
61 | 'edit_published_posts' => 'edit_fx_updaters' // primitive caps inside map_meta_cap()
62 | ),
63 | 'supports' => array( 'title' ),
64 | 'labels' => array(
65 | 'name' => _x( 'Plugins Updater', 'plugins', 'fx-updater' ),
66 | 'singular_name' => _x( 'Plugin Repo', 'plugins', 'fx-updater' ),
67 | 'add_new' => _x( 'Add New', 'plugins', 'fx-updater' ),
68 | 'add_new_item' => _x( 'Add New Plugin Repo', 'plugins', 'fx-updater' ),
69 | 'edit_item' => _x( 'Edit Plugin Repo', 'plugins', 'fx-updater' ),
70 | 'new_item' => _x( 'New Plugin Repo', 'plugins', 'fx-updater' ),
71 | 'all_items' => _x( 'All Plugins', 'plugins', 'fx-updater' ),
72 | 'view_item' => _x( 'View Plugin', 'plugins', 'fx-updater' ),
73 | 'search_items' => _x( 'Search Plugin', 'plugins', 'fx-updater' ),
74 | 'not_found' => _x( 'No Plugin found', 'plugins', 'fx-updater' ),
75 | 'not_found_in_trash' => _x( 'No Plugin found in Trash', 'plugins', 'fx-updater' ),
76 | 'menu_name' => _x( 'Plugins', 'plugins', 'fx-updater' ),
77 | ),
78 | );
79 |
80 | /* REGISTER "plugin_repo" POST TYPE */
81 | $args = apply_filters( 'plugin_repo_post_type_args', $args );
82 | register_post_type( 'plugin_repo', $args );
83 | }
84 |
85 |
86 | /* === ADD ADMIN MENU AS SUB MENU === */
87 |
88 | /* Admin Menu */
89 | add_action( 'admin_menu', 'fx_updater_plugin_repo_admin_menu' );
90 |
91 | /**
92 | * Add admin menu as sub menu in f(x) Updater Settings.
93 | * @since 1.0.0
94 | * @link https://shellcreeper.com/how-to-add-wordpress-cpt-admin-menu-as-sub-menu/
95 | */
96 | function fx_updater_plugin_repo_admin_menu(){
97 |
98 | /* Add Submenu Page: Plugin Repo */
99 | $plugin_repo = get_post_type_object( 'plugin_repo' );
100 | add_submenu_page(
101 | 'fx_updater', // parent slug
102 | $plugin_repo->labels->name, // page title
103 | $plugin_repo->labels->menu_name, // menu title
104 | $plugin_repo->cap->edit_posts, // capability (edit_fx_updaters)
105 | 'edit.php?post_type=plugin_repo' // menu slug
106 | );
107 |
108 | }
109 |
110 | /* Parent Menu Fix */
111 | add_filter( 'parent_file', 'fx_updater_plugin_repo_parent_file' );
112 |
113 | /**
114 | * Fix active parent admin menu to f(x) Updater settings
115 | * @since 1.0.0
116 | * @link https://shellcreeper.com/how-to-add-wordpress-cpt-admin-menu-as-sub-menu/
117 | */
118 | function fx_updater_plugin_repo_parent_file( $parent_file ){
119 | global $current_screen, $self;
120 | if ( in_array( $current_screen->base, array( 'post', 'edit' ) ) && 'plugin_repo' == $current_screen->post_type ) {
121 | $parent_file = 'fx_updater';
122 | }
123 | return $parent_file;
124 | }
125 |
126 |
127 | /* === EDIT POST: TITLE PLACEHOLDER === */
128 |
129 | /* Title Placeholder */
130 | add_filter( 'enter_title_here', 'fx_updater_plugin_edit_title_placeholder', 10, 2 );
131 |
132 | /**
133 | * Change "Enter title here" to "Plugin Name"
134 | * @since 0.1.0
135 | */
136 | function fx_updater_plugin_edit_title_placeholder( $placeholder, $post ){
137 | if( 'plugin_repo' == get_post_type( $post ) ){
138 | $placeholder = _x( 'Plugin Name', 'plugins', 'fx-updater' );
139 | }
140 | return $placeholder;
141 | }
142 |
143 |
144 | /* === EDIT POST: UPDATED MESSAGE === */
145 |
146 | /* Updated message */
147 | add_filter( 'post_updated_messages', 'fx_updater_plugin_updated_message' );
148 |
149 | /**
150 | * Custom Updated Message
151 | * @since 0.1.0
152 | */
153 | function fx_updater_plugin_updated_message( $messages ){
154 | global $post, $post_ID;
155 |
156 | $messages['plugin_repo'] = array(
157 | 0 => '', // Unused. Messages start at index 1.
158 | 1 => _x( 'Plugin updated.', 'plugins', 'fx-updater' ),
159 | 2 => _x( 'Plugin field updated.', 'plugins', 'fx-updater' ),
160 | 3 => _x( 'Plugin field deleted.', 'plugins', 'fx-updater' ),
161 | 4 => _x( 'Plugin updated.', 'plugins', 'fx-updater' ),
162 | /* translators: %s: date and time of the revision */
163 | 5 => isset($_GET['revision']) ? sprintf( _x( 'Plugin restored to revision from %s', 'plugins', 'fx-updater' ), wp_post_revision_title( (int) $_GET['revision'], false ) ) : false,
164 | 6 => _x( 'Plugin published.', 'plugins', 'fx-updater' ),
165 | 7 => _x( 'Plugin saved.', 'plugins', 'fx-updater' ),
166 | 8 => _x( 'Plugin submitted.', 'plugins', 'fx-updater' ),
167 | 9 => sprintf( _x( 'Plugin scheduled for: %1$s.', 'plugins', 'fx-updater' ),
168 | /* translators: Publish box date format, see http://php.net/date */
169 | date_i18n( _x( 'M j, Y @ H:i', 'plugins', 'fx-updater' ), strtotime( $post->post_date ) ) ),
170 | 10 => _x( 'Plugin draft updated.', 'plugins', 'fx-updater' ),
171 | );
172 |
173 | return $messages;
174 | }
175 |
176 |
--------------------------------------------------------------------------------
/includes/repo-plugin/repo-plugin.php:
--------------------------------------------------------------------------------
1 | '',
20 | 'title' => _x( 'Themes', 'plugins', 'fx-updater' ),
21 | 'updater_info' => _x( 'Info', 'plugins', 'fx-updater' ),
22 | );
23 | $columns['updater_info'] = _x( 'Info', 'plugins', 'fx-updater' );
24 |
25 | return array_merge( $new_columns, $columns );
26 | }
27 |
28 | /**
29 | * Custom Columns
30 | * @since 1.0.0
31 | */
32 | function fx_updater_theme_custom_columns( $column, $post_id ){
33 | switch( $column ) {
34 | case 'updater_info' :
35 | /* Vars */
36 | $status = '' . _x( 'Active', 'themes', 'fx-updater' ) . '';
37 | $version = get_post_meta( $post_id, 'version', true );
38 | if( !$version ){
39 | $version = 'N/A';
40 | $status = '' . _x( 'Not Active', 'themes', 'fx-updater' ) . '';
41 | }
42 | $package = get_post_meta( $post_id, 'download_link', true );
43 | if( !$package ){
44 | $package = 'N/A';
45 | $status = '' . _x( 'Not Active', 'themes', 'fx-updater' ) . '';
46 | }
47 | else{
48 | $package = '' . _x( 'Download ZIP', 'themes', 'fx-updater' ) . '';
49 | }
50 | $theme_id = get_post_meta( $post_id, 'id', true );
51 | if( !$theme_id ){
52 | $theme_id = 'N/A';
53 | $status = '' . _x( 'Not Active', 'themes', 'fx-updater' ) . '';
54 | }
55 | $post_status = get_post_status( $post_id );
56 | if( 'publish' !== $post_status ){
57 | $status = '' . _x( 'Not Active', 'themes', 'fx-updater' ) . '';
58 | }
59 | ?>
60 |
61 |
62 |
63 | :
64 |
65 |
66 |
67 | :
68 |
69 |
70 |
71 | :
72 |
73 |
74 |
75 | :
76 |
77 |
78 | get_taxonomy( 'group_repo' )->labels->all_items,
118 | 'hide_empty' => 0,
119 | 'hierarchical' => 1,
120 | 'show_count' => 0,
121 | 'orderby' => 'name',
122 | 'selected' => $selected,
123 | 'taxonomy' => 'group_repo',
124 | 'name' => 'term',
125 | 'value_field' => 'slug',
126 | );
127 |
128 | echo '';
129 | wp_dropdown_categories( $dropdown_options );
130 | }
131 | }
132 |
--------------------------------------------------------------------------------
/includes/repo-theme/meta-box-data.php:
--------------------------------------------------------------------------------
1 | ID;
38 |
39 | /* Theme ID */
40 | $theme_id = get_post_meta( $post_id, 'id', true );
41 |
42 | /* Download ZIP */
43 | $download_link = get_post_meta( $post_id, 'download_link', true );
44 |
45 | /* Version */
46 | $version = 'post-new.php' == $hook_suffix ? '1.0.0' : get_post_meta( $post_id, 'version', true );
47 |
48 | /* Theme type */
49 | $theme_type = fx_updater_sanitize_theme_type( get_post_meta( $post_id, 'theme_type', true ) );
50 | ?>
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
104 |
105 |
106 |
107 | post_type );
134 | if ( 'theme_repo' != $post->post_type || !current_user_can( $post_type->cap->edit_post, $post_id ) ){
135 | return $post_id;
136 | }
137 |
138 | /* == THEME ID == */
139 |
140 | /* Get (old) saved data */
141 | $old_data = get_post_meta( $post_id, 'id', true );
142 |
143 | /* Get new submitted data and sanitize it. */
144 | $new_data = isset( $request['id'] ) ? esc_attr( $request['id'] ) : '';
145 |
146 | /* New data submitted, No previous data, create it */
147 | if ( $new_data && '' == $old_data ){
148 | add_post_meta( $post_id, 'id', $new_data, true );
149 | }
150 | /* New data submitted, but it's different data than previously stored data, update it */
151 | elseif( $new_data && ( $new_data != $old_data ) ){
152 | update_post_meta( $post_id, 'id', $new_data );
153 | }
154 | /* New data submitted is empty, but there's old data available, delete it. */
155 | elseif ( empty( $new_data ) && $old_data ){
156 | delete_post_meta( $post_id, 'id' );
157 | }
158 |
159 | /* == ZIP FILE == */
160 |
161 | /* Get (old) saved data */
162 | $old_data = get_post_meta( $post_id, 'download_link', true );
163 |
164 | /* Get new submitted data and sanitize it. */
165 | $new_data = isset( $request['download_link'] ) ? esc_url_raw( $request['download_link'] ) : '';
166 |
167 | /* New data submitted, No previous data, create it */
168 | if ( $new_data && '' == $old_data ){
169 | add_post_meta( $post_id, 'download_link', $new_data, true );
170 | }
171 | /* New data submitted, but it's different data than previously stored data, update it */
172 | elseif( $new_data && ( $new_data != $old_data ) ){
173 | update_post_meta( $post_id, 'download_link', $new_data );
174 | }
175 | /* New data submitted is empty, but there's old data available, delete it. */
176 | elseif ( empty( $new_data ) && $old_data ){
177 | delete_post_meta( $post_id, 'download_link' );
178 | }
179 |
180 | /* == VERSION == */
181 |
182 | /* Get (old) saved data */
183 | $old_data = get_post_meta( $post_id, 'version', true );
184 |
185 | /* Get new submitted data and sanitize it. */
186 | $new_data = isset( $request['version'] ) ? fx_updater_sanitize_version( $request['version'] ) : '';
187 |
188 | /* New data submitted, No previous data, create it */
189 | if ( $new_data && '' == $old_data ){
190 | add_post_meta( $post_id, 'version', $new_data, true );
191 | }
192 | /* New data submitted, but it's different data than previously stored data, update it */
193 | elseif( $new_data && ( $new_data != $old_data ) ){
194 | update_post_meta( $post_id, 'version', $new_data );
195 | }
196 | /* New data submitted is empty, but there's old data available, delete it. */
197 | elseif ( empty( $new_data ) && $old_data ){
198 | delete_post_meta( $post_id, 'version' );
199 | }
200 | }
201 |
202 |
--------------------------------------------------------------------------------
/includes/repo-theme/register-post-type.php:
--------------------------------------------------------------------------------
1 | '',
30 | 'public' => false,
31 | 'publicly_queryable' => false,
32 | 'show_in_nav_menus' => false,
33 | 'show_in_admin_bar' => false,
34 | 'exclude_from_search' => true,
35 | 'show_ui' => true,
36 | 'show_in_menu' => false,
37 | 'menu_position' => null,
38 | 'menu_icon' => 'dashicons-update',
39 | 'can_export' => true,
40 | 'delete_with_user' => false,
41 | 'hierarchical' => false,
42 | 'has_archive' => false,
43 | 'query_var' => false,
44 | 'rewrite' => false,
45 | 'capability_type' => 'theme_repo',
46 | 'map_meta_cap' => true,
47 | 'capabilities' => array(
48 | 'edit_post' => 'edit_fx_updater', // don't assign these to roles
49 | 'read_post' => 'read_fx_updater', // don't assign these to roles
50 | 'delete_post' => 'delete_fx_updater', // don't assign these to roles
51 | 'create_posts' => 'create_fx_updaters', // primitive meta caps
52 | 'edit_posts' => 'edit_fx_updaters', // primitive caps outside map_meta_cap()
53 | 'edit_others_posts' => 'manage_fx_updaters', // primitive caps outside map_meta_cap()
54 | 'publish_posts' => 'manage_fx_updaters', // primitive caps outside map_meta_cap()
55 | 'read_private_posts' => 'read',
56 | 'read' => 'read',
57 | 'delete_posts' => 'manage_fx_updaters', // primitive caps inside map_meta_cap()
58 | 'delete_private_posts' => 'manage_fx_updaters', // primitive caps inside map_meta_cap()
59 | 'delete_published_posts' => 'manage_fx_updaters', // primitive caps inside map_meta_cap()
60 | 'delete_others_posts' => 'manage_fx_updaters', // primitive caps inside map_meta_cap()
61 | 'edit_private_posts' => 'edit_fx_updaters', // primitive caps inside map_meta_cap()
62 | 'edit_published_posts' => 'edit_fx_updaters' // primitive caps inside map_meta_cap()
63 | ),
64 | 'supports' => array( 'title' ),
65 | 'labels' => array(
66 | 'name' => _x( 'Themes Repository', 'themes', 'fx-updater' ),
67 | 'singular_name' => _x( 'Theme', 'themes', 'fx-updater' ),
68 | 'add_new' => _x( 'Add New', 'themes', 'fx-updater' ),
69 | 'add_new_item' => _x( 'Add New Theme Repo', 'themes', 'fx-updater' ),
70 | 'edit_item' => _x( 'Edit Theme', 'themes', 'fx-updater' ),
71 | 'new_item' => _x( 'New Theme', 'themes', 'fx-updater' ),
72 | 'all_items' => _x( 'All Themes', 'themes', 'fx-updater' ),
73 | 'view_item' => _x( 'View Theme', 'themes', 'fx-updater' ),
74 | 'search_items' => _x( 'Search Theme', 'themes', 'fx-updater' ),
75 | 'not_found' => _x( 'No Theme Found', 'themes', 'fx-updater' ),
76 | 'not_found_in_trash' => _x( 'No Theme Found in Trash', 'themes', 'fx-updater' ),
77 | 'menu_name' => _x( 'Themes', 'themes', 'fx-updater' ),
78 | ),
79 | );
80 |
81 | /* Register "theme_repo" post type */
82 | $args = apply_filters( 'theme_repo_post_type_args', $args );
83 | register_post_type( 'theme_repo', $args );
84 | }
85 |
86 |
87 | /* === ADD ADMIN MENU AS SUB MENU === */
88 |
89 | /* Admin Menu */
90 | add_action( 'admin_menu', 'fx_updater_theme_admin_menu' );
91 |
92 | /**
93 | * Add admin menu,
94 | * Submenu in fx updater settings.
95 | * @since 0.1.0
96 | */
97 | function fx_updater_theme_admin_menu(){
98 |
99 | $cpt_obj = get_post_type_object( 'theme_repo' );
100 | add_submenu_page(
101 | 'fx_updater', // parent slug
102 | $cpt_obj->labels->name, // page title
103 | $cpt_obj->labels->menu_name, // menu title
104 | $cpt_obj->cap->edit_posts, // capability
105 | 'edit.php?post_type=theme_repo' // menu slug
106 | );
107 |
108 | }
109 |
110 | /* Parent Menu Fix */
111 | add_filter( 'parent_file', 'fx_updater_theme_parent_file' );
112 |
113 | /**
114 | * Fix Parent Admin Menu to point to f(x) Updater settings
115 | * @since 0.1.0
116 | */
117 | function fx_updater_theme_parent_file( $parent_file ){
118 | global $current_screen, $self;
119 | if ( in_array( $current_screen->base, array( 'post', 'edit' ) ) && 'theme_repo' == $current_screen->post_type ) {
120 | $parent_file = 'fx_updater';
121 | }
122 | return $parent_file;
123 | }
124 |
125 | /* === EDIT POST: TITLE PLACEHOLDER === */
126 |
127 | /* Title Placeholder */
128 | add_filter( 'enter_title_here', 'fx_updater_theme_edit_title_placeholder', 10, 2 );
129 |
130 | /**
131 | * Change "Enter title here" to "Plugin Name"
132 | * @since 0.1.0
133 | */
134 | function fx_updater_theme_edit_title_placeholder( $placeholder, $post ){
135 | if( 'theme_repo' == get_post_type( $post ) ){
136 | $placeholder = _x( 'Theme Name', 'themes', 'fx-updater' );
137 | }
138 | return $placeholder;
139 | }
140 |
141 |
142 | /* === EDIT POST: UPDATED MESSAGE === */
143 |
144 | /* Updated message */
145 | add_filter( 'post_updated_messages', 'fx_updater_theme_updated_message' );
146 |
147 | /**
148 | * Custom Updated Message
149 | * @since 0.1.0
150 | */
151 | function fx_updater_theme_updated_message( $messages ){
152 | global $post, $post_ID;
153 |
154 | $messages['theme_repo'] = array(
155 | 0 => '', // Unused. Messages start at index 1.
156 | 1 => _x( 'Theme updated.', 'themes', 'fx-updater' ),
157 | 2 => _x( 'Theme field updated.', 'themes', 'fx-updater' ),
158 | 3 => _x( 'Theme field deleted.', 'themes', 'fx-updater' ),
159 | 4 => _x( 'Theme updated.', 'themes', 'fx-updater' ),
160 | /* translators: %s: date and time of the revision */
161 | 5 => isset($_GET['revision']) ? sprintf( _x( 'Theme restored to revision from %s', 'themes', 'fx-updater' ), wp_post_revision_title( (int) $_GET['revision'], false ) ) : false,
162 | 6 => _x( 'Theme published.', 'themes', 'fx-updater' ),
163 | 7 => _x( 'Theme saved.', 'themes', 'fx-updater' ),
164 | 8 => _x( 'Theme submitted.', 'themes', 'fx-updater' ),
165 | 9 => sprintf( _x( 'Theme scheduled for: %1$s.', 'themes', 'fx-updater' ),
166 | /* translators: Publish box date format, see http://php.net/date */
167 | date_i18n( _x( 'M j, Y @ H:i', 'themes', 'fx-updater' ), strtotime( $post->post_date ) ) ),
168 | 10 => _x( 'Theme draft updated.', 'themes', 'fx-updater' ),
169 | );
170 |
171 | return $messages;
172 | }
173 |
174 |
--------------------------------------------------------------------------------
/includes/repo-theme/repo-theme.php:
--------------------------------------------------------------------------------
1 | config = array(
17 | 'server' => 'http://genbumedia.com/',
18 | 'type' => 'plugin',
19 | 'id' => 'fx-updater/fx-updater.php',
20 | 'api' => '1.0.0',
21 | 'post' => array(),
22 | );
23 |
24 | /* Admin Init */
25 | add_action( 'admin_init', array( $this, 'admin_init' ) );
26 |
27 | /* Fix Install Folder */
28 | add_filter( 'upgrader_post_install', array( $this, 'fix_install_folder' ), 11, 3 );
29 | }
30 |
31 | /**
32 | * Admin Init.
33 | * Some functions only available in admin.
34 | */
35 | public function admin_init(){
36 |
37 | /* Add theme update data */
38 | if( 'plugin' !== $this->config['type'] ){
39 | add_filter( 'pre_set_site_transient_update_themes', array( $this, 'add_theme_update_data' ), 10, 2 );
40 | }
41 |
42 | /* Add plugin update data */
43 | if( 'theme' !== $this->config['type'] ){
44 | add_filter( 'pre_set_site_transient_update_plugins', array( $this, 'add_plugin_update_data' ), 10, 2 );
45 | }
46 |
47 | /* Plugin Information */
48 | if( 'theme' !== $this->config['type'] ){
49 | add_filter( 'plugins_api_result', array( $this, 'plugin_info' ), 10, 3 );
50 | }
51 | }
52 |
53 | /**
54 | * Add theme update data if available
55 | */
56 | public function add_theme_update_data( $value, $transient ){
57 | if( isset( $value->response ) ){
58 | $update_data = $this->get_data( 'query_themes' );
59 | foreach( $update_data as $theme => $data ){
60 | if( isset( $data['new_version'], $data['theme'], $data['url'] ) ){
61 | $value->response[$theme] = (array)$data;
62 | }
63 | else{
64 | unset( $value->response[$theme] );
65 | }
66 | }
67 | }
68 | return $value;
69 | }
70 |
71 | /**
72 | * Add plugin update data if available
73 | */
74 | public function add_plugin_update_data( $value, $transient ){
75 | if( isset( $value->response ) ){
76 | $update_data = $this->get_data( 'query_plugins' );
77 | foreach( $update_data as $plugin => $data ){
78 | if( isset( $data['new_version'], $data['slug'], $data['plugin'] ) ){
79 | $value->response[$plugin] = (object)$data;
80 | }
81 | else{
82 | unset( $value->response[$plugin] );
83 | }
84 | }
85 | }
86 | return $value;
87 | }
88 |
89 | /**
90 | * Plugin Information
91 | */
92 | public function plugin_info( $res, $action, $args ){
93 |
94 | /* Get list plugin */
95 | if( 'group' == $this->config['type'] ){
96 | $list_plugins = $this->get_data( 'list_plugins' );
97 | }
98 | else{
99 | $slug = dirname( $this->config['id'] );
100 | $list_plugins = array(
101 | $slug => $this->config['id'],
102 | );
103 | }
104 |
105 | /* If in our list, add our data. */
106 | if( 'plugin_information' == $action && isset( $args->slug ) && array_key_exists( $args->slug, $list_plugins ) ){
107 |
108 | $info = $this->get_data( 'plugin_information', $list_plugins[$args->slug] );
109 |
110 | if( isset( $info['name'], $info['slug'], $info['external'], $info['sections'] ) ){
111 | $res = (object)$info;
112 | }
113 | }
114 | return $res;
115 | }
116 |
117 | /**
118 | * Get update data from server
119 | */
120 | public function get_data( $action, $plugin = '' ){
121 |
122 | /* Get WP Version */
123 | global $wp_version;
124 |
125 | /* Remote Options */
126 | $body = $this->config['post'];
127 | if( 'query_plugins' == $action ){
128 | $body['plugins'] = get_plugins();
129 | }
130 | elseif( 'query_themes' == $action ){
131 | $themes = array();
132 | $get_themes = wp_get_themes();
133 | foreach( $get_themes as $theme ){
134 | $stylesheet = $theme->get_stylesheet();
135 | $themes[$stylesheet] = array(
136 | 'Name' => $theme->get( 'Name' ),
137 | 'ThemeURI' => $theme->get( 'ThemeURI' ),
138 | 'Description' => $theme->get( 'Description' ),
139 | 'Author' => $theme->get( 'Author' ),
140 | 'AuthorURI' => $theme->get( 'AuthorURI' ),
141 | 'Version' => $theme->get( 'Version' ),
142 | 'Template' => $theme->get( 'Template' ),
143 | 'Status' => $theme->get( 'Status' ),
144 | 'Tags' => $theme->get( 'Tags' ),
145 | 'TextDomain' => $theme->get( 'TextDomain' ),
146 | 'DomainPath' => $theme->get( 'DomainPath' ),
147 | );
148 | }
149 | $body['themes'] = $themes;
150 | }
151 | elseif( 'plugin_information' == $action ){
152 | $body['plugin'] = $plugin;
153 | }
154 | $options = array(
155 | 'timeout' => 20,
156 | 'body' => $body,
157 | 'user-agent' => 'WordPress/' . $wp_version . '; ' . get_bloginfo( 'url' ),
158 | );
159 |
160 | /* Remote URL */
161 | $url_args = array(
162 | 'fx_updater' => $action,
163 | $this->config['type'] => $this->config['id'],
164 | );
165 | $server = set_url_scheme( $this->config['server'], 'http' );
166 | $url = $http_url = add_query_arg( $url_args, $server );
167 | if ( $ssl = wp_http_supports( array( 'ssl' ) ) ){
168 | $url = set_url_scheme( $url, 'https' );
169 | }
170 |
171 | /* Try HTTPS */
172 | $raw_response = wp_remote_post( esc_url_raw( $url ), $options );
173 |
174 | /* Fail, try HTTP */
175 | if ( is_wp_error( $raw_response ) ) {
176 | $raw_response = wp_remote_post( esc_url_raw( $http_url ), $options );
177 | }
178 |
179 | /* Still fail, bail. */
180 | if ( is_wp_error( $raw_response ) || 200 != wp_remote_retrieve_response_code( $raw_response ) ) {
181 | return array();
182 | }
183 |
184 | /* return array */
185 | $data = json_decode( trim( wp_remote_retrieve_body( $raw_response ) ), true );
186 | return is_array( $data ) ? $data : array();
187 | }
188 |
189 | /**
190 | * Fix Install Folder
191 | */
192 | public function fix_install_folder( $true, $hook_extra, $result ){
193 | if ( isset( $hook_extra['plugin'] ) ){
194 | global $wp_filesystem;
195 | $proper_destination = trailingslashit( $result['local_destination'] ) . dirname( $hook_extra['plugin'] );
196 | $wp_filesystem->move( $result['destination'], $proper_destination );
197 | $result['destination'] = $proper_destination;
198 | $result['destination_name'] = dirname( $hook_extra['plugin'] );
199 | global $hook_suffix;
200 | if( 'update.php' == $hook_suffix && isset( $_GET['action'], $_GET['plugin'] ) && 'upgrade-plugin' == $_GET['action'] && $hook_extra['plugin'] == $_GET['plugin'] ){
201 | activate_plugin( $hook_extra['plugin'] );
202 | }
203 | }
204 | elseif( isset( $hook_extra['theme'] ) ){
205 | global $wp_filesystem;
206 | $proper_destination = trailingslashit( $result['local_destination'] ) . $hook_extra['theme'];
207 | $wp_filesystem->move( $result['destination'], $proper_destination );
208 | if( get_option( 'theme_switched' ) == $hook_extra['theme'] && $result['destination_name'] == get_stylesheet() ){
209 | wp_clean_themes_cache();
210 | switch_theme( $hook_extra['theme'] );
211 | }
212 | $result['destination'] = $proper_destination;
213 | $result['destination_name'] = $hook_extra['theme'];
214 | }
215 | return $true;
216 | }
217 |
218 | }
219 |
--------------------------------------------------------------------------------
/languages/fx-updater.pot:
--------------------------------------------------------------------------------
1 | #, fuzzy
2 | msgid ""
3 | msgstr ""
4 | "Project-Id-Version: f(x) Updater\n"
5 | "Report-Msgid-Bugs-To: \n"
6 | "POT-Creation-Date: 2016-05-17 09:04+0700\n"
7 | "PO-Revision-Date: \n"
8 | "Last-Translator: dave \n"
9 | "Language-Team: David Chandra P. \n"
10 | "Language: en_US\n"
11 | "MIME-Version: 1.0\n"
12 | "Content-Type: text/plain; charset=UTF-8\n"
13 | "Content-Transfer-Encoding: 8bit\n"
14 | "X-Poedit-KeywordsList: _e;__;esc_attr_e;esc_attr__;esc_html_e;esc_html__;_x;"
15 | "_n;_x:1,2c;_n:1,2;_ex\n"
16 | "X-Poedit-Basepath: ..\n"
17 | "X-Generator: Poedit 1.8.7\n"
18 | "X-Poedit-SearchPath-0: .\n"
19 |
20 | #: fx-updater.php:105
21 | msgid "Get Support"
22 | msgstr ""
23 |
24 | #: fx-updater.php:175
25 | msgid "Thank you for using our plugin :)"
26 | msgstr ""
27 |
28 | #: includes/admin-scripts.php:47
29 | msgid "Upload Theme ZIP"
30 | msgstr ""
31 |
32 | #: includes/admin-scripts.php:48
33 | msgid "Insert ZIP File"
34 | msgstr ""
35 |
36 | #: includes/get-code/settings.php:21 includes/get-code/settings.php:33
37 | #: includes/get-code/settings.php:34
38 | msgctxt "settings"
39 | msgid "Get Code"
40 | msgstr ""
41 |
42 | #: includes/get-code/settings.php:22
43 | msgctxt "settings"
44 | msgid "f(x) Updater"
45 | msgstr ""
46 |
47 | #: includes/get-code/settings.php:52
48 | msgid "f(x) Updater"
49 | msgstr ""
50 |
51 | #: includes/repo-group/manage-columns.php:18
52 | msgctxt "group"
53 | msgid "Items"
54 | msgstr ""
55 |
56 | #: includes/repo-group/manage-columns.php:37
57 | #, php-format
58 | msgctxt "group"
59 | msgid "%d Items"
60 | msgstr ""
61 |
62 | #: includes/repo-group/manage-columns.php:41
63 | msgid "View Themes"
64 | msgstr ""
65 |
66 | #: includes/repo-group/manage-columns.php:45
67 | msgid "View Plugins"
68 | msgstr ""
69 |
70 | #: includes/repo-group/register-taxonomy.php:38
71 | msgctxt "group"
72 | msgid "Repo Groups"
73 | msgstr ""
74 |
75 | #: includes/repo-group/register-taxonomy.php:39
76 | msgctxt "group"
77 | msgid "Repo Group"
78 | msgstr ""
79 |
80 | #: includes/repo-group/register-taxonomy.php:40
81 | #: includes/repo-group/register-taxonomy.php:41
82 | msgctxt "group"
83 | msgid "Groups"
84 | msgstr ""
85 |
86 | #: includes/repo-group/register-taxonomy.php:42
87 | msgctxt "group"
88 | msgid "Search Groups"
89 | msgstr ""
90 |
91 | #: includes/repo-group/register-taxonomy.php:43
92 | msgctxt "group"
93 | msgid "Popular Groups"
94 | msgstr ""
95 |
96 | #: includes/repo-group/register-taxonomy.php:44
97 | msgctxt "group"
98 | msgid "All Groups"
99 | msgstr ""
100 |
101 | #: includes/repo-group/register-taxonomy.php:45
102 | msgctxt "group"
103 | msgid "Edit Group"
104 | msgstr ""
105 |
106 | #: includes/repo-group/register-taxonomy.php:46
107 | msgctxt "group"
108 | msgid "View Group"
109 | msgstr ""
110 |
111 | #: includes/repo-group/register-taxonomy.php:47
112 | msgctxt "group"
113 | msgid "Update Group"
114 | msgstr ""
115 |
116 | #: includes/repo-group/register-taxonomy.php:48
117 | msgctxt "group"
118 | msgid "Add New Group"
119 | msgstr ""
120 |
121 | #: includes/repo-group/register-taxonomy.php:49
122 | msgctxt "group"
123 | msgid "New Group Name"
124 | msgstr ""
125 |
126 | #: includes/repo-group/register-taxonomy.php:50
127 | msgctxt "group"
128 | msgid "Separate groups with commas"
129 | msgstr ""
130 |
131 | #: includes/repo-group/register-taxonomy.php:51
132 | msgctxt "group"
133 | msgid "Add or remove groups"
134 | msgstr ""
135 |
136 | #: includes/repo-group/register-taxonomy.php:52
137 | msgctxt "group"
138 | msgid "Choose from the most used groups"
139 | msgstr ""
140 |
141 | #: includes/repo-plugin/manage-columns.php:21
142 | #: includes/repo-plugin/register-post-type.php:76
143 | msgctxt "plugins"
144 | msgid "Plugins"
145 | msgstr ""
146 |
147 | #: includes/repo-plugin/manage-columns.php:22
148 | #: includes/repo-theme/manage-columns.php:21
149 | #: includes/repo-theme/manage-columns.php:23
150 | msgctxt "plugins"
151 | msgid "Info"
152 | msgstr ""
153 |
154 | #: includes/repo-plugin/manage-columns.php:36
155 | msgctxt "plugins"
156 | msgid "Active"
157 | msgstr ""
158 |
159 | #: includes/repo-plugin/manage-columns.php:39
160 | #: includes/repo-plugin/manage-columns.php:45
161 | #: includes/repo-plugin/manage-columns.php:53
162 | #: includes/repo-plugin/manage-columns.php:57
163 | msgctxt "plugins"
164 | msgid "Not Active"
165 | msgstr ""
166 |
167 | #: includes/repo-plugin/manage-columns.php:48
168 | msgctxt "plugins"
169 | msgid "Download ZIP"
170 | msgstr ""
171 |
172 | #: includes/repo-plugin/manage-columns.php:72
173 | #: includes/repo-theme/manage-columns.php:63
174 | msgid "Status"
175 | msgstr ""
176 |
177 | #: includes/repo-plugin/manage-columns.php:76
178 | #: includes/repo-plugin/meta-box-data.php:75
179 | msgid "Plugin ID"
180 | msgstr ""
181 |
182 | #: includes/repo-plugin/manage-columns.php:80
183 | #: includes/repo-plugin/meta-box-data.php:91
184 | #: includes/repo-theme/manage-columns.php:71
185 | #: includes/repo-theme/meta-box-data.php:73
186 | msgid "Version"
187 | msgstr ""
188 |
189 | #: includes/repo-plugin/manage-columns.php:84
190 | #: includes/repo-theme/manage-columns.php:75
191 | msgid "Package"
192 | msgstr ""
193 |
194 | #: includes/repo-plugin/manage-columns.php:88
195 | #: includes/repo-plugin/meta-box-data.php:155
196 | msgid "WP Version"
197 | msgstr ""
198 |
199 | #: includes/repo-plugin/meta-box-data.php:19
200 | msgctxt "plugins"
201 | msgid "Plugin Data"
202 | msgstr ""
203 |
204 | #: includes/repo-plugin/meta-box-data.php:83
205 | msgid "Your plugin file (required)."
206 | msgstr ""
207 |
208 | #: includes/repo-plugin/meta-box-data.php:97
209 | msgid "Latest plugin version (required)."
210 | msgstr ""
211 |
212 | #: includes/repo-plugin/meta-box-data.php:105
213 | msgid "Plugin ZIP"
214 | msgstr ""
215 |
216 | #: includes/repo-plugin/meta-box-data.php:114
217 | #: includes/repo-theme/meta-box-data.php:96
218 | msgid "Upload"
219 | msgstr ""
220 |
221 | #: includes/repo-plugin/meta-box-data.php:115
222 | #: includes/repo-theme/meta-box-data.php:97
223 | msgid "Remove"
224 | msgstr ""
225 |
226 | #: includes/repo-plugin/meta-box-data.php:118
227 | msgid "Input plugin ZIP URL or upload it."
228 | msgstr ""
229 |
230 | #: includes/repo-plugin/meta-box-data.php:126
231 | msgid "Release Date"
232 | msgstr ""
233 |
234 | #: includes/repo-plugin/meta-box-data.php:132
235 | msgid "01-Jan"
236 | msgstr ""
237 |
238 | #: includes/repo-plugin/meta-box-data.php:133
239 | msgid "02-Feb"
240 | msgstr ""
241 |
242 | #: includes/repo-plugin/meta-box-data.php:134
243 | msgid "03-Mar"
244 | msgstr ""
245 |
246 | #: includes/repo-plugin/meta-box-data.php:135
247 | msgid "04-Apr"
248 | msgstr ""
249 |
250 | #: includes/repo-plugin/meta-box-data.php:136
251 | msgid "05-May"
252 | msgstr ""
253 |
254 | #: includes/repo-plugin/meta-box-data.php:137
255 | msgid "06-Jun"
256 | msgstr ""
257 |
258 | #: includes/repo-plugin/meta-box-data.php:138
259 | msgid "07-Jul"
260 | msgstr ""
261 |
262 | #: includes/repo-plugin/meta-box-data.php:139
263 | msgid "08-Aug"
264 | msgstr ""
265 |
266 | #: includes/repo-plugin/meta-box-data.php:140
267 | msgid "09-Sep"
268 | msgstr ""
269 |
270 | #: includes/repo-plugin/meta-box-data.php:141
271 | msgid "10-Oct"
272 | msgstr ""
273 |
274 | #: includes/repo-plugin/meta-box-data.php:142
275 | msgid "11-Nov"
276 | msgstr ""
277 |
278 | #: includes/repo-plugin/meta-box-data.php:143
279 | msgid "12-Dec"
280 | msgstr ""
281 |
282 | #: includes/repo-plugin/meta-box-data.php:148
283 | msgid "Last updated date. (Month, Date, Year)"
284 | msgstr ""
285 |
286 | #: includes/repo-plugin/meta-box-data.php:161
287 | msgid "Minimum/Required WordPress version."
288 | msgstr ""
289 |
290 | #: includes/repo-plugin/meta-box-data.php:165
291 | msgid "Up to/Tested WordPress version."
292 | msgstr ""
293 |
294 | #: includes/repo-plugin/meta-box-data.php:173
295 | msgid "Changelog"
296 | msgstr ""
297 |
298 | #: includes/repo-plugin/meta-box-data.php:178
299 | msgid "Your plugin changelog here..."
300 | msgstr ""
301 |
302 | #: includes/repo-plugin/meta-box-data.php:181
303 | msgid "Add changelog using markdown or HTML."
304 | msgstr ""
305 |
306 | #: includes/repo-plugin/meta-box-data.php:189
307 | msgid "Upgrade Notice"
308 | msgstr ""
309 |
310 | #: includes/repo-plugin/meta-box-data.php:197
311 | msgid "Upgrade Notice for this version. No HTML allowed."
312 | msgstr ""
313 |
314 | #: includes/repo-plugin/register-post-type.php:65
315 | msgctxt "plugins"
316 | msgid "Plugins Updater"
317 | msgstr ""
318 |
319 | #: includes/repo-plugin/register-post-type.php:66
320 | msgctxt "plugins"
321 | msgid "Plugin Repo"
322 | msgstr ""
323 |
324 | #: includes/repo-plugin/register-post-type.php:67
325 | msgctxt "plugins"
326 | msgid "Add New"
327 | msgstr ""
328 |
329 | #: includes/repo-plugin/register-post-type.php:68
330 | msgctxt "plugins"
331 | msgid "Add New Plugin Repo"
332 | msgstr ""
333 |
334 | #: includes/repo-plugin/register-post-type.php:69
335 | msgctxt "plugins"
336 | msgid "Edit Plugin Repo"
337 | msgstr ""
338 |
339 | #: includes/repo-plugin/register-post-type.php:70
340 | msgctxt "plugins"
341 | msgid "New Plugin Repo"
342 | msgstr ""
343 |
344 | #: includes/repo-plugin/register-post-type.php:71
345 | msgctxt "plugins"
346 | msgid "All Plugins"
347 | msgstr ""
348 |
349 | #: includes/repo-plugin/register-post-type.php:72
350 | msgctxt "plugins"
351 | msgid "View Plugin"
352 | msgstr ""
353 |
354 | #: includes/repo-plugin/register-post-type.php:73
355 | msgctxt "plugins"
356 | msgid "Search Plugin"
357 | msgstr ""
358 |
359 | #: includes/repo-plugin/register-post-type.php:74
360 | msgctxt "plugins"
361 | msgid "No Plugin found"
362 | msgstr ""
363 |
364 | #: includes/repo-plugin/register-post-type.php:75
365 | msgctxt "plugins"
366 | msgid "No Plugin found in Trash"
367 | msgstr ""
368 |
369 | #: includes/repo-plugin/register-post-type.php:138
370 | msgctxt "plugins"
371 | msgid "Plugin Name"
372 | msgstr ""
373 |
374 | #: includes/repo-plugin/register-post-type.php:158
375 | #: includes/repo-plugin/register-post-type.php:161
376 | msgctxt "plugins"
377 | msgid "Plugin updated."
378 | msgstr ""
379 |
380 | #: includes/repo-plugin/register-post-type.php:159
381 | msgctxt "plugins"
382 | msgid "Plugin field updated."
383 | msgstr ""
384 |
385 | #: includes/repo-plugin/register-post-type.php:160
386 | msgctxt "plugins"
387 | msgid "Plugin field deleted."
388 | msgstr ""
389 |
390 | #: includes/repo-plugin/register-post-type.php:163
391 | #, php-format
392 | msgctxt "plugins"
393 | msgid "Plugin restored to revision from %s"
394 | msgstr ""
395 |
396 | #: includes/repo-plugin/register-post-type.php:164
397 | msgctxt "plugins"
398 | msgid "Plugin published."
399 | msgstr ""
400 |
401 | #: includes/repo-plugin/register-post-type.php:165
402 | msgctxt "plugins"
403 | msgid "Plugin saved."
404 | msgstr ""
405 |
406 | #: includes/repo-plugin/register-post-type.php:166
407 | msgctxt "plugins"
408 | msgid "Plugin submitted."
409 | msgstr ""
410 |
411 | #: includes/repo-plugin/register-post-type.php:167
412 | #, php-format
413 | msgctxt "plugins"
414 | msgid "Plugin scheduled for: %1$s."
415 | msgstr ""
416 |
417 | #: includes/repo-plugin/register-post-type.php:169
418 | msgctxt "plugins"
419 | msgid "M j, Y @ H:i"
420 | msgstr ""
421 |
422 | #: includes/repo-plugin/register-post-type.php:170
423 | msgctxt "plugins"
424 | msgid "Plugin draft updated."
425 | msgstr ""
426 |
427 | #: includes/repo-theme/manage-columns.php:20
428 | msgctxt "plugins"
429 | msgid "Themes"
430 | msgstr ""
431 |
432 | #: includes/repo-theme/manage-columns.php:36
433 | msgctxt "themes"
434 | msgid "Active"
435 | msgstr ""
436 |
437 | #: includes/repo-theme/manage-columns.php:40
438 | #: includes/repo-theme/manage-columns.php:45
439 | #: includes/repo-theme/manage-columns.php:53
440 | #: includes/repo-theme/manage-columns.php:57
441 | msgctxt "themes"
442 | msgid "Not Active"
443 | msgstr ""
444 |
445 | #: includes/repo-theme/manage-columns.php:48
446 | msgctxt "themes"
447 | msgid "Download ZIP"
448 | msgstr ""
449 |
450 | #: includes/repo-theme/manage-columns.php:67
451 | #: includes/repo-theme/meta-box-data.php:57
452 | msgid "Theme ID"
453 | msgstr ""
454 |
455 | #: includes/repo-theme/meta-box-data.php:19
456 | msgctxt "themes"
457 | msgid "Theme Data"
458 | msgstr ""
459 |
460 | #: includes/repo-theme/meta-box-data.php:65
461 | msgid "Your theme folder (required)."
462 | msgstr ""
463 |
464 | #: includes/repo-theme/meta-box-data.php:79
465 | msgid "Latest theme version (required)."
466 | msgstr ""
467 |
468 | #: includes/repo-theme/meta-box-data.php:87
469 | msgid "Theme ZIP"
470 | msgstr ""
471 |
472 | #: includes/repo-theme/meta-box-data.php:100
473 | msgid "Input theme ZIP URL or upload it."
474 | msgstr ""
475 |
476 | #: includes/repo-theme/register-post-type.php:66
477 | msgctxt "themes"
478 | msgid "Themes Repository"
479 | msgstr ""
480 |
481 | #: includes/repo-theme/register-post-type.php:67
482 | msgctxt "themes"
483 | msgid "Theme"
484 | msgstr ""
485 |
486 | #: includes/repo-theme/register-post-type.php:68
487 | msgctxt "themes"
488 | msgid "Add New"
489 | msgstr ""
490 |
491 | #: includes/repo-theme/register-post-type.php:69
492 | msgctxt "themes"
493 | msgid "Add New Theme Repo"
494 | msgstr ""
495 |
496 | #: includes/repo-theme/register-post-type.php:70
497 | msgctxt "themes"
498 | msgid "Edit Theme"
499 | msgstr ""
500 |
501 | #: includes/repo-theme/register-post-type.php:71
502 | msgctxt "themes"
503 | msgid "New Theme"
504 | msgstr ""
505 |
506 | #: includes/repo-theme/register-post-type.php:72
507 | msgctxt "themes"
508 | msgid "All Themes"
509 | msgstr ""
510 |
511 | #: includes/repo-theme/register-post-type.php:73
512 | msgctxt "themes"
513 | msgid "View Theme"
514 | msgstr ""
515 |
516 | #: includes/repo-theme/register-post-type.php:74
517 | msgctxt "themes"
518 | msgid "Search Theme"
519 | msgstr ""
520 |
521 | #: includes/repo-theme/register-post-type.php:75
522 | msgctxt "themes"
523 | msgid "No Theme Found"
524 | msgstr ""
525 |
526 | #: includes/repo-theme/register-post-type.php:76
527 | msgctxt "themes"
528 | msgid "No Theme Found in Trash"
529 | msgstr ""
530 |
531 | #: includes/repo-theme/register-post-type.php:77
532 | msgctxt "themes"
533 | msgid "Themes"
534 | msgstr ""
535 |
536 | #: includes/repo-theme/register-post-type.php:136
537 | msgctxt "themes"
538 | msgid "Theme Name"
539 | msgstr ""
540 |
541 | #: includes/repo-theme/register-post-type.php:156
542 | #: includes/repo-theme/register-post-type.php:159
543 | msgctxt "themes"
544 | msgid "Theme updated."
545 | msgstr ""
546 |
547 | #: includes/repo-theme/register-post-type.php:157
548 | msgctxt "themes"
549 | msgid "Theme field updated."
550 | msgstr ""
551 |
552 | #: includes/repo-theme/register-post-type.php:158
553 | msgctxt "themes"
554 | msgid "Theme field deleted."
555 | msgstr ""
556 |
557 | #: includes/repo-theme/register-post-type.php:161
558 | #, php-format
559 | msgctxt "themes"
560 | msgid "Theme restored to revision from %s"
561 | msgstr ""
562 |
563 | #: includes/repo-theme/register-post-type.php:162
564 | msgctxt "themes"
565 | msgid "Theme published."
566 | msgstr ""
567 |
568 | #: includes/repo-theme/register-post-type.php:163
569 | msgctxt "themes"
570 | msgid "Theme saved."
571 | msgstr ""
572 |
573 | #: includes/repo-theme/register-post-type.php:164
574 | msgctxt "themes"
575 | msgid "Theme submitted."
576 | msgstr ""
577 |
578 | #: includes/repo-theme/register-post-type.php:165
579 | #, php-format
580 | msgctxt "themes"
581 | msgid "Theme scheduled for: %1$s."
582 | msgstr ""
583 |
584 | #: includes/repo-theme/register-post-type.php:167
585 | msgctxt "themes"
586 | msgid "M j, Y @ H:i"
587 | msgstr ""
588 |
589 | #: includes/repo-theme/register-post-type.php:168
590 | msgctxt "themes"
591 | msgid "Theme draft updated."
592 | msgstr ""
593 |
--------------------------------------------------------------------------------
/license.txt:
--------------------------------------------------------------------------------
1 | GNU GENERAL PUBLIC LICENSE
2 | Version 2, June 1991
3 |
4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
6 | Everyone is permitted to copy and distribute verbatim copies
7 | of this license document, but changing it is not allowed.
8 |
9 | Preamble
10 |
11 | The licenses for most software are designed to take away your
12 | freedom to share and change it. By contrast, the GNU General Public
13 | License is intended to guarantee your freedom to share and change free
14 | software--to make sure the software is free for all its users. This
15 | General Public License applies to most of the Free Software
16 | Foundation's software and to any other program whose authors commit to
17 | using it. (Some other Free Software Foundation software is covered by
18 | the GNU Lesser General Public License instead.) You can apply it to
19 | your programs, too.
20 |
21 | When we speak of free software, we are referring to freedom, not
22 | price. Our General Public Licenses are designed to make sure that you
23 | have the freedom to distribute copies of free software (and charge for
24 | this service if you wish), that you receive source code or can get it
25 | if you want it, that you can change the software or use pieces of it
26 | in new free programs; and that you know you can do these things.
27 |
28 | To protect your rights, we need to make restrictions that forbid
29 | anyone to deny you these rights or to ask you to surrender the rights.
30 | These restrictions translate to certain responsibilities for you if you
31 | distribute copies of the software, or if you modify it.
32 |
33 | For example, if you distribute copies of such a program, whether
34 | gratis or for a fee, you must give the recipients all the rights that
35 | you have. You must make sure that they, too, receive or can get the
36 | source code. And you must show them these terms so they know their
37 | rights.
38 |
39 | We protect your rights with two steps: (1) copyright the software, and
40 | (2) offer you this license which gives you legal permission to copy,
41 | distribute and/or modify the software.
42 |
43 | Also, for each author's protection and ours, we want to make certain
44 | that everyone understands that there is no warranty for this free
45 | software. If the software is modified by someone else and passed on, we
46 | want its recipients to know that what they have is not the original, so
47 | that any problems introduced by others will not reflect on the original
48 | authors' reputations.
49 |
50 | Finally, any free program is threatened constantly by software
51 | patents. We wish to avoid the danger that redistributors of a free
52 | program will individually obtain patent licenses, in effect making the
53 | program proprietary. To prevent this, we have made it clear that any
54 | patent must be licensed for everyone's free use or not licensed at all.
55 |
56 | The precise terms and conditions for copying, distribution and
57 | modification follow.
58 |
59 | GNU GENERAL PUBLIC LICENSE
60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
61 |
62 | 0. This License applies to any program or other work which contains
63 | a notice placed by the copyright holder saying it may be distributed
64 | under the terms of this General Public License. The "Program", below,
65 | refers to any such program or work, and a "work based on the Program"
66 | means either the Program or any derivative work under copyright law:
67 | that is to say, a work containing the Program or a portion of it,
68 | either verbatim or with modifications and/or translated into another
69 | language. (Hereinafter, translation is included without limitation in
70 | the term "modification".) Each licensee is addressed as "you".
71 |
72 | Activities other than copying, distribution and modification are not
73 | covered by this License; they are outside its scope. The act of
74 | running the Program is not restricted, and the output from the Program
75 | is covered only if its contents constitute a work based on the
76 | Program (independent of having been made by running the Program).
77 | Whether that is true depends on what the Program does.
78 |
79 | 1. You may copy and distribute verbatim copies of the Program's
80 | source code as you receive it, in any medium, provided that you
81 | conspicuously and appropriately publish on each copy an appropriate
82 | copyright notice and disclaimer of warranty; keep intact all the
83 | notices that refer to this License and to the absence of any warranty;
84 | and give any other recipients of the Program a copy of this License
85 | along with the Program.
86 |
87 | You may charge a fee for the physical act of transferring a copy, and
88 | you may at your option offer warranty protection in exchange for a fee.
89 |
90 | 2. You may modify your copy or copies of the Program or any portion
91 | of it, thus forming a work based on the Program, and copy and
92 | distribute such modifications or work under the terms of Section 1
93 | above, provided that you also meet all of these conditions:
94 |
95 | a) You must cause the modified files to carry prominent notices
96 | stating that you changed the files and the date of any change.
97 |
98 | b) You must cause any work that you distribute or publish, that in
99 | whole or in part contains or is derived from the Program or any
100 | part thereof, to be licensed as a whole at no charge to all third
101 | parties under the terms of this License.
102 |
103 | c) If the modified program normally reads commands interactively
104 | when run, you must cause it, when started running for such
105 | interactive use in the most ordinary way, to print or display an
106 | announcement including an appropriate copyright notice and a
107 | notice that there is no warranty (or else, saying that you provide
108 | a warranty) and that users may redistribute the program under
109 | these conditions, and telling the user how to view a copy of this
110 | License. (Exception: if the Program itself is interactive but
111 | does not normally print such an announcement, your work based on
112 | the Program is not required to print an announcement.)
113 |
114 | These requirements apply to the modified work as a whole. If
115 | identifiable sections of that work are not derived from the Program,
116 | and can be reasonably considered independent and separate works in
117 | themselves, then this License, and its terms, do not apply to those
118 | sections when you distribute them as separate works. But when you
119 | distribute the same sections as part of a whole which is a work based
120 | on the Program, the distribution of the whole must be on the terms of
121 | this License, whose permissions for other licensees extend to the
122 | entire whole, and thus to each and every part regardless of who wrote it.
123 |
124 | Thus, it is not the intent of this section to claim rights or contest
125 | your rights to work written entirely by you; rather, the intent is to
126 | exercise the right to control the distribution of derivative or
127 | collective works based on the Program.
128 |
129 | In addition, mere aggregation of another work not based on the Program
130 | with the Program (or with a work based on the Program) on a volume of
131 | a storage or distribution medium does not bring the other work under
132 | the scope of this License.
133 |
134 | 3. You may copy and distribute the Program (or a work based on it,
135 | under Section 2) in object code or executable form under the terms of
136 | Sections 1 and 2 above provided that you also do one of the following:
137 |
138 | a) Accompany it with the complete corresponding machine-readable
139 | source code, which must be distributed under the terms of Sections
140 | 1 and 2 above on a medium customarily used for software interchange; or,
141 |
142 | b) Accompany it with a written offer, valid for at least three
143 | years, to give any third party, for a charge no more than your
144 | cost of physically performing source distribution, a complete
145 | machine-readable copy of the corresponding source code, to be
146 | distributed under the terms of Sections 1 and 2 above on a medium
147 | customarily used for software interchange; or,
148 |
149 | c) Accompany it with the information you received as to the offer
150 | to distribute corresponding source code. (This alternative is
151 | allowed only for noncommercial distribution and only if you
152 | received the program in object code or executable form with such
153 | an offer, in accord with Subsection b above.)
154 |
155 | The source code for a work means the preferred form of the work for
156 | making modifications to it. For an executable work, complete source
157 | code means all the source code for all modules it contains, plus any
158 | associated interface definition files, plus the scripts used to
159 | control compilation and installation of the executable. However, as a
160 | special exception, the source code distributed need not include
161 | anything that is normally distributed (in either source or binary
162 | form) with the major components (compiler, kernel, and so on) of the
163 | operating system on which the executable runs, unless that component
164 | itself accompanies the executable.
165 |
166 | If distribution of executable or object code is made by offering
167 | access to copy from a designated place, then offering equivalent
168 | access to copy the source code from the same place counts as
169 | distribution of the source code, even though third parties are not
170 | compelled to copy the source along with the object code.
171 |
172 | 4. You may not copy, modify, sublicense, or distribute the Program
173 | except as expressly provided under this License. Any attempt
174 | otherwise to copy, modify, sublicense or distribute the Program is
175 | void, and will automatically terminate your rights under this License.
176 | However, parties who have received copies, or rights, from you under
177 | this License will not have their licenses terminated so long as such
178 | parties remain in full compliance.
179 |
180 | 5. You are not required to accept this License, since you have not
181 | signed it. However, nothing else grants you permission to modify or
182 | distribute the Program or its derivative works. These actions are
183 | prohibited by law if you do not accept this License. Therefore, by
184 | modifying or distributing the Program (or any work based on the
185 | Program), you indicate your acceptance of this License to do so, and
186 | all its terms and conditions for copying, distributing or modifying
187 | the Program or works based on it.
188 |
189 | 6. Each time you redistribute the Program (or any work based on the
190 | Program), the recipient automatically receives a license from the
191 | original licensor to copy, distribute or modify the Program subject to
192 | these terms and conditions. You may not impose any further
193 | restrictions on the recipients' exercise of the rights granted herein.
194 | You are not responsible for enforcing compliance by third parties to
195 | this License.
196 |
197 | 7. If, as a consequence of a court judgment or allegation of patent
198 | infringement or for any other reason (not limited to patent issues),
199 | conditions are imposed on you (whether by court order, agreement or
200 | otherwise) that contradict the conditions of this License, they do not
201 | excuse you from the conditions of this License. If you cannot
202 | distribute so as to satisfy simultaneously your obligations under this
203 | License and any other pertinent obligations, then as a consequence you
204 | may not distribute the Program at all. For example, if a patent
205 | license would not permit royalty-free redistribution of the Program by
206 | all those who receive copies directly or indirectly through you, then
207 | the only way you could satisfy both it and this License would be to
208 | refrain entirely from distribution of the Program.
209 |
210 | If any portion of this section is held invalid or unenforceable under
211 | any particular circumstance, the balance of the section is intended to
212 | apply and the section as a whole is intended to apply in other
213 | circumstances.
214 |
215 | It is not the purpose of this section to induce you to infringe any
216 | patents or other property right claims or to contest validity of any
217 | such claims; this section has the sole purpose of protecting the
218 | integrity of the free software distribution system, which is
219 | implemented by public license practices. Many people have made
220 | generous contributions to the wide range of software distributed
221 | through that system in reliance on consistent application of that
222 | system; it is up to the author/donor to decide if he or she is willing
223 | to distribute software through any other system and a licensee cannot
224 | impose that choice.
225 |
226 | This section is intended to make thoroughly clear what is believed to
227 | be a consequence of the rest of this License.
228 |
229 | 8. If the distribution and/or use of the Program is restricted in
230 | certain countries either by patents or by copyrighted interfaces, the
231 | original copyright holder who places the Program under this License
232 | may add an explicit geographical distribution limitation excluding
233 | those countries, so that distribution is permitted only in or among
234 | countries not thus excluded. In such case, this License incorporates
235 | the limitation as if written in the body of this License.
236 |
237 | 9. The Free Software Foundation may publish revised and/or new versions
238 | of the General Public License from time to time. Such new versions will
239 | be similar in spirit to the present version, but may differ in detail to
240 | address new problems or concerns.
241 |
242 | Each version is given a distinguishing version number. If the Program
243 | specifies a version number of this License which applies to it and "any
244 | later version", you have the option of following the terms and conditions
245 | either of that version or of any later version published by the Free
246 | Software Foundation. If the Program does not specify a version number of
247 | this License, you may choose any version ever published by the Free Software
248 | Foundation.
249 |
250 | 10. If you wish to incorporate parts of the Program into other free
251 | programs whose distribution conditions are different, write to the author
252 | to ask for permission. For software which is copyrighted by the Free
253 | Software Foundation, write to the Free Software Foundation; we sometimes
254 | make exceptions for this. Our decision will be guided by the two goals
255 | of preserving the free status of all derivatives of our free software and
256 | of promoting the sharing and reuse of software generally.
257 |
258 | NO WARRANTY
259 |
260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
268 | REPAIR OR CORRECTION.
269 |
270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
278 | POSSIBILITY OF SUCH DAMAGES.
279 |
280 | END OF TERMS AND CONDITIONS
281 |
282 | How to Apply These Terms to Your New Programs
283 |
284 | If you develop a new program, and you want it to be of the greatest
285 | possible use to the public, the best way to achieve this is to make it
286 | free software which everyone can redistribute and change under these terms.
287 |
288 | To do so, attach the following notices to the program. It is safest
289 | to attach them to the start of each source file to most effectively
290 | convey the exclusion of warranty; and each file should have at least
291 | the "copyright" line and a pointer to where the full notice is found.
292 |
293 | {description}
294 | Copyright (C) {year} {fullname}
295 |
296 | This program is free software; you can redistribute it and/or modify
297 | it under the terms of the GNU General Public License as published by
298 | the Free Software Foundation; either version 2 of the License, or
299 | (at your option) any later version.
300 |
301 | This program is distributed in the hope that it will be useful,
302 | but WITHOUT ANY WARRANTY; without even the implied warranty of
303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
304 | GNU General Public License for more details.
305 |
306 | You should have received a copy of the GNU General Public License along
307 | with this program; if not, write to the Free Software Foundation, Inc.,
308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
309 |
310 | Also add information on how to contact you by electronic and paper mail.
311 |
312 | If the program is interactive, make it output a short notice like this
313 | when it starts in an interactive mode:
314 |
315 | Gnomovision version 69, Copyright (C) year name of author
316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
317 | This is free software, and you are welcome to redistribute it
318 | under certain conditions; type `show c' for details.
319 |
320 | The hypothetical commands `show w' and `show c' should show the appropriate
321 | parts of the General Public License. Of course, the commands you use may
322 | be called something other than `show w' and `show c'; they could even be
323 | mouse-clicks or menu items--whatever suits your program.
324 |
325 | You should also get your employer (if you work as a programmer) or your
326 | school, if any, to sign a "copyright disclaimer" for the program, if
327 | necessary. Here is a sample; alter the names:
328 |
329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program
330 | `Gnomovision' (which makes passes at compilers) written by James Hacker.
331 |
332 | {signature of Ty Coon}, 1 April 1989
333 | Ty Coon, President of Vice
334 |
335 | This General Public License does not permit incorporating your program into
336 | proprietary programs. If your program is a subroutine library, you may
337 | consider it more useful to permit linking proprietary applications with the
338 | library. If this is what you want to do, use the GNU Lesser General
339 | Public License instead of this License.
340 |
341 |
--------------------------------------------------------------------------------