Tags Mod for sNews version 2

Tags Mod for sNews version 2

Here is the second edition of my tags mod for sNews 1.7, this mod will format your tags in an SEO link, as shown on my own page here, i.e. http://yousite.com/tag/widget/. This version also separates the tags search from the standard search.

Please note, if you have installed my tags mod and want to upgrade to this version, I will write up a separate tutorial on that as well, if you can't figure out the differences from this one. As before, you will need to add your keywords with a single comma and space separating them, i.e.
keyword1, keyword2, keyword3, etc.

So let's get started. BACK-UP your snews.php file and work on a copy.

1. Find the hard coded link items;

$l['cat_listSEF'] = 'archive,contact,sitemap,login,administration,admin_category,admin_article,article_new,extra_new,page_new,snews_categories,snews_articles,extra_contents,snews_pages,snews_settings,snews_files,logout,groupings,admin_groupings';

now we need to add the "tag" page, as in the highlighted code;

$l['cat_listSEF'] = 'archive,contact,sitemap,tag,login,administration,admin_category,admin_article,article_new,extra_new,page_new,snews_categories,snews_articles,extra_contents,snews_pages,snews_settings,snews_files,logout,groupings,admin_groupings';

2. now in the function center(), find the globals and add the code like below, IF YOU HAVE ALREADY ADDED THE url code from the SEO aearch mod, you do not need to add that again.

global $categorySEF, $subcatSEF, $articleSEF;
    // begin new code
    $url = explode('/',clean(cleanXSS($_GET['category']))); // already done if you have the SEO search mode installed
    if ($url[0] == "tag") {
        $tags_query = $url[1];
    }
    // end new code

3. Now around line 545, still within the function center(), find the following code;

case isset($_POST['search_query']): // this may have been changed to $search_query if you have installed the SEO search mod
    search(); return; break;

Now ADD the following code below;

case isset($tags_query):
    tagsearch($tags_query); return; break;

4. Find the function articles. Look for

