Commits

Jacob Moen committed ff09f33

Link management fixed: using a SimpleWiki raw link handler to manage wiki links.

  • Participants
  • Parent commits 7657486

Comments (0)

Files changed (7)

File protected/modules/wiki/assets/wiki.css

 	text-decoration: none;
 }
 
+.wiki-text a {
+    text-decoration: none;
+}
+
 /* markItUp skin */
 .markItUp * {
     margin:0px; padding:0px;

File protected/modules/wiki/controllers/DefaultController.php

 
 class DefaultController extends Controller
 {
+    protected $wikilinks = array();
+
     public function actions()
     {
         return array(
         $this->redirect(array('view', 'uid' => 'index'));
     }
 
+
+    public function myreplacelinkhandler($node)
+    {
+        $rawlink = $node->linkparts->rawlink; // this is the link set by the user in [[somerawlink|go somewhere]]
+        $caption = $node->caption;
+
+        $this->wikilinks[] = array(
+                'title' => $caption,
+                'wiki_uid' => $rawlink,
+            );
+
+        // if link is invalid, set $node->unknown = TRUE; and return $node before next line - this causes the dashed underline and question mark to alert the author
+
+        $node->linkparts->rawlinkaddress = $rawlink; // or some valid href value
+
+        return $node;
+    }
+
+    public function mylinkhandler($node)
+    {
+        $rawlink = $node->linkparts->rawlink; // this is the link set by the user in [[somerawlink|go somewhere]]
+        $caption = $node->caption;
+
+        // if link is invalid, set $node->unknown = TRUE; and return $node before next line - this causes the dashed underline and question mark to alert the author
+
+        $node->linkparts->rawlinkaddress = $rawlink; // or some valid href value
+
+        return $node;
+    }
+
     /**
      * Handles viewing a page
      *
 
                 $wiki = new Muster\SimpleWiki($text);
                 $wiki->register_events(array('onemit' => array($wiki,'auto_quicktoc')));
+                $wiki->register_rawlink_handler(array($this,"mylinkhandler"));
                 $text = $wiki->get_html();
 
-                $text = $this->replaceWikiLinks($text);
+                $text = $this->replaceWikiLinks($text, $page->content);
 
                 Yii::app()->cache->set($cacheId, $text);
             }
      * @param string $text
      * @return string
      */
-    private function replaceWikiLinks($text)
+    private function replaceWikiLinks($text, $content)
     {
-        $links = $this->getWikiLinks($text);
-        foreach($links as $search => $link)
+        $this->getWikiLinks($content);
+        foreach($this->wikilinks as $link)
         {
             $htmlOptions = array();
             $wikiLink = Wikilink::model()->findByWikiUid($link['wiki_uid']);
             if(!empty($wikiLink)) //FIXME: does NOT handle pipe in wikilinks..
                 $htmlOptions['class'] = ($wikiLink->page_to_id) ? 'existing' : 'nonexisting';
             $replace = CHtml::link($link['title'], array('view', 'uid' => $link['wiki_uid']), $htmlOptions);
-            $text = str_replace($search, $replace, $text);
+            $text = str_replace( '<a href="' . $link['wiki_uid'] .'">' . $link['title'] . '</a>', $replace, $text);
         }
 
         return $text;
      */
     private function getWikiLinks($input)
     {
-        $links = array();
-        preg_match_all('~\[\[(.*?)\]\]~', $input, $matches);
-        foreach($matches[0] as $i => $fullMatch)
-        {
-            $contentMatch = $matches[1][$i];
-            $parts = explode('|', $contentMatch);
-            $first = array_shift($parts);
-            if(count($parts))
-            {
-                $links[$fullMatch] = array(
-                    'title' => implode('', $parts),
-                    'wiki_uid' => $first,
-                );
-            }
-            else
-            {
-                $links[$fullMatch] = array(
-                    'title' => $first,
-                    'wiki_uid' => $first,
-                );
-            }
-        }
-        return $links;
+        $this->wikilinks = array();
+        $wiki = new Muster\SimpleWiki($input);
+        $wiki->register_rawlink_handler(array($this,"myreplacelinkhandler"));
+        $wiki->get_html();
+
+        return $this->wikilinks;
     }
 }

File protected/modules/wiki/models/Wikilink.php

 		));
 	}
 
