1<?php
2/**
3 * DokuWiki ApexCharts Plugin
4 *
5 * @license    GPL 2 (http://www.gnu.org/licenses/gpl.html)
6 * @author     Karl Nickel <kazozaagir@gmail.com>
7 */
8
9// must be run within Dokuwiki
10if (!defined('DOKU_INC')) die();
11
12class syntax_plugin_achart extends DokuWiki_Syntax_Plugin {
13    /**
14     * @return string Syntax mode type
15     */
16    public function getType() {
17        return 'substition';
18    }
19    /**
20     * @return string Paragraph type
21     */
22    public function getPType() {
23        return 'block';
24    }
25    /**
26     * @return int Sort order - Low numbers go before high numbers
27     */
28    public function getSort() {
29        return 200;
30    }
31
32    /**
33     * Connect lookup pattern to lexer.
34     *
35     * @param string $mode Parser mode
36     */
37    public function connectTo($mode) {
38        $this->Lexer->addSpecialPattern('<achart.+?</achart>',$mode,'plugin_achart');
39    }
40
41    /**
42     * Handle matches of the achart syntax
43     *
44     * @param string $match The match of the syntax
45     * @param int    $state The state of the handler
46     * @param int    $pos The position in the document
47     * @param Doku_Handler    $handler The handler
48     * @return array Data for the renderer
49     */
50    public function handle($match, $state, $pos, Doku_Handler $handler){
51        $match = substr(trim($match), 7, -10);
52        list($opts, $adata) = explode('>', $match, 2);
53        preg_match_all('/(\S+)=["\']?((?:.(?!["\']?\s+(?:\S+)=|[>"\']))+.)["\']?/', $opts, $matches, PREG_SET_ORDER);
54        $opts = array(
55            'width' => $this->getConf('width'),
56            'height' => $this->getConf('height'),
57            'align' => $this->getConf('align'),
58			'url' => null,
59        );
60        foreach($matches as $m) {
61            $opts[strtolower($m[1])] = $m[2];
62        }
63
64        $adata = preg_replace_callback(
65            '#//.*?$|/\*.*?\*/|\'(?:\\.|[^\\\'])*\'|"(?:\\.|[^\\"])*"#ms',
66            function($matches){
67                $m = $matches[0];
68                return substr($m, 0, 1)==='/' ? ' ' : $m;
69            }, $adata
70        ); // remove comments (respecting quoted strings)
71        $adata = explode("\n", $adata);
72        $adata = implode("", array_map('trim', $adata));
73        $chartid = uniqid('__achart_');
74        //$adata = base64_encode($adata);
75
76        return array($chartid, $adata, $opts);
77    }
78
79    /**
80     * Render xhtml output or metadata
81     *
82     * @param string         $mode      Renderer mode (supported modes: xhtml)
83     * @param Doku_Renderer  $renderer  The renderer
84     * @param array          $data      The data from the handler() function
85     * @return bool If rendering was successful.
86     */
87    public function render($mode, Doku_Renderer $renderer, $data) {
88        if($mode != 'xhtml') return false;
89
90        list($chartid, $adata, $opts) = $data;
91        $resize = '';
92        $align = '';
93		$file = '';
94        foreach($opts as $n => $v) {
95            if(in_array($n, array('width','height')) && $v) {
96                $resize .= $n.':'.hsc($v).';';
97            } elseif($n=='align' && in_array($v, array('left','right','center'))) {
98                $align = 'media'.$v;
99            } elseif ($n=='url' && $v) {
100				$file = $v;
101			}
102        }
103
104		if (!empty($file)) {
105			// Check for csv files
106			if($file !== '' && !preg_match('/^https?:\/\//i', $file)) {
107				$file = cleanID($file);
108				if(!strlen(getNS($file))) {
109					$file = $INFO['namespace'] . ':' . $file;
110				}
111			}
112			// Load csv files
113			if(preg_match('/^https?:\/\//i', $file)) {
114				$http = new DokuHTTPClient();
115				$content = $http->get($file);
116				try {
117					if($content === false)
118						throw new \Exception('Chart cannot be displayed ! Failed to fetch remote CSV file');
119				} catch (\Exception $e) {
120					msg(hsc($e->getMessage()), -1);
121					return false;
122				}
123			} else {
124				$fileNS = getNS($file);
125				$file = mediaFN($file);
126				try {
127					if(auth_quickaclcheck($fileNS . ':*') < AUTH_READ)
128						throw new \Exception('Chart cannot be displayed ! Access denied to CSV file');
129					if(!file_exists($file))
130						throw new \Exception('Chart cannot be displayed ! Requested local CSV file does not exist');
131				} catch (\Exception $e) {
132					msg(hsc($e->getMessage()), -1);
133					return false;
134				}
135			}
136			// If not valid UTF-8 is given we assume ISO-8859-1
137			if(!utf8_check($file)) $file = utf8_encode($file);
138		/*
139		 * Converts CSV to JSON
140		 */
141		//Read the csv and return as array
142		error_reporting(0);
143		$data = array_map('str_getcsv', file($file));
144		//Get the first raw as the key
145		$keys = array_shift($data);
146		//Add label to each value
147		$newArray = array_map(function($values) use ($keys){
148			return array_combine($keys, $values);
149		}, $data);
150
151		// Print it out as JSON
152		$csvData[]= array_combine(array('data'),array($newArray));
153		//Remove on given data in syntax{.....}
154		$newAdata=substr($adata, 1, strlen($adata) - 2);
155		$jsonData = array('series'=> $csvData,'config'=>$newAdata);
156		#unset($jsondata['config']);
157		$newJson=json_encode($jsonData,JSON_NUMERIC_CHECK);
158		$newJson = preg_replace(array('/("series")/i') , "series",$newJson);
159		$newJson = preg_replace(array('/("data")/i') , "data",$newJson);
160		$newJson = preg_replace(array('/("config":")/i') , "",$newJson);
161		$newJson = preg_replace(array('/(}")/i') , "}",$newJson);
162		//Remove escaping slashes
163		$newJson = str_replace('\\','',$newJson);
164	}
165
166		#print_r ($newJson);
167		if(!empty($file)) {$chartData = $newJson;} else {$chartData = $adata;};
168        if($align) $resize = ' style="'.$resize.'padding: 2px;border: 1px solid #eee;margin:3px 2px;"';
169        if($ClassAttr) $ClassAttr = ' class="'.$ClassAttr.'"';
170
171        $renderer->doc .= '<div id="'.$chartid.'"'.$ClassAttr.$resize.' data-achart="'.base64_encode($chartData).'"></div>'."\n";
172
173        return true;
174    }
175}
176