1<?php
2if( !defined( 'DOKU_INC' ) ) die();
3if( !defined( 'DOKU_PLUGIN' ) ) define( 'DOKU_PLUGIN',
4        DOKU_INC . 'lib/plugins/' );
5require_once (DOKU_PLUGIN . 'action.php');
6
7class action_plugin_crosspost extends DokuWiki_Action_Plugin
8{
9
10    function register(Doku_Event_Handler $controller)
11    {
12        $controller->register_hook( 'ACTION_ACT_PREPROCESS', 'BEFORE', $this,
13                'handle_act' );
14        $controller->register_hook( 'DOKUWIKI_STARTED', 'BEFORE', $this,
15                'check_cp_redirect' );
16        $controller->register_hook( 'HTML_EDITFORM_OUTPUT', 'BEFORE', $this,
17                'print_edit_form' );
18        if( $this->getConf( 'cp_add_links' ) )
19        {
20            $controller->register_hook( 'TPL_CONTENT_DISPLAY', 'BEFORE', $this,
21                    'add_cp_links' );
22        }
23    }
24
25    /*
26     * Save page: create and delete crossposts here
27     */
28    function handle_act(&$e, $param)
29    {
30        global $ID;
31        global $INPUT;
32        global $conf;
33
34        if( !is_object( $INPUT ) || !$INPUT->post ||
35                 !$INPUT->post->param( 'crosspost_plugin' ) ) return;
36
37        $match = $INPUT->post->param( 'crosspost_to' );
38        $match = preg_split( '/[\s,]+/', $match, -1, PREG_SPLIT_NO_EMPTY );
39
40        $meta = p_get_metadata( $ID, 'crosspost_to' );
41        $meta = preg_split( '/[\s,]+/', $meta, -1, PREG_SPLIT_NO_EMPTY );
42
43        $name = noNS( $ID );
44        $save = array();
45
46        foreach( $match as $page )
47        {
48            if( strpos( $ID, ':' ) === false ) $page .= ':' . $ID;
49            else $page .= ':' . $name;
50
51            if( auth_quickaclcheck( $page ) < AUTH_CREATE ) continue;
52
53            $exists = @file_exists( wikiFN( $page ) );
54            if( $ID != $page && !$exists )
55            {
56                saveWikiText( $page, "{{page>$ID}}", 'link from ' . $ID );
57                p_set_metadata( $page,
58                        array('crosspost_source' => $ID
59                        ) );
60            }
61            else
62            {
63                p_set_metadata( $page,
64                        array('cache' => 'expire'
65                        ), false, false );
66            }
67            if( !in_array( $page, $save ) ) $save[] = $page;
68        }
69        foreach( $meta as $page )
70        {
71            if( !in_array( $page, $match ) )
72            {
73                @unlink( wikiFN( $page ) );
74                p_set_metadata( $page,
75                        array('crosspost_source' => '','cache' => 'expire'
76                        ), false, false );
77            }
78        }
79
80        p_set_metadata( $ID,
81                array('crosspost_to' => join( ',', $save ),'cache' => 'expire'
82                ) );
83
84        $sidebar = isset( $conf['sidebar'] ) ? $conf['sidebar'] : 'sidebar';
85        if( $sidebar ) p_set_metadata( $sidebar,
86                array('cache' => 'expire'
87                ), false, false );
88    }
89
90    /*
91     * Redirect to "original" page if edited page is copy
92     */
93    function check_cp_redirect(&$e, $param)
94    {
95        global $ID;
96        global $ACT;
97
98        if( $ACT != 'edit' ) return;
99        $meta = p_get_metadata( $ID, 'crosspost_source' );
100        if( !$meta ) return;
101        if( $meta == $ID ) return;
102        send_redirect( wl( $meta ) . '?do=edit' );
103    }
104
105    private function _getTitle($mode, $entry)
106    {
107        $title = $entry;
108        $header = false;
109        if( $mode == 'title' )
110        {
111            $header = p_get_first_heading( $entry );
112        }
113        elseif( $mode == 'section' )
114        {
115            $header = p_get_first_heading( getNS( $entry ) );
116        }
117        elseif( $mode == 'last section:title' )
118        {
119            $header = p_get_first_heading( $entry );
120            if( $header )
121            {
122                $ns = p_get_first_heading( getNS( $entry ) );
123                if( $ns ) $header = $ns . ':' . $header;
124            }
125        }
126        elseif( $mode == 'full title' )
127        {
128            $header = p_get_first_heading( $entry );
129            if( $header )
130            {
131                $allns = explode( ':', $entry );
132                array_pop( $allns );
133                while( count( $allns ) )
134                {
135                    $ns = p_get_first_heading( join( ':', $allns ) );
136                    if( $ns )
137                    {
138                        $header = "$ns:$header";
139                        array_pop( $allns );
140                    }
141                    else
142                    {
143                        $header = array_pop( $allns ) . ":$header";
144                    }
145                }
146            }
147        }
148        return $header ? $header : $title;
149    }
150
151    private function _skip_ns($xns, $ns)
152    {
153        foreach( $xns as $x )
154        {
155            if( $x{0} == '!' )
156            {
157                $pos = strpos( $ns, ltrim( $x, '!' ) );
158                if( $pos !== false && $pos == 0 ) return true;
159            }
160            else
161            {
162                if( $x == $ns ) return true;
163            }
164        }
165        return false;
166    }
167
168    /*
169     * Add NS selection to edit page form
170     */
171    function print_edit_form(&$e, $param)
172    {
173        global $ID;
174        global $ACT;
175        global $conf;
176
177        if( $ACT != 'edit' ) return;
178
179        $xns = $this->getConf( 'cp_disable_on' );
180        $xns = preg_split( '/[\s,+]/', $xns, -1, PREG_SPLIT_NO_EMPTY );
181        if( $this->_skip_ns( $xns, getNS( $ID ) ) ) return;
182
183        $title_mode = $this->getConf( 'cp_form_titles' );
184        $namespaces = array();
185        search( $namespaces, $conf['datadir'], 'search_namespaces', array() );
186        $this_ns = '';
187
188        $meta = p_get_metadata( $ID, 'crosspost_to' );
189        $meta = preg_split( '/[\s,+]/', $meta, -1, PREG_SPLIT_NO_EMPTY );
190
191        for( $i = 0; $i < count( $meta ); $i++ )
192        {
193            $ns = getNS( $meta[$i] );
194            if( $meta[$i] == $ID )
195            {
196                array_splice( $meta, $i, 1 );
197                $this_ns = $ns;
198            }
199            else
200            {
201                if( auth_quickaclcheck( $ns ) >= AUTH_CREATE )
202                {
203                    $meta[$i] = $ns;
204                }
205                else
206                {
207                    array_splice( $meta, $i, 1 );
208                }
209            }
210        }
211
212        if( !$this_ns ) $this_ns = getNS( $ID );
213        $xns = $this->getConf( 'cp_ns_disabled' );
214        $xns = preg_split( '/[\s,+]/', $xns, -1, PREG_SPLIT_NO_EMPTY );
215        $links = array();
216
217        foreach( $namespaces as $ns )
218        {
219            if( $ns['id'] == $this_ns ) continue;
220            if( $this->_skip_ns( $xns, $ns['id'] ) ) continue;
221
222            $header = $this->_getTitle( $title_mode, $ns['id'] );
223            $link = '<a ';
224            $class = 'crosspost';
225            if( in_array( $ns['id'], $meta ) )
226            {
227                $class = 'crosspost_added';
228            }
229            $link .= 'href="#" onclick="' . 'return cp_link_clicked(this,&quot;' .
230                     $ns['id'] . '&quot)' . '" class="' . $class . '">' . $header .
231                     '</a>';
232            $links[] = $link;
233        }
234
235        $input = '<b>' . $this->getlang( 'cp_to' ) . '</b><br/>' .
236                 '<input type="hidden" name="crosspost_plugin" value="crosspost_plugin" />' .
237                 '<input type="text" name="crosspost_to" id="crosspost_to" value="' .
238                 join( ',', $meta ) . '" style="width:100%" class="crosspost" ' .
239                 'onkeydown="return false;" onmousedown="return false;" ' . '/>' .
240                 '<div class="crosspost_form">' . '<b>' .
241                 $this->getlang( 'cp_click_to_add' ) . '</b><br/>' .
242                 join( ' ', $links ) . '</div>';
243
244        $e->data->insertElement( 2, $input );
245    }
246
247    private function _add_link($mode, $page, $title)
248    {
249        $link_title = $this->_getTitle( $mode, $page );
250        return '<span class="crosspost"><a href="' . wl( $page ) . '" title="' .
251                 $title . '" ' . '" class = "crosspost_link">' . $link_title .
252                 '</a></span>';
253    }
254
255    function add_cp_links(&$e, $param)
256    {
257        global $ID;
258
259        $cs_source = p_get_metadata( $ID, 'crosspost_source' );
260        $cc = p_get_metadata( $cs_source ? $cs_source : $ID, 'crosspost_to' );
261        $cc = preg_split( '/[\s,+]/', $cc, -1, PREG_SPLIT_NO_EMPTY );
262        if( count( $cc ) < 1 ) return;
263        $title_mode = $this->getConf( 'cp_link_titles' );
264        $title_copy = $this->getLang( 'cp_link_copy' );
265        $title_source = $this->getLang( 'cp_link_source' );
266
267        if( !in_array( $ID, $cc ) ) $cc[] = $ID;
268        if( $cs_source && !in_array( $cs_source, $cc ) ) $cc[] = $cs_source;
269
270        $addlink = '';
271        foreach( $cc as $page )
272        {
273            if( $page != $ID )
274            {
275                $addlink .= $this->_add_link( $title_mode, $page,
276                        $page == $cs_source ? $title_source : $title_copy ) . " ";
277            }
278        }
279
280        if( $addlink ) ptln( '<div class="crosspost">' . $addlink . '</div>' );
281    }
282}
283