-}
+}

File protected/modules/wiki/models/Wikipage.php

 		);
 	}
 
+	protected function beforeSave()
+	{
+	    if(parent::beforeSave())
+	    {
+            preg_match_all("#\[\[([^:]+?)\]\]#", $this->content, $matches);
+            foreach ($matches[0] as $match) {
+                $exploded = explode('|', $match);
+                if(preg_match('/\s/', $exploded[0]) > 0)
+                {
+                    $trimmed = trim($exploded[0], chr(0x5B).chr(0x5D));
+                    $fixed = str_replace(' ', '_', $trimmed);
+                    $this->content = str_replace($match, '[[' . $fixed . '|' . $trimmed . ']]', $this->content);
+                }
+            }
+            return true;
+	    }
+	    else
+	        return false;
+	}
+
 	/**
 	 * @param string $uid unique wiki id
 	 */

File protected/modules/wiki/vendors/simplewiki1_1/muster/SimpleWiki.php

 namespace Muster;
 
 use StdClass;
-use
+use 
 	Muster\Simplewiki\DocNode,
 	Muster\Simplewiki\Parser,
 	Muster\Simplewiki\Emitter;
 
 /**
-@mainpage Simplewiki  Markup Parser and HTML generator.
+@mainpage Simplewiki  Markup Parser and HTML generator. 
 @htmlonly <b><a target="_blank" href="http://simplewiki.org">SimpleWiki</a></b> @endhtmlonly is a wiki markup parser and HTML emitter.
  Go to @htmlonly <b><a target="_blank" href="http://simplewiki.org">website</a></b>@endhtmlonly.
 
 @date October 22, 2012
 
 @par Software roots:
-Modelled after creole.py and creole2html.py
-	at http://wiki.sheep.art.pl/Wiki%20Creole%20Parser%20in%20Python
+Modelled after creole.py and creole2html.py 
+	at http://wiki.sheep.art.pl/Wiki%20Creole%20Parser%20in%20Python 
 	- author of creole.py and creole2html.py: Radomir Dopieralski
 	- many of the regular expressions were based on creole.py
-
+	
 @par
 The notions for decorator and block declaration markup were derived in part
 	from the wikistyle and directive markup developed for PmWiki (pmwiki.org)
 	by its author, Patrick Michaud.
-
-@par Two steps:
+	
+@par Two steps: 
 	-# build document tree (parser)
 	-# use document tree to generate html (emitter)
 
 	http://www.wikicreole.org/wiki/Creole1.0 \n
 	http://www.wikicreole.org/wiki/CheatSheet \n
 	http://www.wikicreole.org/wiki/CreoleAdditions \n
-@par
+@par	
 extensions and modifications to creole:
 - raw url is minimally recognized separately from link for performance reasons
 - table markup requires closing (trailing righmost) "|"
-- link format =>
+- link format => 
 	[[url or SymbolicLink:Selector, or \#anchor\n
-		| caption (default = url or SymbolicLink)\n
+		| caption (default = url or SymbolicLink)\n 
 		| title]]\n
 - target anchor written as [[\#anchor]]
 - image format =>\n
 - no alternate link syntax
 - no monospace, use inline span decoration instead (\%s mono%...%%)
 - for indented paragraphs, use dl markup (:), or blockquote or dl block declarations or instead
-- no plugin extension per se, though class methods, symlinks, events, and macros
+- no plugin extension per se, though class methods, symlinks, events, and macros 
 	can be registered by client software
 - superscipts, subscripts, underline, overline, and strikeout are provided with span decorator
 
 @par Markup extensions:
-Arguments can be associated with most document objects
+Arguments can be associated with most document objects 
 	through decorators and declarations
 @par
-	- identifier=value ("=" separator) means attribute,
+	- identifier=value ("=" separator) means attribute, 
 		- value can be delimited with double or single quotes
 	- identifier:value (":" separator) means css style rule
 		- value can be delimited with double or single quotes
 	- value on its own means class or command (eg. zebrastripes)
 		referred to as 'class method'
-	- callouts for classes can be registered
+	- callouts for classes can be registered 
 		with SimpleWiki by client software
 	- element selectors of callouts can vary interpretation of arguments
 @par
 @par
 inline decorators => \%selector ...% (selector = l,i,s,c)
 	- selectors = l (lower case 'L') for list, i for image, s for span, c for code.
-	- if l, i or c are not immediately followed by their respective objects,
+	- if l, i or c are not immediately followed by their respective objects, 
 		deocrators are returned as text
 	- s creates a span
 	- %% = (empty inline decorator) is optional close for span decorator
 
-@par
+@par	
 block decorators => |:selector ...:| (selector = h,p,ul,ol,li,table,tr,th,td,b,pre)
 	- "b" is block divider and creates an empty div
-
+	
 @par
 block declaration => (:selector[\\d]* ...:)\<text\>(:selector[\\d]*end:)
-	- block declarations, both opening and closing tags,
+	- block declarations, both opening and closing tags, 
 		must be the first non-space characters of a line
-	- opening tags can be followed by text on same line
+	- opening tags can be followed by text on same line 
 		to prevent generation of paragraph markup
 	- can be nested based on id number [\\d]*
 	- native selectors:\n
 $wiki = new SimpleWiki($raw_text);
 $html = $wiki->get_html();
 @endverbatim
-@par
+@par 
 Or for iterative usage:
 @verbatim
 $wiki = new SimpleWiki(); //once
 /**
 	Facade, and default method classes, macros, events and symlinks
 	public methods.
-
+	
 	$wiki = new SimpleWiki($markup)  - create object to process markup text
-
+	
 	note that SimpleWiki registers a number of default behaviours with SimpleWikiEmitter
 */
 class SimpleWiki
 	protected $_footnotes;
 	protected $_footnotereferences;
 	protected $_working_parser;
-
+	
 /**@{ @name constructor */
 /** The constructor for Simplewiki. The constructor does the following:
 	- invokes internal instances of the parser and emitter
 	- registers standard callbacks, macros, and symlinks
-
+	
 	@param string $text the markup text to be processed.
 	@return void
-
+	
 	@sa prepare\n
 		register_class_callbacks\n
 		register_property_callbacks\n
 		register_macro_callbacks\n
 		register_symlinks
-
+	
 	@par Specific operations:
 */
 	public function __construct($text = NULL)
 		$this->_parser = new Parser($text);
 		/// instantiate and save new emitter
 		$this->_emitter = new Emitter();
-		/// register standard class callbacks
+		/// register standard class callbacks 
 		$this->register_class_callbacks(
 			array(
 				'span' => array(
 					'html'=> array($this,'callback_code_html'))
 			)
 		);
-		/// register standard macro callbacks
+		/// register standard macro callbacks 
 		$this->register_macro_callbacks(
 			array(
 				'quicktoc' => array($this,'macro_quicktoc')
 			)
 		);
-		/// register standard symlinks
+		/// register standard symlinks 
 		$this->register_symlinks(array('Anchor'=>'','Local'=>''));
 	}
 	public function __clone()
 	}
 /**@}*/
 /**@{ @name settings methods*/
-/** Get or set permission to allow html in markup. ...from within "<pre>" elements.
+/** Get or set permission to allow html in markup. ...from within "<pre>" elements. 
 	@param boolean $bool true allows html (default), false disallows
 	@return boolean
 
 	@return void
 
 	$classname can be any valid class string
-
+	
 	Callback methods are passed a DocNode object, and are expected to manipulate
 		and return that object
-
+		
 	This method is a facade for the emitter object (Emitter), to which the callbacks are passed directly.
 
 	@sa DocNode::$type for $nodetype options
 	@return void
 
 	$propertyname can be any valid class string
-
+	
 	Callback methods are passed a DocNode object, and are expected to manipulate
 		and return that object
-
+		
 	This method is a facade for the emitter object (Emitter), to which the callbacks are passed directly.
 
 	@sa DocNode::$type for $nodetype options
 	@param array $symlinks  [$symlink]=>$value\n
 	$value should be an absolute or relative html link (href/src)
 	@return void
-
+	
 	$symlink can be any string of letters with a leading uppercase letter.
-
+	
 	This method is a facade for the emitter object (Emitter), to which the callbacks are passed directly.
-
+	
 	@sa register_symlink_handler
 	@sa Emitter::register_symlinks
 */
 
 	$handler is handed a DocNode object, and is expected to manipulate
 		and return that object.
-
+	
 	This method is a facade for the emitter object (Emitter), to which the callbacks are passed directly.
