Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added option to include backlinking pages #247

Open
wants to merge 6 commits into
base: master
Choose a base branch
from

Conversation

andjar
Copy link

@andjar andjar commented Apr 24, 2020

Hi,

I have tried to add an option so that you can use

{{backlinks>[pagename]#[includetag]&[flags]}}

in addition to tagtopic etc. It will include the pages linking to [pagename]. Only pages tagged with the provided [includetag] will be included. It only looks in the namespace of [pagename].

@michitux
Copy link
Member

Some first comments:

  • Why do you remove the exclude feature? Please keep the exclude feature!
  • This "WRAP" feature should be a flag, not a separate mode as it makes (probably) sense to combine this with all other mode.
  • Why do you combine backlinks with tags? I would rather imagine a separate exclude_tags flag that takes a list of tags to exclude. Note that recursive includes are no problem. The include plugin detects this, i.e., when page A includes B and B includes A, A will only include B and B will only include A.
  • Backlinks are highly problematic because including pages creates backlinks. I.e., image we have pages A and B, both have an include statement for backlinks. Then you add a link to B from A. Now B includes A. This means that now B references A (includes count as links, too). Thus, A now also includes B. If you remove the link from B to A, I think both includes will remain as both includes still count as links. Further, imagine A also links to C and C also includes backlinks. Then suddenly B will also link to C due to B including A and thus C will also include B. I understand that this feature might be useful, but I think this requires some more thought. Could you maybe describe what your particular use case is? What I was thinking about is Roam which - from what I can see on the screenshots, I have not used it myself - includes all referencing pages below the current page and this certainly won't work as intended with this feature but probably requires a different approach.
  • The syntax {{backlinks>.+?}} is already taken by the backlinks plugin, I definitely won't add this conflicting syntax to the include plugin, this needs to be renamed e.g. to include_backlinks.

@andjar
Copy link
Author

andjar commented Apr 25, 2020

Thanks for your comments, @michitux! They are a nice starting point for further development

  • Pasted the code from an old version without exclude, will fix!
  • I agree with you. Will remove the WRAP feature for now.
  • Will use "blinks" instead

To the most complicated point; I chose to use "inclusion" tags to try to avoid some of the problems you are mentioning. As long as you only include backlinks across tagspace I think you should be fine; i.e. if pages a, b, c, d are tagged "small" and A, B, C, D are tagged "capital", one should only include backlinks to A tagged with "small" and so on. It is far from perfect, but I too had roam in my thoughts; demo https://www.youtube.com/watch?v=7JOgkxssXks

@michitux
Copy link
Member

Thank you for that demo, that made things a bit clearer and looks super cool. Two thoughts:

  • Maybe it makes more sense (for your use case) to realize these backlinks via the template or an action plugin and not via the include plugin or only using the include plugin to actually render the HTML.
  • Instead of using tags for the exclusion it should be relatively straightforward to use the new exclude feature where you can specify a regular expression that specifies which pages should not be included.
  • You can use an action plugin to override the "page not found" content in such a way that you would not need to create the topic page to get the default content. I always wanted to do that for the tag plugin but I have not found the time to do this.

@michitux
Copy link
Member

Another note: Having a closer look at Roam I noticed that my tagextract plugin prototype might be another missing piece for implementing Roam in DokuWiki. Basically it allows you to tag individual list items and then on a separate page include all items with a certain tag. During inclusion, these tags are transformed into links back to the original list item. Probably this not a perfect fit but it should be a good demo how this could be implemented. I think it should be possible to implement something similar with regular links, i.e., that list items (or paragraphs) with links to a certain page are collected together. If you are interested in this feel free to contact me. I won't have much time till next weekend, but afterwards I might have some time occasionally.

@andjar
Copy link
Author

andjar commented May 3, 2020

Hi, sorry for the late answer, I'll answer you in depth later. I have made a prototype of a plugin autocreating missing pages:

  1. Install the autostartpage plugin
  2. Create your _autostartpage.txt pages
  3. Edit action.php in the autostartpage plugin to:
<?php
/**
 * DokuWiki Plugin autostartpage (Action Component)
 *
 * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html
 * @author  Charles Knight <[email protected]>
 */
 
// must be run within Dokuwiki
if(!defined('DOKU_INC')) die();

class action_plugin_autostartpage extends DokuWiki_Action_Plugin {

    /**
     * Registers a callback function for a given event
     *
     * @param Doku_Event_Handler $controller DokuWiki's event controller object
     * @return void
     */
    public function register(Doku_Event_Handler &$controller) {
       $controller->register_hook('IO_WIKIPAGE_WRITE', 'AFTER', $this, 'autostartpage_handle');
    }

    /**
     * [Custom event handler which performs action]
     *
     * @author Charles Knight, [email protected]
     * @param Doku_Event $event  event object by reference
     * @param mixed      $param  [the parameters passed as fifth argument to register_hook() when this
     *                           handler was registered]
     * @return void
     */

    public function autostartpage_handle(Doku_Event &$event, $param) {
        global $conf;
        global $INFO;
        $data = $event->data;
        $id=$data[1].":".$data[2];
        $file=wikiFN($id);
        
        global $conf;

        if (!defined('LINK_PATTERN')) define('LINK_PATTERN', '%\[\[([^\]|#]*)(#[^\]|]*)?\|?([^\]]*)]]%');

        if(!preg_match("/.*\.txt$/", $file)) {
            return;
        }

        $currentID = pathID($file);
        $currentNS = getNS($currentID);

        if($conf['allowdebug']) echo sprintf("<p><b>%s</b>: %s</p>\n", $file, $currentID);

        // echo "  <!-- checking file: $file -->\n";
        $body = io_readFile($file);
        
        // ignores entries in blocks that ignore links
        foreach( array(
                  '@<nowiki>.*?<\/nowiki>@su',
                  '@%%.*?%%@su',
                  '@<php>.*?</php>@su',
                  '@<PHP>.*?</PHP>@su',
                  '@<html>.*?</html>@su',
                  '@<HTML>.*?</HTML>@su',
                  '@^( {2,}|\t)[^\*\- ].*?$@mu',
                  '@<code[^>]*?>.*?<\/code>@su',
                  '@<file[^>]*?>.*?<\/file>@su'
        )
        as $ignored )
        {
            $body = preg_replace($ignored, '',  $body);
        }

        $links = array();
        $missinglinks = array();
        preg_match_all( LINK_PATTERN, $body, $links );
        $cc = 1;
        foreach($links[1] as $link) {
            if($conf['allowdebug']) echo sprintf("--- Checking %s<br />\n", $link);

            if( (0 < strlen(ltrim($link)))
            and ! preg_match('/^[a-zA-Z0-9\.]+>{1}.*$/u',$link) // Interwiki
            and ! preg_match('/^\\\\\\\\[\w.:?\-;,]+?\\\\/u',$link) // Windows Share
            and ! preg_match('#^([a-z0-9\-\.+]+?)://#i',$link) // external link (accepts all protocols)
            and ! preg_match('<'.PREG_PATTERN_VALID_EMAIL.'>',$link) // E-Mail (pattern above is defined in inc/mail.php)
            and ! preg_match('!^#.+!',$link) // inside page link (html anchor)
            ) {
                # remove parameters
                $link = preg_replace('/\?.*/', '', $link);

                $pageExists = false;
                resolve_pageid($data[1], $link, $pageExists );
                resolve_pageid($currentNS, $link, $pageExists );
                if ($conf['allowdebug']) echo sprintf("---- link='%s' %s ", $link, $pageExists?'EXISTS':'MISS');

                if(((strlen(ltrim($link)) > 0)           // there IS an id?
                and !auth_quickaclcheck($link) < AUTH_READ)) {
                    // should be visible to user
                    //echo "      <!-- adding $link -->\n";

                    if($conf['allowdebug']) echo ' A_LINK' ;

                    $link= utf8_strtolower( $link );
                }
                else
                {
                    if($conf['allowdebug']) echo ' EMPTY_OR_FORBIDDEN' ;
                }
            } // link is not empty and is a local link?
            else {
                if($conf['allowdebug']) echo ' NOT_INTERNAL';
            }

            if($conf['allowdebug']) echo "<br />\n";
            
            if($pageExists == FALSE){
                $missinglinks[$cc] = $link;
                $cc++;
            }
            
        }
        $cc = 1;
        foreach($missinglinks as $missinglink){
            
            $templatefile = wikiFN($this->getConf('templatefile'), '', false);
            if(@file_exists($templatefile)){
                $wikitext=io_readFile($templatefile);
            }
            
            $id = $missinglink;
            $ns = getNS($id);
            $page = noNS($id);
            
            $silent=$this->getConf('silent');
            $ns_sepchar = ":";

            $parent=implode($ns_sepchar, array_splice(preg_split("/".preg_quote($ns_sepchar, "/")."/", $ns), 0, -1));
            $goodns=preg_replace("/".$conf['sepchar']."/"," ",noNS($ns));
            $page=preg_replace("/".$conf['sepchar']."/"," ",noNS($id));
            $f=$conf['start'];

            /**THESE ARE THE CODES FOR TEMPLATES**/
            // @ID@         full ID of the page
            // @NS@         namespace of the page
            // @PAGE@       page name (ID without namespace and underscores replaced by spaces)
            // @!PAGE@      same as above but with the first character uppercased
            // @!!PAGE@     same as above but with the first character of all words uppercased
            // @!PAGE!@     same as above but with all characters uppercased
            // @FILE@       page name (ID without namespace, underscores kept as is)
            // @!FILE@      same as above but with the first character uppercased
            // @!FILE!@     same as above but with all characters uppercased
            // @USER@       ID of user who is creating the page
            // @NAME@       name of user who is creating the page
            // @MAIL@       mail address of user who is creating the page
            // @DATE@       date and time when edit session started
            /**PLUS WE ADDED THESE**/
            // @!NS@        namespace of the page (with spaces) but with the first character uppercased
            // @!!NS@       namespace of the page (with spaces) but with the first character of all words uppercased
            // @!!NS!@      namespace of the page (with spaces) but with all characters uppercased
            // @PARENT@     the name of the parent namespace. Blank if parent is top
            // @DATE=STRFTIME@   Where `STRFTIME` is a strftime configure string of page creation time,
            //       e.g. %a %d-%m-%y => Thu 06-12-12
            
            $wikitext=preg_replace("/@NS@/", $ns, $wikitext);
            $wikitext=preg_replace("/@!NS@/", ucfirst($goodns), $wikitext);
            $wikitext=preg_replace("/@!!NS@/", ucwords($goodns), $wikitext);
            $wikitext=preg_replace("/@!!NS!@/", strtoupper($goodns), $wikitext);
            $wikitext=preg_replace("/@ID@/", $id, $wikitext);
            $wikitext=preg_replace("/@PAGE@/",$page, $wikitext);
            $wikitext=preg_replace("/@!PAGE@/",ucfirst($page), $wikitext);
            $wikitext=preg_replace("/@!!PAGE@/",$uupage=ucwords($page), $wikitext);
            $wikitext=preg_replace("/@!PAGE!@/",strtoupper($page), $wikitext);
            $wikitext=preg_replace("/@FILE@/",$f, $wikitext);
            $wikitext=preg_replace("/@!FILE@/",ucfirst($f), $wikitext);
            $wikitext=preg_replace("/@!FILE!@/",strtoupper($f), $wikitext);
            $wikitext=preg_replace("/@USER@/",$_SERVER['REMOTE_USER'], $wikitext);
            $wikitext=preg_replace("/@NAME@/",$INFO['userinfo']['name'], $wikitext);
            $wikitext=preg_replace("/@MAIL@/",$INFO['userinfo']['mail'], $wikitext);
            $wikitext=preg_replace("/@DATE@/",strftime("%D"), $wikitext);
            $wikitext=preg_replace("/@PARENT@/",$parent, $wikitext);
            if(preg_match("/@DATE=(.*)@/", $wikitext, $matches)){
                $wikitext=str_replace($matches[0], strftime($matches[1]), $wikitext);
            }
            
            
            saveWikiText($missinglink, $wikitext, "autostartpage", $minor = false); 
            $cc++;
        }
    }
    
}

Best,
Anders

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants