1<?php
2/**
3 * DokuWiki Plugin TagAdd (Action Component)
4 *
5 * @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
6 * @author lisps
7 */
8
9
10/**
11 * All DokuWiki plugins to extend the parser/rendering mechanism
12 * need to inherit from this class
13 */
14class action_plugin_tagadd extends DokuWiki_Action_Plugin
15{
16
17    public function register(Doku_Event_Handler $controller) {
18        $controller->register_hook('DOKUWIKI_STARTED', 'AFTER',  $this, '_addjs');
19        if($this->getConf('showPagetoolBtn')) {
20    		$controller->register_hook('TEMPLATE_PAGETOOLS_DISPLAY', 'BEFORE', $this, '_addbutton');
21    		$controller->register_hook('MENU_ITEMS_ASSEMBLY', 'AFTER', $this, 'addsvgbutton');
22        }
23        $controller->register_hook('AJAX_CALL_UNKNOWN', 'BEFORE',  $this, '_ajax_call');
24    }
25
26    public function _addjs(Doku_Event $event, $param) {
27        global $ID;
28        global $JSINFO;
29        global $ACT;
30
31		$perm = auth_quickaclcheck($ID);
32		if ($perm > AUTH_READ)
33			$JSINFO['acl_write'] = '1';
34        $JSINFO['currentNamespace'] = (($ns = getNS($ID))?$ns:'');
35
36        if(!isset($JSINFO['act'])) {
37            $JSINFO['act'] = $ACT;
38        }
39
40        $JSINFO['tagadd_altKey'] = $this->getConf('altKey');
41        $JSINFO['tagadd_ctrlKey'] = $this->getConf('ctrlKey');
42        $JSINFO['tagadd_keyCode'] = array_map('trim',explode(',',$this->getConf('keyCode')));
43
44    }
45
46    public function _addbutton(Doku_Event $event) {
47		global $ID;
48
49        $perm = auth_quickaclcheck($ID);
50		if ($perm > AUTH_READ) {
51			$event->data['items'][] = '<li class="plugin_tagadd__addtags">' . tpl_link(wl($ID), '<span>'.$this->getLang('btn_addTagButton').'</span>',
52												'class="action tagadd" title="'.$this->getLang('btn_addTagButton').'"', 1) . '</li>';
53		}
54	}
55
56
57	public function addsvgbutton(Doku_Event $event) {
58	    if($event->data['view'] != 'page') return;
59	    $event->data['items'][] = new \dokuwiki\plugin\tagadd\MenuItem();
60	}
61
62
63	public function _ajax_call(Doku_Event $event, $param) {
64	    if ($event->data !== 'plugin_tagadd') {
65	        return;
66	    }
67	    //no other ajax call handlers needed
68	    $event->stopPropagation();
69	    $event->preventDefault();
70
71	    /* @var $INPUT \Input */
72	    global $INPUT;
73	    global $ID;
74
75	    #Variables
76	    $action = $INPUT->str('action');
77
78
79	    /* @var $Hajax \helper_plugin_ajaxedit */
80	    $Hajax = $this->loadHelper('ajaxedit');
81
82	    $Htf = $this->loadhelper('tagfilter');
83
84	    //Action Save Tags
85	    if($action === 'saveTags') {
86	        $chked_tags=array();
87	        foreach($INPUT->arr('form', array()) as $entry){
88	            if($entry['name']=='tag')
89	                $chked_tags[]=$entry['value'];
90	        }
91	        sort($chked_tags);
92
93	        $this->editWiki($chked_tags);
94
95	        $Htag= $this->loadHelper('tag');
96
97	        $links=$Htag->tagLinks($chked_tags);
98	        p_set_metadata($ID,array('subject'=>$chked_tags));
99	        if(!$Htf->isNewTagVersion()) {
100	            $Htag->_updateTagIndex($ID,$chked_tags);
101	        }
102
103	        $Hajax->success(array('links'=>$links));
104
105	    }
106
107	    //Action LoadForm
108	    if($action === 'loadForm') {
109	        global $INPUT;
110	        $ns = $INPUT->str('ns');
111
112	        $form = $this->createForm($ID, $ns);
113	        $Hajax->success(['form' => $form]);
114	    }
115	}
116
117	/*
118	 * Returns html breadcrumbs string for namspace selection
119	 * @param string $ID Id of the site
120	 * @return string html string
121	 */
122	protected function createNsSelector($ID, $ns_selected){
123	    //root namespace selector
124	    if($ns_selected === '' ) {
125	        $output="<a href='' onclick='tagadd__loadForm(\"\");return false;'><b>[:]</b></a>->";
126	    } else {
127	        $output="<a href='' onclick='tagadd__loadForm(\"\");return false;'>[:]</a>->";
128	    }
129	    $ns=explode(':',$ID);
130        $anz_ns=count($ns);
131        $root = '';
132
133        foreach($ns as $key => $part){
134            //this is the site name
135            if($key+1==$anz_ns) {
136                $output.=$part;
137            } else {
138                $root .=$part.':';
139
140                if($root == $ns_selected ) {
141                    $output.="<a href='' onclick='tagadd__loadForm(\"".$root."\");return false;'><b>[".$part."]</b></a>->";
142                } else {
143                    $output.="<a href='' onclick='tagadd__loadForm(\"".$root."\");return false;'>[".$part."]</a>->";
144                }
145            }
146        }
147        return $output;
148	}
149
150
151	/**
152	 * Returns html string with the accordion to select the tags
153	 * loads the tags from the given site and the tags of the given namespace
154	 *
155	 * @param string $ID site id
156	 * @param string $ns namespace from which the tags should be loaded
157	 */
158	protected function createForm($ID,$ns)
159	{
160
161	    $Htagfilter= $this->loadHelper('tagfilter');
162	    $siteTags = $Htagfilter->getTagsBySiteID($ID);
163	    $nsTags   = $Htagfilter->getTagsByNamespace(trim($ns,':'));
164
165	    sort($siteTags);
166	    sort($nsTags);
167	    //print_r($nsTags);
168	    //workaround for empty entries in the arrays
169	    $siteTags=array_filter($siteTags);
170	    $nsTags=array_filter($nsTags);
171	    /*
172	     echo '<pre>';
173	     //print_r($nsTags);
174	     print_r($siteTags);
175	     echo '</pre>';
176	     */
177	    $html =  $this->createNsSelector($ID, $ns);
178
179	    if(count($nsTags)<1) return $html."<br><br>no Tags found";
180	    $form = new Doku_Form('tagadd__form');
181	    $form->_content[]='<div id="tagadd__accordion" height="800px">';
182
183	    $this->createAccordion($form,$nsTags,$siteTags);
184
185	    $html .= $form->getForm();
186	    return $html;
187	}
188
189	/**
190	 * Categorysize Tags by the first part before a ':'
191	 * @param array $tags Array of tags
192	 * <pre>
193	 * array('category1:tag1','category1:tag2','category2:tag1','category2:tag2')
194	 * </pre>
195	 * @returns array multidimensional array
196	 * <pre>
197	 * [category1] => 'category1:tag1'
198	 *             => 'category1:tag2'
199	 * [category2] => 'category2:tag1'
200	 *             => 'category2:tag2'
201	 * </pre>
202	 */
203	protected function categorysizeTags($tags)
204	{
205	    $catTags = array();
206	    foreach($tags as $nsTag){
207	        $category=substr($nsTag,0,strpos($nsTag,':'));
208	        $catTags[$category][]=$nsTag;
209	    }
210	    ksort($catTags);
211	    return $catTags;
212	}
213
214	/**
215	 * creates the accordion with the checkbox fields
216	 * @param Doku_Form $form doku form instance
217	 * @param array $nsTags Selectable tags
218	 * @param array $siteTags Checked tags
219	 * @return string html code
220	 */
221	protected function createAccordion($form,$nsTags,$siteTags)
222	{
223	    $nsTags_cat = $this->categorysizeTags($nsTags);
224	    $siteTags_cat = $this->categorysizeTags($siteTags);
225
226	    foreach($nsTags_cat as $category=>$tags) {
227	        $catTagsCount = array_key_exists($category, $siteTags_cat) ? count($siteTags_cat[$category]) : '0';
228	        $form->_content[]='<h3><a href="#">'.$category.' ('.$catTagsCount.'/'.count($tags).')</a></h3><div>';
229	        foreach($tags as $tag){
230	            $chk_attrs=array();
231
232	            if(in_array($tag,$siteTags)){
233	                $chk_attrs['checked']='checked';
234	            }
235	            $form->addElement(form_makeCheckboxField('tag', $tag, $tag, 'ad_'.$tag, 'tagadd', $chk_attrs));
236
237	        }
238	        $form->_content[]='</div>';
239	    }
240	    $form->_content[]='</div>';
241	}
242
243	/**
244	 * save the tags to the raw wiki page
245	 * @tags array tags
246	 */
247	protected function editWiki($tags) {
248	    $Hajax = $this->loadHelper('ajaxedit');
249	    $idcount = 0;
250
251	    $data=$Hajax->getWikiPage();
252	    //find "our" fsinput fields
253	    $found=explode("{{tag>",$data);
254
255	    if ($idcount < count($found) && count($found)>1) {
256
257	        $found[$idcount+1] = ltrim($found[$idcount+1]);
258	        $stop=strpos($found[$idcount+1],"}}");
259	        if ($stop === FALSE) {
260	            $Hajax->error('cant find object');
261	        }
262	        else {
263	            $oldstr = substr($found[$idcount+1],0,$stop);
264	            $newstr=implode(" ",$tags);
265	            if($stop == 0)
266	                $found[$idcount+1]= " ".$newstr." ".$found[$idcount+1];
267	                else
268	                    $found[$idcount+1]=str_replace($oldstr," ".$newstr." ",$found[$idcount+1]);
269	        }
270	        //create new pagesource
271	        $data=implode("{{tag>",$found);
272
273	        //get removed and added tags
274	        $oldtags_r=explode(" ",$oldstr);
275	        $oldtags_r = array_filter($oldtags_r);
276	        $diff =array_diff($oldtags_r,$tags);
277	        $rem = array_intersect($oldtags_r,$diff);
278	        $diff =array_diff($tags,$oldtags_r);
279	        $add = array_intersect($tags,$diff);
280	    } else if(!empty($tags)){
281	        $text = '{{tag>' . implode(" ",$tags) .'}}';
282	        $data.=DOKU_LF.$text;
283
284	        $rem = array();
285	        $add = $tags;
286	    }
287	    else{
288	        $Hajax->error('no tags selected');
289	    }
290
291
292	    $log = "";
293	    if(!empty($add)) {
294	        $log.=' Added: '.implode(",",$add);
295	    }
296	    if(!empty($rem)) {
297            $log.=' Removed: '.implode(",",$rem);
298	    }
299
300        $summary= "Tag".$idcount." ".$log;
301        $Hajax->saveWikiPage($data,$summary,true, [],false);
302
303	}
304
305
306}
307
308