-
+	
 	@sa register_symlinks
 	@sa Emitter::register_symlink_handler
 */
 	@param array $callbacks in the form <i>[$macroname]=>$methodref</i>\n
 	$methodref is typically <i> array(objectref,'methodname') </i>
 	@return void
-
+	
 	$macro callbacks are handed a DocNode object, and are expected to manipulate
 		and return that object.
-
+		
 	This method is a facade for the emitter object (Emitter), to which the callbacks are passed directly.
 	@sa Emitter::register_macro_callouts
 */
-	public function register_macro_callbacks($callbacks)
+	public function register_macro_callbacks($callbacks) 
 	{
 		$emitter = $this->_emitter;
 		$emitter->register_macro_callouts($callbacks);
 	@param array $callbacks in the form <i>[$eventname]=>$methodref</i>\n
 	$methodref is typically <i> array(objectref,'methodname') </i>
 	@return void
-
+	
 	$eventname can be 'onemit' or 'onafteremit'
-
+	
 	$event callbacks are handed a DocNode object, and are expected to manipulate
 		and return that object.
-
+		
 	This method is a facade for the emitter object (Emitter), to which the callbacks are passed directly.
 	@sa Emitter::register_events
 */
 /**@{ @name accessor methods */
 
 /** Get or set the instance of the parser used by Simplewiki.
-	The parser method allows the caller to set a different instance of the parser,
+	The parser method allows the caller to set a different instance of the parser, 
 	or to obtain the current instance of the parser.
 	@param object $parser class Parser
 	@return object, class Parser
 		return $this->_parser;
 	}
 /** Get or set the instance of the emitter used by Simplewiki.
-	The emitter method allows the caller to set a different instance of the emitter,
+	The emitter method allows the caller to set a different instance of the emitter, 
 	or to obtain the current instance of the emitter.
 	@param $emitter object, optional, class Emitter
 	@sa Emitter
 /**@}*/
 
 	#-----------------------------[ STANDARD CLASS CALLBACKS ]-----------------------------#
-
+	
 /**@{ @name standard class callbacks
 
 Standard class callbacks are registered in the constructor (__construct) with register_class_callbacks
 		$count = $footnote->id = ++$footnotes->count;
 		// generate markup for link
 		$parser = $this->_working_parser;
-		$markup =
+		$markup = 
 			'%s superscript%[[#footnotemarker'
 			. $count
 			. ']][[#footnote'
 		return $node;
 	}
 /**@}*/
-
+	
 	#-----------------------------[ DEFAULT MACROS ]----------------------------------#
 /**@{@name standard macros */
 /** quicktoc macro.
 		# set data for content line items
 		$contentheadings = array();
 		$count = 0;
-		$headings = array();
 		foreach ($contents as $heading)
 		{
 			// assign id
 			$count++;
-			$heading_text = $this->_emitter->emit_node_text($heading);
-			$heading_text = str_replace(' ', '_', $heading_text);
-			if(!in_array($heading_text, $headings)) {
-				$headings[] = $heading_text;
-				$sessionid = $heading_text;
-			} else {
-				$sessionid = $heading_text . '_' . uniqid();
-			}
+			$sessionid = 'heading' . $count;
 			$headingid = @$heading->decoration->attributes['id'];
 			if (is_null($headingid))
 			{
 			$contentdepthstack = array();
 			$flooroffset = 0; // lowest depth, controlled by nesting
 			$flooroffsetstack = array();
-			// make sure there is no change for first item
+			// make sure there is no change for first item 
 			//	- markup requires starting depth of 1
 			$previouslevel = $contentheadings[0]->level;
 			$previouslevelstack = array();
-			$previousnestinglevel = $contentheadings[0]->nesting;
+			$previousnestinglevel = $contentheadings[0]->nesting; 
 			// process collected elements
 			foreach ($contentheadings as $contentheading)
 			{
 						$previouslevel = array_pop($previouslevelstack);
 					}
 				}
-				if ($level > $previouslevel)
+				if ($level > $previouslevel) 
 					$contentdepth++;
-				elseif ($level < $previouslevel)
+				elseif ($level < $previouslevel) 
 					$contentdepth--;
 				$contentdepth = min($level,$contentdepth);
 				$contentdepth = max($contentdepth,1);
 				$previousnestinglevel = $nestinglevel;
 				$previouscontentdepth = $contentdepth;
 				// generate markup
-				$markup .=
-					str_repeat('*',$contentdepth + $flooroffset)
+				$markup .= 
+					str_repeat('*',$contentdepth + $flooroffset) 
 					. '[[#'
-					. $contentheading->id
-					. '|'
+					. $contentheading->id 
+					. '|' 
 					. $contentheading->text
 					. "]]\n";
 			}
 			// enclose markup
 			$caption = preg_replace('/\\n/','',$caption); // to allow \\ (newline) markup in caption
-			$markup =
+			$markup = 
 "(:div id=quicktoc-platform:)\n
 	(:div id=quicktoc-header:)\n
 		|:p div id=quicktoc-caption quicktoc-closed:|%c html%{{{" . $caption . "}}}\n
 	(:divend:)\n
-	(:div id=quicktoc-body:)\n"
+	(:div id=quicktoc-body:)\n" 
 		. $markup . "\n
 	(:divend:)\n
 (:divend:)\n";
 		return $contents;
 	}
 	#----------------------[ NATIVE EVENT CALLBACKS ]--------------------------#
-/**@{ @name standard event methods */
+/**@{ @name standard event methods */	
 	# triggered at onafteremit event...
 	public function render_footnotes($document)
 	{
 					foreach ($references as $reference)
 					{
 						$ref = $reference->id;
-						$markup .=
-							' [[#footnotemarker'
-							. $ref
-							. '|^]]['
-							. $ref
-							.'][[#footnote'
-							. $ref
+						$markup .= 
+							' [[#footnotemarker' 
+							. $ref 
+							. '|^]][' 
+							. $ref 
+							.'][[#footnote' 
+							. $ref 
 							. ']]';
 						$reference->rendered=true;
 					}
 				. "}}}\n";
 		}
 		// wrap footnote block
-		$markup =
+		$markup = 
 			"\n|:b divider:|\n----\n"
 			. "(:div footnoteblock:)\n======Footnotes:======\n"
 			. $markup

File protected/modules/wiki/vendors/simplewiki1_1/muster/Simplewiki/Emitter.php

 	/// array($objectref,'methodname')
 	protected $_blockdef_handler;
 	/**@}*/
-/**@{ @name Creation */
+/**@{ @name Creation */	
 	/** The constructor.
 		Sets regex rules, and combines them into applied regex.
 		No parameters.
 		return $this->emit_node($dom);
 	}
 /** controller directs flow to one of the node emit methods.
-	@param object $node
+	@param object $node 
 	@return html
 */
 	protected function emit_node($node) // controller
 		$this->_rawlink_handler = $handler;
 	}
 /** [$nodetype][$classname]=>$methodref. Typically called by SimpleWiki (as facade).
- One callback per type class.
+ One callback per type class. 
 */
