Tagging URL --> Summary Page

plugin
tags

#1

I Like the idea of having summary pages like for categories or a tag clouds, etc.
What about transfering this idea to weblinks used on the homepage?

Maybe Links can have a tag for categorizing them. something like

(link: http://foo.bar tag: Blog text: Foobars Blogsite)
(link: http://foo.foo tag: Blog text: Foofoos Blogsite)
(link: http://bar.foo tag: Shop text: Barfoos Shopsite)
(link: http://foo.foo tag: Forum text: Foofoos Webforum)

Then, a plugin catches all of those links and sort them by tags and alphabet, etc… on a new subpage.

Blogs

Foobars Blogsite
Foofoos Blogsite

Forums

Foofoos Webforum

Shops

Barfoos Shopsite

What are your thoughts?


#2

i was able to add a cat (for category) attribute to the link-tag via site/tags/link.php

<?php

// link tag
kirbytext::$tags['link'] = array(
  'attr' => array(
    'text',
    'class',
    'role',
    'title',
    'rel',
    'lang',
    'target',
    'popup',
    'cat'
  ),
  'html' => function($tag) {

    $link = url($tag->attr('link'), $tag->attr('lang'));
    $text = $tag->attr('text');

    if(empty($text)) {
      $text = $link;
    }

    if(str::isURL($text)) {
      $text = url::short($text);
    }

    return html::a($link, $text, array(
      'rel'    => $tag->attr('rel'),
      'class'  => $tag->attr('class'),
      'role'   => $tag->attr('role'),
      'title'  => $tag->attr('title'),
      'target' => $tag->target(),
    ));

  }
);

Now im searching for a solution to fetch the markdown from pages like this (link: http://foo.foo cat: Blog text: Foofoos Blogsite) and put them into an array sorted by attributes, like:

$array = [
    "link" => "http://foo.foo",
    "cat"  => "Blog",
    "text" => "Foofoos Blogsite",
];

Any ideas/hints on that?


#3

This can be done with some regex voodoo on the raw content:

$rawContent = $page->content()->raw();

#4

thx. but i think the most problematic part (for me) would be the regex-voodoo =)


#5

I can recommend for example https://regex101.com. Create your pattern and then use preg_match_all()to get the different capture groups.


#6

i’ll take a look on that… thx a lot.

What about the included parsing functions? cause there is already a working parsing inside kirby, is it possible to use it for my idea?


#7

Well, you don’t want to parse the tags, you want to find them and do something with the result, don’t you?


#8

you are right. the point of my thought was, to use the regex of the parsing feature or something like that.


#9

I don’t know, you would have to look if the kirbytext or kirbytag classes contain anything that you can reuse. Or rather, get the link tags and then try to use the tag class to further process it. Haven’t looked into that more closely yet. The pattern to get the link tags is pretty simple.


#10

I’ve looked a bit closer, here’s a page method:

You can pass a field as parameter. If you don’t, the raw content is used.

page::$methods['getTaggedLinklist'] = function($page, $field = null) {
  if(!is_null($field) && in_array($field, $page->content()->fields())) {
    $raw = $page->{$field}();
  } else {
    $raw = $page->content()->raw();
  }
  // find all tags
  preg_match_all('!(?=[^\]])\([a-z0-9_-]+:.*?\)!', $raw, $matches);
  $list = [];
  if(count($matches[0]) > 0) {
    // loop through the matches
    foreach($matches[0] as $match) {
      // remove the brackets
      $tag  = trim(rtrim(ltrim($match, '('), ')'));
      // get the name of the tag
      $name = trim(substr($tag, 0, strpos($tag, ':')));
      // check if it is a link tag
      if($name === 'link') {
        // create a new Kirbytag instance
        $tag = new Kirbytag(null, $name, $tag);
        // make sure the tag attr is present
        if(!is_null($tag->attr('tag'))) {
          // get all attributes we need from the tag and add to list array
           $list[] = [
             'link' => $tag->attr('link'),
             'tag' => $tag->attr('tag'),
             'text' => $tag->attr('text')
           ];
        }
      }
    }
  }
  return $list;

};

dump($page->getTaggedLinklist());

Note: this method requires a modified link kirbytag with a tag attribute. However, similar code can be quite useful for other use cases as well. You could also make this a pages method (with some modifications) or use the code inside.


Add JS src to <head> only if custom tag is in use
#11

wow… thx a lot!
i also took a look into the regex and found this for the first instance https://regex101.com/r/uhjrYR/1/ (after hours of playing with regex)
this regex also excludes code snippets that are inside backticks.

wouldn’t it be easier and/or more compatible to store all attributes into the list-array, regardless of content and/or name?

again: thx a lot!


#12

That’s not difficult, $tag->attr() gives you an array of all attributes:

page::$methods['getTaggedLinklist'] = function($page, $field = null) {
  if(!is_null($field) && in_array($field, $page->content()->fields())) {
    $raw = $page->{$field}();
  } else {
    $raw = $page->content()->raw();
  }
  // find all tags
  preg_match_all('!(?=[^\]])\([a-z0-9_-]+:.*?\)!', $raw, $matches);
  $list = [];
  if(count($matches[0]) > 0) {
    // loop through the matches
    foreach($matches[0] as $match) {
      // remove the brackets
      $tag  = trim(rtrim(ltrim($match, '('), ')'));
      // get the name of the tag
      $name = trim(substr($tag, 0, strpos($tag, ':')));
      // check if it is a link tag
      if($name == 'link') {
        // create a new Kirbytag instance
        $tag = new Kirbytag(null, $name, $tag);
        // get all the attributes and add them to our list
        $attributes = $tag->attr();
        $list[] = $attributess;
      }
    }
  }
  return $list;

};

dump($page->getTaggedLinklist());

We could, of course, make this more versatile by passing the tags we want to catch as a parameter array instead of hard-coding the link tag… but this is just an example.


#13

I don’t think that time is wasted… This regex stuff is useful all the time.


#14

Ok… after several days of playing around, i was able to get an array sorted by tags, with subarrays sorted by date.
everything is filtered: no doublicate links, no links of code-blocks nor inline-code-lines.

currently i am thinking about the real usecase. i would like to have an output, that does not depend on the kirby-template. so i think it would be logical to produce markdown output.

i think it could be done via another custom tag. any better ideas?


#15

Ok, here is the code, if anyone is interested in. maybe it is a help for someone.
Since, the markdown-extra tables are not very comfortable to align and multiple head-lines are not supported, i will change my original idea and write my own html table and/or tab based template. also i’m not sure which data i will output. so, here is a sample output you will have to modifiy for yourself. at this stage, i think, every change will just help myself and not anyone else… so here are my findings/is my code.

  • The Plugin analyses the raw content files for Markdown Links which are extended by a tag: attribut.
  • Original: (link: http://wikipedia.de text: wikipedia Website)
  • New: (link: http://wikipedia.de tag: wiki text: wikipedia Website)
  • when you use the new snippet tag in any of your pages, you will get a table of link collection.

site/snippets/linksnippet.php

<div>
<?php
$items = page('home')->children()->visible();

foreach($items as $item) {
    page::$methods['getTaggedLinklist'] = function($page, $field = null) {
          if(!is_null($field) && in_array($field, $page->content()->fields())) {
          $raw = $page->{$field}();
            } else {
                $raw = $page->content()->raw();
                }
        // find all tags
        preg_match_all('!`[^``]*`(*SKIP)(*F)|\(([^)]+\))!', $raw, $matches);
        $list = [];
        if(count($matches[0]) > 0) {
          // loop through the matches
          foreach($matches[0] as $match) {
            // remove the brackets
            $tag  = trim(rtrim(ltrim($match, '('), ')'));
              // get the name of the tag
              $name = trim(substr($tag, 0, strpos($tag, ':')));
                // check if it is a link tag
                if($name == 'link') {
                  // create a new Kirbytag instance
                  $tag = new Kirbytag(null, $name, $tag);
                  // check if tag is included
                  if(!is_null($tag->attr('tag'))) {
                    // get all the attributes and add them to our list
                    $list[] = $tag->attr();
                  }
                }
          }
        }
        return $list;
      };

      foreach($item->getTaggedLinklist() as $element) {
          // define content of each link-array
      if(empty($element['text'])) {
            $textelement = parse_url($element['link'], PHP_URL_HOST);
              } else {
                $textelement = $element['text'];
                }
          $output = array(
                  'tag' => $element['tag'],
                  'link' => $element['link'],
                  'text' => $textelement,
                  'pagetitle' => $item->title()->value(),
                  'pagelink' => $item->url(),
                  'date' => $item->date()
                  );
          $key = $element['tag'];
          $full[$key][] = $output; // add link-array to tag-group
          // filter duplicate links while
              $temparr = array_unique(array_column($full[$key], 'link'));
              $finalarray[$key] = array_intersect_key($full[$key], $temparr);
          // sort each element in a tag-group by date
              usort($finalarray[$key], function($a, $b) {
                        return $a['date'] - $b['date'];
                        }
                    );
      }
    }

    // sort tag-groups by alphabet
    uksort($finalarray, 'strcasecmp');

    // generate markdown table of array

    $tgmt = "\n"; // tag group markdown table
    foreach($finalarray as $taggroupname => $taggroup) {
    $tgmt .= '### ' . $taggroupname . "\n";
    $tgmt .= '|link|text|date|' . "\n"; // table header
    $tgmt .= '|--|--:|--:|' . "\n";
        foreach($taggroup as $tgi) { // taggroupitem
            // convert unix timestamp
            $timeconv = date('Y.m.d - H:i', $tgi['date']);

            // generate table row
            $linkstring = url::short($tgi['link'], 33);

            $link = '(link: ' .  $tgi['link'] . ' text: ' . $linkstring . ')';
            $tgmt .= '|' . $link . '|' . $tgi['text'] . '|(link: ' .  $tgi['pagelink'] . ' text: ' . $timeconv . ')|' . "\n";
        }
    }
    echo kirbytext($tgmt);
?>
</div>

site/tag/link.php

<?php

// link tag
kirbytext::$tags['link'] = array(
  'attr' => array(
    'text',
    'class',
    'role',
    'title',
    'rel',
    'lang',
    'target',
    'popup',
    'tag'
  ),
  'html' => function($tag) {

    $link = url($tag->attr('link'), $tag->attr('lang'));
    $text = $tag->attr('text');

    if(empty($text)) {
      $text = $link;
    }

    if(str::isURL($text)) {
      $text = url::short($text);
    }

    return html::a($link, $text, array(
      'rel'    => $tag->attr('rel'),
      'class'  => $tag->attr('class'),
      'role'   => $tag->attr('role'),
      'title'  => $tag->attr('title'),
      'target' => $tag->target(),
    ));

  }
);

site/tag/snippet.php

<?php
kirbytext::$tags['snippet'] = array(
  'attr' => array(
  ),
  'html' => function($tag) {
        $file =  $tag->attr('snippet');
        return snippet($file, array(), true);
  }
);

some notes:

  • links inside codeblocks or inline-code are ignored
  • duplicate links are ignored
  • invisible pages are ignored
  • output is sorted by tag and by date
  • when text attribute is empty, the hostname of the link is used as text
  • tags are case-sensitive
  • take a look at linksnippet.php to get an idea of outputcode
  • i’m not a coder. those few lines of code cost me a lot of time. most of you will laugh on the ugly code, the ugly structure and time i wasted. i know, the most of you would write something like this is an hour or less. for me it took several full days of finding code snippets and trying to understand them.
  • thx a lot to @texnixe for your help.
  • also a lot help was the idea of this thread: Embedding Snippets

Include in site
snippet-tag

possible output (with align-problem)


#16

Forgive me if i have missed something but isn’t that alignment issue a CSS style problem? Looks to me like the tables are deciding for themselves what the column widths should be?

Markdown tables are pretty fussy… i have used this tool before to help me out.

Setting the table layout to fixed in the css should line up those columns.

table {
 table-layout: fixed;
}

I had a similar problem on a site I built, that had a bunch of separate price tables with text in-between like you have. That was the fix.


#17

Problem is cell-content.
The Markdown allows only the alignment inside the cells.

Aligning cells is indeed a template/css styling option. and for this, it is impossible to use markdown tables AND column alignment at the same time WITHOUT changing template-code.

my original idea was to create table via markdown to have a nice integration for nearly every template without any further code changes.

so, until kirby has any feature to align collums integrated, it is a template based problem


#18

Two things:

  • the page method shouldn’t live within the snippet, but in a separate plugin file
  • why markdown at all in this context? Doesn’t make sense to me. Also, with standard HTML, you can apply classes as needed.

Edit: only saw your last post after posting my own


#19

why markdown at all in this context? Doesn’t make sense to me.

the only reason was to code a “plugin”, that is compatible in design for every template.
for sure it is possible to generate output in html, however i want. but then, it is not “universal-compatible” =)


#20

The only thing you could do with your current markup is add a class to the headings and then style the tables after the headings with that class. Hacky, though.

## Heading ##    {.there-s-a-table-following-this-heading}