verifyPackage( 'wiki' ); // bypass lookup_content_inc.php as we can't prevent it parsing faulty pages unset($_REQUEST['content_id']); // Disable parsing data if not asking to preview page $_REQUEST["parse"] = false; include WIKI_PKG_INCLUDE_PATH.'lookup_page_inc.php'; if( $gContent->isValid() ) { $gContent->verifyUpdatePermission(); } else { $gContent->verifyCreatePermission(); } //make comment count for this page available for templates if( $gBitSystem->isFeatureActive( 'wiki_comments' ) && !empty( $_REQUEST['page_id'] ) ) { $gComment = new LibertyComment(); $numComments = $gComment->getNumComments($gContent->mContentId); $gBitSmarty->assign('comments_count', $numComments); } #edit preview needs this if( !isset( $_REQUEST['title'] ) && isset( $gContent->mInfo['title'] ) ) { $_REQUEST['title'] = $gContent->mInfo['title']; } if( $gContent->isLocked() ) { $gBitSystem->fatalError( 'Cannot edit page because it is locked' ); } $gContent->invokeServices( 'content_edit_function' ); if( !empty( $gContent->mInfo ) ) { $formInfo = $gContent->mInfo; $data_to_edit = !empty( $gContent->mInfo['data'] ) ? $gContent->mInfo['data'] : ''; if (!empty($_REQUEST['section'])) { $section = $_REQUEST['section']; $data_to_edit = extract_section($data_to_edit,$section); $formInfo['data'] = $data_to_edit; $formInfo['edit_section'] = 1; $formInfo['section'] = $_REQUEST['section']; } $formInfo['edit'] = $data_to_edit; $formInfo['edit_comment'] = ''; } $gBitSmarty->assign( 'footnote', '' ); $gBitSmarty->assign( 'has_footnote', 'n' ); if( $gBitSystem->isFeatureActive( 'wiki_footnotes' ) ) { if( $gBitUser->mUserId ) { $footnote = $gContent->getFootnote( $gBitUser->mUserId ); $gBitSmarty->assign( 'footnote', $footnote ); if( $footnote ) $gBitSmarty->assign( 'has_footnote', 'y' ); $gBitSmarty->assign( 'parsed_footnote', LibertyContent::parseDataHash( $footnote ) ); if( isset( $_REQUEST['footnote'] ) ) { $gBitSmarty->assign( 'parsed_footnote', LibertyContent::parseDataHash( $_REQUEST['footnote'] ) ); $gBitSmarty->assign( 'footnote', $_REQUEST['footnote'] ); $gBitSmarty->assign( 'has_footnote', 'y' ); if( empty( $_REQUEST['footnote'] ) ) { $gContent->expungeFootnote( $gBitUser->mUserId ); } else { $gContent->storeFootnote( $gBitUser->mUserId, $_REQUEST['footnote'] ); } } } } if( isset( $_REQUEST["edit"] ) ) { $formInfo['edit'] = $_REQUEST["edit"]; } if(isset($_REQUEST["section"])) { $formInfo['section'] = $_REQUEST["section"]; $formInfo['edit_section'] = 1; } if( isset( $_REQUEST['title'] ) ) { $formInfo['title'] = $_REQUEST['title']; } elseif( isset( $_REQUEST['page'] ) ) { $formInfo['title'] = $_REQUEST['page']; } if( isset( $_REQUEST["description"] ) ) { $formInfo['description'] = $_REQUEST["description"]; } $formInfo['edit_comment'] = $_REQUEST["edit_comment"] ?? ''; $cat_obj_type = BITPAGE_CONTENT_TYPE_GUID; if( $gBitSystem->isFeatureActive( 'wiki_copyrights' ) ) { if( isset( $_REQUEST['copyrightTitle'] ) ) { $gBitSmarty->assign( 'copyrightTitle', $_REQUEST["copyrightTitle"] ); } if( isset( $_REQUEST['copyrightYear'] ) ) { $gBitSmarty->assign( 'copyrightYear', $_REQUEST["copyrightYear"] ); } if( isset( $_REQUEST['copyrightAuthors'] ) ) { $gBitSmarty->assign( 'copyrightAuthors', $_REQUEST["copyrightAuthors"] ); } } // Pro // Check if the page has changed if( isset( $_REQUEST["fCancel"] ) ) { if( BitBase::verifyId( $gContent->mContentId ) ) { header( "Location: ".$gContent->getDisplayUrl() ); } else { header( "Location: ".WIKI_PKG_URL ); } die; } elseif( isset( $_REQUEST["fSavePage"] ) ) { // Check if all Request values are delivered, and if not, set them // to avoid error messages. This can happen if some features are // disabled // add permisions here otherwise return error! if( $gBitSystem->isFeatureActive( 'wiki_copyrights' ) && isset( $_REQUEST['copyrightAuthors'] ) && !empty( $_REQUEST['copyrightYear'] ) && !empty( $_REQUEST['copyrightTitle'] ) ) { require_once WIKI_PKG_INCLUDE_PATH.'copyrights_lib.php'; $copyrightYear = $_REQUEST['copyrightYear']; $copyrightTitle = $_REQUEST['copyrightTitle']; $copyrightAuthors = $_REQUEST['copyrightAuthors']; $copyrightslib->add_copyright( $gContent->mPageId, $copyrightTitle, $copyrightYear, $copyrightAuthors, $gBitUser->mUserId ); } // $sBitLinks = new BitLinks(); if( $gContent->mPageId ) { if( isset( $_REQUEST['isminor'] ) && $_REQUEST['isminor']=='on' ) { $_REQUEST['minor'] = true; } else { $_REQUEST['minor'] = false; $links = []; // $sBitLinks->get_links( $edit ); // $wikilib->cache_links( $links ); // $sBitLinks->storeLinks( $links ); } } else { $links = []; // $sBitLinks->get_links( $_REQUEST["edit"] ); // $notcachedlinks = $sBitLinks->get_links_nocache( $_REQUEST["edit"] ); // $cachedlinks = array_diff( $links, $notcachedlinks ); // $sBitLinks->cache_links( $cachedlinks ); // $sBitLinks->storeLinks( $cachedlinks ); } $data_to_parse = $formInfo['edit']; if (!empty($formInfo['section']) && !empty($gContent->mInfo['data']) ) { $full_page_data = $gContent->mInfo['data']; $data_to_parse = replace_section($full_page_data,$formInfo['section'],$formInfo['edit']); $_REQUEST["edit"] = $data_to_parse; } if( $gContent->store( $_REQUEST ) ) { if( $gBitSystem->isFeatureActive( 'wiki_watch_author' ) ) { $gBitUser->storeWatch( "wiki_page_changed", $gContent->mPageId, $gContent->mContentTypeGuid, $_REQUEST['title'], $gContent->getDisplayUrl() ); } $return = [ 'title' => $gContent->mPageName ]; header( "Location: ".$gContent->getDisplayUrlFromHash( $return ) ); die; } $formInfo = $_REQUEST; $formInfo['data'] = &$_REQUEST['edit']; } elseif( !empty( $_REQUEST['edit'] ) ) { // perhaps we have a javascript non-saving form submit $formInfo = $_REQUEST; $formInfo['data'] = &$_REQUEST['edit']; } if( isset( $_REQUEST['format_guid'] ) && !isset( $gContent->mInfo['format_guid'] ) ) { $formInfo['format_guid'] = $gContent->mInfo['format_guid'] = $_REQUEST['format_guid']; } $preview = false; if( isset( $_REQUEST["preview"] ) ) { // Chrome does not like seeing the page source in the page itself. header("X-XSS-Protection: 0"); $preview = true; $gBitSmarty->assign( 'title',!empty( $_REQUEST["title"] ) ? $_REQUEST["title"] : $gContent->mPageName ); if (!empty($formInfo['section'])) { $formInfo['edit_section'] = 1; } $data_to_parse['data'] = $formInfo['edit']; if( !empty( $formInfo['section'] ) && !empty( $gContent->mInfo['data'] )) { $full_page_data = $gContent->mInfo['data']; } $formInfo['parsed_data'] = LibertyContent::parseDataHash( $data_to_parse ); $gContent->invokeServices( 'content_preview_function' ); } $gBitSmarty->assign( 'preview',$preview ); if( $gContent->isInStructure() ) { $gBitSmarty->assign( 'showstructs', $gContent->getStructures() ); } // Flag for 'page bar' that currently 'Edit' mode active // so no need to show comments & attachments, but need // to show 'wiki quick help' $gBitSmarty->assign( 'edit_page', 'y' ); // formInfo might be set due to a error on submit if( empty( $formInfo ) ) { $formInfo = &$gContent->mInfo; } // make original page title available for template $formInfo['original_title'] =( !empty( $gContent->mInfo['title'] ) ) ? $gContent->mInfo['title'] : "" ; $gBitSmarty->assign( 'pageInfo', $formInfo ); $gBitSmarty->assign( 'errors', $gContent->mErrors ); if( $gBitSystem->isPackageActive( 'ckeditor' ) ) { // loadCkEditor(); } $gBitSystem->display( 'bitpackage:wiki/edit_page.tpl', 'Edit: '.$gContent->getTitle() , [ 'display_mode' => 'edit' ] ); //******************* WIKI Edit Functions function htmldecode( $string ) { $string = strtr( $string, array_flip( get_html_translation_table( HTML_ENTITIES ) ) ); $string = preg_replace( "/&#([0-9]+);/me", "chr('\\1')", $string ); return $string; } function parse_output( &$obj, &$parts,$i ) { if( !empty( $obj->parts ) ) { for( $i=0; $iparts ); $i++ ) { parse_output( $obj->parts[$i], $parts,$i ); } } else { $ctype = $obj->ctype_primary.'/'.$obj->ctype_secondary; switch( $ctype ) { case 'application/x-tikiwiki': $aux["body"] = $obj->body; $ccc=$obj->headers["content-type"]; $items = mb_split( ';',$ccc ); foreach( $items as $item ) { $portions = mb_split( '=',$item ); if( isset( $portions[0] ) &&isset( $portions[1] ) ) { $aux[trim( $portions[0] )]=trim( $portions[1] ); } } $parts[]=$aux; } } } function extract_section($data,$section) { global $gContent, $gBitSystem; if( $gContent->mInfo['format_guid'] == PLUGIN_GUID_TIKIWIKI ) { $section_data = preg_split( "/\n(".( str_repeat( "!", $gBitSystem->getConfig( "wiki_section_edit" ) ) )."[^!])/", "\n$data", -1, PREG_SPLIT_DELIM_CAPTURE ); $a = 1 + ($section - 1) * 2; $b = $a + 1; return $section_data[$a] . $section_data[$b]; } } function replace_section($data,$section,$new_section_data) { global $gContent, $gBitSystem; if( $gContent->mInfo['format_guid'] == PLUGIN_GUID_TIKIWIKI ) { $section_data = preg_split("/(\n".( str_repeat( "!", $gBitSystem->getConfig( "wiki_section_edit" ) ) )."[^!])/", "\n$data" ,-1,PREG_SPLIT_DELIM_CAPTURE); $a = 1 + ($section - 1) * 2; $b = $a + 1; $section_data[$a] = "\n"; $section_data[$b] = $new_section_data; return substr(implode('',$section_data),1); } } function compare_import_versions( $a1, $a2 ) { return $a1["version"] - $a2["version"]; } /** * \brief Parsed HTML tree walker( used by HTML sucker ) * * This is initial implementation( stupid... w/o any intellegence( almost : ) ) * It is rapidly designed version... just for test: 'can this feature be useful'. * Later it should be replaced by well designed one :) don't bash me now : ) * * \param &$c array -- parsed HTML * \param &$src string -- output string * \param &$p array -- ['stack'] = closing strings stack, ['listack'] = stack of list types currently opened ['first_td'] = flag: 'is was just before this ' */ function walk_and_parse( &$c, &$src, &$p ) { for( $i=0; $i <= $c["contentpos"]; $i++ ) { // If content type 'text' output it to destination... switch ($c[$i]["type"]) { case "text": $src .= $c[$i]["data"]; break; case "tag": switch ($c[$i]["data"]["type"]) { case "open": switch ($c[$i]["data"]["name"]) { case "br": $src .= "\n"; break; case "title": $src .= "\n!"; $p['stack'][] = [ 'tag' => 'title', 'string' => "\n" ]; break; case "p": $src .= "\n"; $p['stack'][] = [ 'tag' => 'p', 'string' => "\n" ]; break; case "b": $src .= '__'; $p['stack'][] = [ 'tag' => 'b', 'string' => '__' ]; break; case "i": $src .= "''"; $p['stack'][] = [ 'tag' => 'i', 'string' => "''" ]; break; case "u": $src .= "=="; $p['stack'][] = [ 'tag' => 'u', 'string' => "==" ]; break; case "center": $src .= '::'; $p['stack'][] = [ 'tag' => 'center', 'string' => '::' ]; break; case "code": $src .= '-+'; $p['stack'][] = [ 'tag' => 'code', 'string' => '+-' ]; break; // headers detection looks like real suxx code... // but possible it run faster :) I don't know where is profiler in PHP... case "h1": $src .= "\n!"; $p['stack'][] = [ 'tag' => 'h1', 'string' => "\n" ]; break; case "h2": $src .= "\n!!"; $p['stack'][] = [ 'tag' => 'h2', 'string' => "\n" ]; break; case "h3": $src .= "\n!!!"; $p['stack'][] = [ 'tag' => 'h3', 'string' => "\n" ]; break; case "h4": $src .= "\n!!!!"; $p['stack'][] = [ 'tag' => 'h4', 'string' => "\n" ]; break; case "h5": $src .= "\n!!!!!"; $p['stack'][] = [ 'tag' => 'h5', 'string' => "\n" ]; break; case "h6": $src .= "\n!!!!!!"; $p['stack'][] = [ 'tag' => 'h6', 'string' => "\n" ]; break; case "pre": $src .= '~pp~'; $p['stack'][] = [ 'tag' => 'pre', 'string' => '~/pp~' ]; break; // Table parser case "table": $src .= '||'; $p['stack'][] = [ 'tag' => 'table', 'string' => '||' ]; break; case "tr": $p['first_td'] = true; break; case "td": $src .= $p['first_td'] ? '' : '|'; $p['first_td'] = false; break; // Lists parser case "ul": $p['listack'][] = '*'; break; case "ol": $p['listack'][] = '#'; break; case "li": // Generate wiki list item according to current list depth. //( ensure '*/#' starts from begining of line ) for ( $l = ''; strlen( $l ) < count( $p['listack'] ); $l .= end( $p['listack'] ) ) ; $src .= "\n$l "; break; case "font": // If color attribute present in tag if (isset( $c[$i]["pars"]["color"]["value"] )) { $src .= '~~' . $c[$i]["pars"]["color"]["value"] . ':'; $p['stack'][] = [ 'tag' => 'font', 'string' => '~~' ]; } break; case "img": // If src attribute present in tag if (isset( $c[$i]["pars"]["src"]["value"] )) { // Note what it produce( img ) not {img}! Will fix this below... $src .= '( img src=' . $c[$i]["pars"]["src"]["value"] . ' )'; } break; case "a": // If href attribute present in tag if (isset( $c[$i]["pars"]["href"]["value"] )) { $src .= '[' . $c[$i]["pars"]["href"]["value"] . '|'; $p['stack'][] = [ 'tag' => 'a', 'string' => ']' ]; } break; } break; default: switch ($c[$i]["data"]["name"]) { case "ul": if (end( $p['listack'] ) == '*') array_pop( $p['listack'] ); break; case "ol": if (end( $p['listack'] ) == '#') array_pop( $p['listack'] ); break; default: $e = end( $p['stack'] ); if ($c[$i]["data"]["name"] == $e['tag']) { $src .= $e['string']; array_pop( $p['stack'] ); } break; } break; } break; } // Recursive call on tags with content... if( isset( $c[$i]["content"] ) ) { // if( substr( $src, -1 )!= " " )$src .= " "; walk_and_parse( $c[$i]["content"], $src, $p ); } } } if( isset( $_REQUEST["suck_url"] ) ) { if( !$gBitSystem->isFeatureActive( 'wiki_url_import' ) ) { $gBitSystem->fatalError( KernelTools::tra( "Importing remote URLs is disabled" )); } // Suck another page and append to the end of current require_once WIKI_PKG_INCLUDE_PATH.'htmlparser/html_parser_inc.php'; $suck_url = $_REQUEST["suck_url"] ?? ''; $parsehtml = isset( $_REQUEST["parsehtml"] ) ? ( $_REQUEST["parsehtml"] == 'on' ? 'y' : 'n' ): 'n'; if( isset( $_REQUEST['do_suck'] ) && strlen( $suck_url ) > 0 ) { // \note by zaufi // This is ugly implementation of wiki HTML import. // I think it should be plugable import/export converters with ability // to choose from edit form what converter to use for operation. // In case of import converter, it can try to guess what source // file is( using mime type from remote server response ). // Of couse converters may have itsown configuration panel what should be // pluged into wiki page edit form too...( like HTML importer may have // flags 'strip HTML tags' and 'try to convert HTML to wiki' : ) // At least one export filter for wiki already coded : ) -- PDF exporter... $parsed_url = parse_url($suck_url); //Disallow urls without schema (usually relative urls), or http(s) if(!isset($parsed_url['scheme']) || ($parsed_url['scheme']!='http' && $parsed_url['scheme']!='https')){ $gBitSystem->fatalError( KernelTools::tra( "Invalid URL; not absolute or not HTTP" )); } //Make sure the passed host isn't local if(!isset($parsed_url['host']) || ($parsed_url['host']=='localhost') || strncmp($parsed_url['host'],"127.",4)==0){ $gBitSystem->fatalError( KernelTools::tra( "The host specified is either empty or local." )); } $sdta = @file_get_contents( $suck_url ); if( isset( $php_errormsg ) && strlen( $php_errormsg ) ) { $gBitSystem->fatalError( KernelTools::tra( "Can't import remote HTML page" )); } // Need to parse HTML? if( $parsehtml == 'y' ) { // Read compiled( serialized ) grammar $grammarfile = WIKI_PKG_INCLUDE_PATH.'htmlparser/htmlgrammar.cmp'; if( !$fp = @fopen( $grammarfile,'r' ) ) { $gBitSystem->fatalError( KernelTools::tra( "Can't parse remote HTML page" )); } $grammar = unserialize( fread( $fp, filesize( $grammarfile ) ) ); fclose( $fp ); // create parser object, insert html code and parse it $htmlparser = new HtmlParser( $sdta, $grammar, '', 0 ); $htmlparser->Parse(); // Should I try to convert HTML to wiki? $parseddata = ''; $p = [ 'stack' => [], 'listack' => [], 'first_td' => false ]; walk_and_parse( $htmlparser->content, $parseddata, $p ); // Is some tags still opened?( It can be if HTML not valid, but this is not reason // to produce invalid wiki : ) while( count( $p['stack'] ) ) { $e = end( $p['stack'] ); $sdta .= $e['string']; array_pop( $p['stack'] ); } // Unclosed lists r ignored... wiki have no special start/end lists syntax.... // OK. Things remains to do: // 1 ) fix linked images $parseddata = preg_replace( ',\[(.*)\|\( img src=(.*)\)\],mU','{img src=$2 link=$1}', $parseddata ); // 2 ) fix remains images( not in links ) $parseddata = preg_replace( ',\( img src=(.*)\),mU','{img src=$1}', $parseddata ); // 3 ) remove empty lines $parseddata = preg_replace( ",[\n]+,mU","\n", $parseddata ); // Reassign previous data $sdta = $parseddata; } $_REQUEST['edit'] .= $sdta; } }