-	public function register_class_callouts($callouts)
+	public function register_class_callouts($callouts) 
 	{
 		// ['nodetype']['class']=>$methodref
 		$calloutlist = $this->_class_callouts;
 		$this->_class_callouts = $calloutlist;
 	}
 /** [$nodetype][$propertyname]=>$methodref. Typically called by SimpleWiki (as facade).
- One callback per type class.
+ One callback per type class. 
 */
-	public function register_property_callouts($callouts)
+	public function register_property_callouts($callouts) 
 	{
 		// ['nodetype']['property']=>$methodref
 		$calloutlist = $this->_property_callouts;
 		}
 		$this->_property_callouts = $calloutlist;
 	}
-/** [$macroname]=>$methodref. Typically called by SimpleWiki (as facade).
+/** [$macroname]=>$methodref. Typically called by SimpleWiki (as facade). 
 */
 	public function register_macro_callouts($callouts)
 	{
 	protected function call_classes($node)
 	{
 		$callbacks = isset($this->_class_callouts[$node->type])?$this->_class_callouts[$node->type]:NULL;
-		if (!empty($callbacks))
+		if (!empty($callbacks)) 
 		{
 			$classes = !empty($node->decoration->classes)?$node->decoration->classes:NULL;
 			if (!empty($classes))
 	protected function call_properties($node)
 	{
 		$callbacks = isset($this->_property_callouts[$node->type])?$this->_property_callouts[$node->type]:NULL;
-		if (!empty($callbacks))
+		if (!empty($callbacks)) 
 		{
 			$properties = !empty($node->decoration->properties)?$node->decoration->properties:NULL;
 			if (!empty($properties))
 		# symlink for src
 		if (!empty($node->linkparts->symlink)) {
 			$node = $this->expand_symlink($node);
-			$node->decoration->attributes['src'] =
+			$node->decoration->attributes['src'] = 
 				$node->linkparts->symlinkpath . $node->linkparts->internalselector;
 			$node->decoration->attributedelimiters['src'] = '"';
 		}
 		# external address for src
 		else {
-			$node->decoration->attributes['src'] =
+			$node->decoration->attributes['src'] = 
 				!empty($node->linkparts->externaladdress)?$node->linkparts->externaladdress:NULL;
 			$node->decoration->attributedelimiters['src'] = '"';
 		}
 			$node->decoration->attributedelimiters['alt'] = '"';
 		}
 		// ... target.
-		elseif ($node->target)
+		elseif ($node->target) 
 		{
 			$node->decoration->attributes['alt'] = substr($this->char_filter(strip_tags($node->target),$node),0,60) . '...';
 			$node->decoration->attributedelimiters['alt'] = '"';
 		$node = $this->prepare_node($node);
 		return $node;
 	}
-	/** identify anchor for special handling;
+	/** identify anchor for special handling; 
 		prepare attributes - name, href, title.
 		Then call standard prepare_node
 		@param object $node
 		}
 		elseif (!empty($node->linkparts->symlink)) {
 			$node = $this->expand_symlink($node);
-			$node->decoration->attributes[$attributename] =
+			$node->decoration->attributes[$attributename] = 
 				$node->linkparts->symlinkpath . (!empty($node->linkparts->internalselector)?$node->linkparts->internalselector:'');
 			$node->decoration->attributedelimiters[$attributename] = '"';
 		}
 		elseif (!empty($node->linkparts->externaladdress)) {
-			$node->decoration->attributes[$attributename] =
+			$node->decoration->attributes[$attributename] = 
 				!empty($node->linkparts->externaladdress)?$node->linkparts->externaladdress:NULL;
 			$node->decoration->attributedelimiters[$attributename] = '"';
 		}
 		else {
 			$node = $this->expand_rawlink($node);
-			$node->decoration->attributes[$attributename] =
+			$node->decoration->attributes[$attributename] = 
 				!empty($node->linkparts->rawlinkaddress)?$node->linkparts->rawlinkaddress:NULL;
 			$node->decoration->attributedelimiters[$attributename] = '"';
 		}
 		}
 		return $node;
 	}
-	/** Standard data preparation for emitting html.
-		trigger class and property callouts;
+	/** Standard data preparation for emitting html. 
+		trigger class and property callouts; 
 		prepare attributes, classes, and styles for HTML
 		by combining into single attribute array
 		@param object $node document node
 	*/
 	protected function standard_assembly($node)
 	{
-		return $node->opentag_head . implode(' ',$node->attributes) . $node->opentag_tail
+		return $node->opentag_head . implode(' ',$node->attributes) . $node->opentag_tail 
 			. $node->elementcontent . $node->closetag;
 	}
 	/**
 			isset($matches['external_mailproto']) 		or ($matches['external_mailproto'] = '');
 			isset($matches['external_mailselector']) 	or ($matches['external_mailselector'] = '');
 			isset($matches['rawlink']) 					or ($matches['rawlink'] = '');
-
+			
 			$node->linkparts = new StdClass;
 			$node->linkparts->anchor = $matches['anchor'];
-
+			
 			$node->linkparts->internaladdress = $matches['internal_address'];
 			$node->linkparts->symlink = $matches['symlink'];
 			$node->linkparts->internalselector = $matches['internal_selector'];
 			$node->linkparts->internalversion = $matches['internal_version'];
-
+			
 			$node->linkparts->externaladdress = $matches['external_address'];
 			$node->linkparts->externalprotocol = $matches['external_proto'];
 			$node->linkparts->externalselector = $matches['external_selector'];
-
+			
 			if ($matches['external_mailaddress']) {
 				$node->linkparts->externaladdress = $matches['external_mailaddress'];
 				$node->linkparts->externalprotocol = $matches['external_mailproto'];
 				$node->linkparts->externalselector = $matches['external_mailselector'];
 			}
-
+			
 			$node->linkparts->rawlink = $matches['rawlink'];
 		}
-		if (empty($node->caption) and empty($node->linkparts->externalprotocol)) {
+		if (empty($node->caption) and empty($node->linkparts->anchor)) {
 			if ($matches['external_mailaddress']) {
 				$node->caption = $node->linkparts->externalselector;
 			} else {
 			}
 		}
 
+		$node->opentag_head = "<a";
+		$node->opentag_tail = ">";
+		$node->elementcontent = $node->caption;
+		$node->closetag = "</a>";
 		$node->unknown = FALSE;
 		$node = $this->prepare_link_node($node); // might set $node->unknown = TRUE;
-		if ($node->unknown === TRUE) {
-			$node->opentag_head = "[";
-			$node->opentag_tail = "[";
-			$node->elementcontent = $node->caption;
-			$node->closetag = "]]";
-			return $node->opentag_head . $node->opentag_tail
-			. $node->elementcontent . $node->closetag;
-		} else {
-			$node->opentag_head = "<a ";
-			$node->opentag_tail = ">";
-			$node->elementcontent = $node->caption;
-			$node->closetag = "</a>";
+		if ($node->unknown) {
+			$node->opentag_head .= 'rel="nofollow" ';
+			$node->opentag_tail .= '<span style="border-bottom:1px dashed gray">';
+			$node->closetag = '</span><sup>?</sup>' . $node->closetag;
 		}
 		return $this->standard_assembly($node);
 	}
 			isset($matches['external_proto']) 		or ($matches['external_proto'] = '');
 			isset($matches['external_selector']) 	or ($matches['external_selector'] = '');
 			isset($matches['rawlink']) 				or ($matches['rawlink'] = '');
-
+			
 			$node->linkparts = new StdClass;
 			$node->linkparts->internaladdress = $matches['internal_address'];
 			$node->linkparts->symlink = $matches['symlink'];
 			$node->linkparts->internalselector = $matches['internal_selector'];
 			$node->linkparts->internalversion = $matches['internal_version'];
-
+			
 			$node->linkparts->externaladdress = $matches['external_address'];
 			$node->linkparts->externalprotocol = $matches['external_proto'];
 			$node->linkparts->externalselector = $matches['external_selector'];
-
+			
 			$node->linkparts->rawlink = $matches['rawlink'];
 		}
 		$node->opentag_head = "<img";
 		$node->opentag_tail = "/>";
-		$node = $this->prepare_image_node($node);
+		$node = $this->prepare_image_node($node); 
 		return $node->opentag_head . implode(' ',$node->attributes) . $node->opentag_tail;
 	}
 	#--------------------------------[ lists ]-------------------------------------#
 		$node->closetag = "\n</ol>";
 		$node = $this->prepare_node($node);
 		return $this->standard_assembly($node);
-//		return $node->opentag_head . implode(' ',$node->attributes) . $node->opentag_tail
+//		return $node->opentag_head . implode(' ',$node->attributes) . $node->opentag_tail 
 //			. $node->elementcontent . $node->closetag;
 	}
 /**	emit html for unordered list.
 			$node = call_user_func($this->_blockdef_handler,$node);
 		if (!$node->elementcontent) $node->elementcontent = $this->emit_children($node);
 		if ($node->knowntag)
-		{
+		{	
 			$node = $this->prepare_node($node);
 			return $this->standard_assembly($node);
 		}
 		else
 		{
-			return $node->opentag_head . $node->opentag_tail
+			return $node->opentag_head . $node->opentag_tail 
 				. $node->elementcontent . $node->closetag;
 		}
 	}
 			$arguments = property_exists($node,"decoration")? $node->decoration->markup: '';
 			if ($arguments != '') $arguments = ' ' . $arguments;
 			$caption = $node->caption;
-			if ($caption)
+			if ($caption) 
 				$caption = '|' . $caption;
 			$closetag = '>>';
 			return $this->char_filter($opentag_head . $arguments .  $caption . $closetag,$node);

File protected/modules/wiki/views/default/view.php

 
 		<?php echo CHtml::link(Yii::t('wiki', 'Page Index'), array('pageIndex'))?>
 	</div>
-</div>
+</div>