if ($_ID || (!$_catID && $frontpage != 0)) {

and update the $query_articles variable by adding the highlighted code

if ($_ID || (!$_catID && $frontpage != 0)) {
        if (!$_ID) $_ID = $frontpage;
            // article or page, id as indentifier
            $query_articles = 'SELECT
                    a.id AS aid,title,a.seftitle AS asef,a.category AS acat,a.keywords_meta AS keywords,text,a.date,
                    a.displaytitle,a.displayinfo,a.commentable,a.visible
                FROM  '._PRE.'articles'.' AS a
                WHERE id ='.$_ID.$visible;
        } else {

5. Now look for

// get the rows for category
if ($_catID) {

and update the $query_articles variable by adding the highlighted code

// get the rows for category
if ($_catID) {
    $query_articles = 'SELECT
            a.id AS aid,title,a.seftitle AS asef,a.keywords_meta AS keywords,text,a.date,
            a.displaytitle,a.displayinfo,a.commentable,a.visible
    FROM '._PRE.'articles'.' AS a
    WHERE position = 1
        AND a.published =1
        AND category = '.$_catID.$visible.'
    ORDER BY artorder ASC,date DESC
    LIMIT '.($currentPage - 1) * $article_limit.','.$article_limit;
} else {
    $query_articles = 'SELECT
            a.id AS aid,title,a.seftitle AS asef,a.keywords_meta AS keywords,text,a.date,
                displaytitle,displayinfo,commentable,a.visible,
            c.name AS name,c.seftitle AS csef,
            x.name AS xname,x.seftitle AS xsef
        FROM '._PRE.'articles'.' AS a
        LEFT OUTER JOIN '._PRE.'categories'.' as c
            ON category = c.id
        LEFT OUTER JOIN '._PRE.'categories'.' as x
            ON c.subcat =  x.id AND x.published =\'YES\'
        WHERE show_on_home = \'YES\'
            AND position = 1
            AND a.published =1
            AND c.published =\'YES\''.$visible.'
        ORDER BY date DESC
        LIMIT '.($currentPage - 1) * $article_limit.','.$article_limit;
}

6. That will slurp the keywords into the $r['keywords'] variable. We will now prepare to break the keywords apart and insert them into the article, underneath the "infoline" and admin edit links. Still within the articles function, find the 2 instances of the admin links;

} else if (_ADMIN) {
    echo '<p>'.$edit_link.'</p>';
}

then insert the following code below both of the admin edit links, like this;

// insert new code here
if (trim($r['keywords']) != "") { // not empty 
    $article_tags = explode(', ', $r['keywords']);
    $keyCount = count($article_tags);
    if ($keyCount > 0) {
        echo '<div class="tags">';
        for ($i = 0; $i < $keyCount; $i++) {
            if ($i == 0) {
                $pre = "";
            } else {
                $pre = ", ";
            }
             echo $pre.'<a href="'._SITE.'tag/'. urlencode($article_tags[$i]).'/" title="See more articles on '.$article_tags[$i].'">'.$article_tags[$i].'</a>';
        }
        echo "</div>";
    }
}

7. Now add the tags engine, THERE ARE TWO OPTIONS, PICK ONE. The first tagsearch function is for people who want to use MySQL's full-text speed and don't need to use tags under the full-text limit of 4 characters. If that's you, then simply copy and paste the entire function below to just above the closing php tag, if not, skip to step 9 now.

//TAGS ENGINE
function tagsearch($tags_query,$limit = 100) {
    $tags_query = clean(cleanXSS($tags_query));
    echo '<h2>'.l(tag_results).'</h2>';
    if (strlen($tags_query) < 4) {
        echo '<div><p>'.l('charerror').'</p>';
    } else {
        $tags_query = trim($tags_query);
       
        $now = date("Y-m-d H:i:s",time());
        $query = 'SELECT a.id,a.title,a.seftitle AS asef,a.date AS date,
                   c.name AS name,c.seftitle AS csef,
                   x.name AS xname,x.seftitle AS xsef, MATCH(a.keywords_meta) AGAINST (\'"+'.$tags_query.'"\' IN BOOLEAN MODE) AS score
            FROM '._PRE.'articles'.' AS a
            LEFT OUTER JOIN '._PRE.'categories'.' as c
                ON category = c.id AND c.published =\'YES\'
            LEFT OUTER JOIN '._PRE.'categories'.' as x
                ON c.subcat =  x.id AND x.published =\'YES\'
            WHERE MATCH(a.keywords_meta) AGAINST (\'"+'.$tags_query.'"\' IN BOOLEAN MODE) AND position != 2
                AND a.published = 1
                AND date <= \''.$now.'\'';
        if(!_ADMIN){
            $query = $query.'AND a.visible = \'YES\'';
        }
        $query = $query.' GROUP BY a.id HAVING score > \'0\' ORDER BY score DESC LIMIT '.$limit;
        $result = mysql_query($query);
        $numrows = mysql_num_rows($result);
        if (!$numrows) {
            echo '<div><p>'.l('noresults').'
                <strong>'.stripslashes(entity($tags_query)).'</strong>.</p>';
        } else {
            echo '<div id="tag_results"><p><strong>'.$numrows.'</strong> '.l('tagresultsfound').' <strong>'.
            stripslashes(entity($tags_query)).'</strong>.</p>';
            while ($r = mysql_fetch_array($result)) {
                $date = date(s('date_format'), strtotime($r['date']));
                if ($r['name']) { $name = ' in '.$r['name']; } else { $name = "";}
                if (isset($r['xsef']))  $link = $r['xsef'].'/'.$r['csef'].'/';
                else $link = isset($r['csef']) ? $r['csef'].'/' : '';
                echo '<p><a href="'._SITE.$link.$r['asef'].'/">'.$r['title'].'</a><br /><span class="tag_sub">added on '.$date.$name.'</span></p>';
            }
        }
    }
    echo '</div>';
}

8. Now set up a MySQL full-text index on your keywords field in your database

ALTER TABLE articles ADD FULLTEXT(keywords_meta); 

9. OK, add this tagsearch function only if you don't want to use MySQL's full-text search;

//TAGS ENGINE
function tagsearch($tags_query,$limit = 100,$min_chars = 2) {
    $tags_query = clean(cleanXSS($tags_query));
    echo '<h2>'.l(tag_results).'</h2>';
    if (strlen($tags_query) < $min_chars) {
        echo '<div><p>Tags must be at least '.$min_chars.' characters!</p>';
    } else {
        $tags_query = trim($tags_query);
       
        $now = date("Y-m-d H:i:s",time());
        $query = 'SELECT a.id,a.title,a.seftitle AS asef,a.date AS date,
                   c.name AS name,c.seftitle AS csef,
                   x.name AS xname,x.seftitle AS xsef
            FROM '._PRE.'articles'.' AS a
            LEFT OUTER JOIN '._PRE.'categories'.' as c
                ON category = c.id AND c.published =\'YES\'
            LEFT OUTER JOIN '._PRE.'categories'.' as x
                ON c.subcat =  x.id AND x.published =\'YES\'
            WHERE (a.keywords_meta LIKE \'%'.$tags_query.'%\' AND a.keywords_meta REGEXP \'[[:<:]]'.$tags_query.'[[:>:]]\') AND position != 2
                AND a.published = 1
                AND date <= \''.$now.'\'';
        if(!_ADMIN){
            $query = $query.'AND a.visible = \'YES\'';
        }
        
        $query = $query.' ORDER BY date DESC LIMIT '.$limit;
        $result = mysql_query($query);
        $numrows = mysql_num_rows($result);
        if (!$numrows) {
            echo '<div><p>'.l('noresults').'
                <strong>'.stripslashes(entity($tags_query)).'</strong>.</p>';
        } else {
            echo '<div id="tag_results"><p><strong>'.$numrows.'</strong> '.l('tagresultsfound').' <strong>'.
            stripslashes(entity($tags_query)).'</strong>.</p>';
            while ($r = mysql_fetch_array($result)) {
                $date = date(s('date_format'), strtotime($r['date']));
                if ($r['name']) { $name = ' in '.$r['name']; } else { $name = "";}
                if (isset($r['xsef']))  $link = $r['xsef'].'/'.$r['csef'].'/';
                else $link = isset($r['csef']) ? $r['csef'].'/' : '';
                echo '<p><a href="'._SITE.$link.$r['asef'].'/">'.$r['title'].'</a><br /><span class="tag_sub">added on '.$date.$name.'</span></p>';
            }
        }
    }
    echo '</div>';
}

10. Now open your language file (EN.php in the lang folder if you're using the default) and add the following lines;

# Tag search
$l['tag'] = 'Tags';
$l['tagresultsfound'] = 'item(s) tagged with;';
$l['tag_results'] = 'Tag Search';

10. Now you need an icon, like this one tags, save that and drop it into your images folder and then add the following to your style sheet. These are my colors, feel free to create your own.

#tag_results p {
    line-height:1.2em;
    margin: 0 0 10px 0;
}

#tag_results a {
    color: #386E80;
}

.tag_sub {
    font-size: 0.9em;
    color: #777777;
}

.tags {
    background: #fff url('../images/tags.png') left center no-repeat;
    font-size: 0.9em;
    margin-bottom: 14px;
    padding: 0 0 2px 14px;
}

.tags a {
    color: #f66363;
    text-decoration: none;
}

.tags a:hover {
    color: #000;
    text-decoration: none;
}

You can change things around to display them only on certain articles like I have done, for example I only show the tags on true "articles", not on "pages". To show only on articles with "infoline" shown and not "pages" like mine, I wrapped the second instance of the tag code in the following;

if ($infoline == true && $_catID != 0) {
//tag code goes here
}

Tags

 

You might like

Comments


I'm of to work now (no chance to upgrade), but it looks great.

Thanks for sharing Matt :)


There seems to be a error in the code:

in TAGS ENGINE the word "Here" in front of:

$tags_query = ereg_replace("[^-\+\s0-9a-zA-Z]", " ", $tags_query);


LOL... what the heck, strange, I have no idea how that ended up in there, thanks Sven, I have cleaned that up. Great catch!

Let me know how it works for you.


Thanks. I'm gonna give it a try
which tag code goes in the infoline?
if ($infoline == true && $_catID != 0) {
//tag code goes here
}


Hi Moz,

That code is just an extra option. If you wrap that around the second occurrence of the the tags code, it'll will include the tags ONLY if it's an article, not a "page".

The tags code I'm referring to is the code you're placing below both of the admin edit links.

So the second instance of the tags would look like this below the admin link;

} else if (_ADMIN) {
echo '<p>'.$edit_link.'</p>';
}
// insert new code here
if ($infoline == true && $_catID != 0) {
if (trim($r['keywords']) != "") { // not empty
$article_tags = explode(', ', $r['keywords']);
$keyCount = count($article_tags);
if ($keyCount > 0) {
echo '<div class="tags">';
for ($i = 0; $i < $keyCount; $i++) {
if ($i == 0) {
$pre = "";
} else {
$pre = ", ";
}
echo $pre.'<a href="'._SITE.'tag/'. urlencode($article_tags[$i]).'" title="See more articles on .$article_tags[$i].'">'.$article_tags[$i].'</a>';
}
echo "</div>";
}
}
}

Does that make sense?

Thanks.


Yes it does.
Thanks Matt. Great job.


Hi!

Great article, Matt!

Can You please do a little mod to display the tag in the meta title?
Instead of: Tags - mdj.us
to display: "tag" - mdj.us

And like this also for the Search.

Thanks!


Hi Tomaz,

That's easy, look for the function title(), find the line

$title = $_TITLE ? $_TITLE.' - ' : '';
and change it to

$url = explode('/',clean(cleanXSS($_GET['category'])));
if ($url[0] == "tag" || $url[0] == "search") {
$title = $url[1].' - ';
} else {
$title = $_TITLE ? $_TITLE.' - ' : '';
}

that will show the tags and search pages like the standard articles, i.e. if you search for purple widgets, the meta title will be
purple widgets - Search - yoursite.com


Thanks Matt!

Working fine.


Hello
I see a problem.
The tags URL are being indexed by the search engines, but since there's no specific meta description as "All tags on the sublect blablabla" all the meta description are the same (the website default one).

And searh engines want different meta description.
Do you see what I mean?


Hi Philippe,

Yeah, this is an inherent issue with all the "predefined" pages within sNews, it's the same thing with the contact page, sitemap, archive, etc.

This happens because sNews looks for the description_meta field in the database, and when it doesn't see it, it will assign the default for the website.

It's actually a fairly easy fix though, you can use this switch statement in the title function
add it below the code I gave above, so the whole thing would look like this;



$url = explode('/',clean(cleanXSS($_GET['category'])));
if ($url[0] == "tag" || $url[0] == "search") {
$title = $url[1].' - ';
} else {
$title = $_TITLE ? $_TITLE.' - ' : '';
}
switch ($url[0]) {
case "tag":
$_DESCR = "All items tagged with; ".$url[1];
break;
case "search":
$_DESCR = "Search results for all items containing the term(s); ".$url[1];
break;
case "archive":
$_DESCR = "View previous entries by date.";
break;
case "contact":
$_DESCR = "Contact the website operator.";
break;
case "sitemap":
$_DESCR = "Sitemap tree of all pages and articles on this site.";
break;
}


So good Matt!
(except there's no "S" at archive) ;-)
Thanks a lot pal!!!


Philippe, damn you're quick! Thanks for all the bug checking you do, you make my life easier, lol.

I have removed the "s".


hi man nice mood, but when you click on tag i get Tag Search
There are no results for query keyword.

??


Serkan, do you have a url to show me an example of the error?

You can send it to me through the contact form if you don't want want to publish it here.

If not, what is the tag that's not returning results? Is it all tags, or just one?


Well, it actually should not be possible to use special characters (for example "") in the tag search. For now it's possible to use those, so it's a possible xss vulnerability (website.com/tag//). Any way to fix this?


Hi Vic, the tags query is sanitized by running it through the clean (mysql_real_escape_string) as well as the cleanXSS (cleans up XSS) functions before doing anything with it, I fail to see where the vulnerability is?

The part where we output the search term could possibly contain malformed HTML, that should use htmlentities (I'll add that now), but that isn't an XSS or security issue.


Hey!

Yes, that was actually about it. :-) Thanks.

Wouldn't it be a problem to add in the section of the website if there are no results for the tag search (this would also apply to SEF/SEO search)? That would be handy in case someone tries to link to you, placing inappropriate words as tags (for example, website.com/tag/some-bad-words/). The tag would prevent search engines from indexing such content.


I will e-mail you with this, because tag cannot be printed in comments.


Vic, yeah, I used the default snews output that's why I didn't notice it until now, and you are correct, it's broken in the default snews.php search (nothing to do with my SEF/SEO mod actually), so we'll want to notify the snews crew as well.

Cool, I'll write up an update on this now.

Shoot me an email via the contact form, thanks a bunch for the input!


Hello.

For me This mod works fine, but I have one question...
I use in my tags character like: "ąśćęół" it's possible change this to "asceol" (automatically)?


Fyner, you mean convert the characters as you type them in the "customize" box with javascript, like the SEF Title?


Yes. For simple links to article sNews convert this characters (mod), but for this no :/


Can you give me a list of Polish accented characters and their English "non-accented" equivalents?

I know a few, if you wanted a php function to do that, it would be something like this;


function removeaccents($string) {
$accented = array('ą','ć','ł','ż','ź','ń','ó','ę','ś');
$stripped = array('a','c','l','z','x','n','o','e','s');
$output = str_replace($accented, $stripped, $string);
return $output;
}
but it will require a more work to put something together in Javascript, but I can take a look.


This all Polish accented characters ;]

I have function like this:

function deLocalize( inStr ) {
var outStr = inStr;
outStr = outStr.replace(/[Ä…Ä„]/g, 'a');
outStr = outStr.replace(/[ćĆ]/g, 'c');
outStr = outStr.replace(/[ęĘ]/g, 'e');
outStr = outStr.replace(/[łŁ]/g, 'l');
outStr = outStr.replace(/[ńŃ]/g, 'n');
outStr = outStr.replace(/[óÓ]/g, 'o');
outStr = outStr.replace(/[śŚ]/g, 's');
outStr = outStr.replace(/[żŻ]/g, 'z');
outStr = outStr.replace(/[źŹ]/g, 'z');
return outStr;
}

but this is not work for tag URLs :/


Comments are closed. No new comments allowed.

Copyleft 2002 - 2017 Matt Jones
Hand crafted with HTML5 & CSS3
↑ Back to top