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
, 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
}



Comments
RSS Comments Feed
Matt
I will try to look at it later tonight, I'm no javascript expert though :)
Paul
At first I would like thank you for great mod :)
Now I'm trying to implement tag2 mod into snews for SQLite. I have one problem because I don't understand the one rule in tagsearch function - REGEXP \'[[::]]\'
On SQLite it doesn't work. If I remove condition it looks like below.
For tag 'marketing' in tag cloud it shows articles tagged with
'marketing' - ok
'emarketing' - wrong
'e-marketing' - wrong
Could you help me with it?
Matt
I've only worked a tiny bit with SQLite before, but it appears REGEXP needs a user defined function, SQLite has no regexp library.
http://www.sqlite.org/lang_expr.html#like
Without a regular expression, there's no surefire way to match whole words, unless you were to put a non-breaking space in front of the first tag in the field, then you could do a simple LIKE using the preceding space, something like this perhaps? Note the blank space AFTER the wildcard.
You'd have to make sure each tag had that preceding blank space.
Paul
I'll check your proposal for solution with leading blank space for each tag. One more thing - could you explain this regular expression pattern? Till your mod I didn't meet with such pattern.
Would you so kind and try to create such a user defined function for SQLite?
Matt
The [[:<:]] & [[:>:]] are the word boundaries for MySQL REGEXP.
I don't have an SQLite install to test anything, but you can define your own functions for SQLite using PHP's sqlite_create_function.
So you would do something like this;
sqlite_create_function($db, 'TAG_REGEX', 'sqlite_tag_regex', 2);
function sqlite_tag_regex($str, $regex) {
if (preg_match('/\b$regex\b/', $str)) {
return 1;
}
return 0;
}
then you would need to change the REGEXP bit to something like
WHERE (a.keywords_meta LIKE \'%'.$tags_query.'%\' AND a.keywords_meta TAG_REGEX $tags_query) AND position != 2
keep in mind this is totally untested and I probably messed something up, but it's an idea of the methods used.
Paul
Paul
So I have to stay in present tags functionality on SQLite.
Matt
Kamil
I use this code and works OK.
But if I click some tags, i see only title of articles. How show also some intro text ? Like exmaple on your blog http://www.mdj.us/tag/tags/
This is very important for me
Thanks
Matt
Just add the meta description to the query selection;
SELECT a.id,a.title,a.seftitle AS asef,a.decription_meta as desc
then you can add it to the output, like this;
echo '<p><a href="'._SITE.$link.$r['asef'].'/">'.$r['title'].'</a><br /><span class="highlight">'.$r['desc'].'<br /></span><span class="tag_sub">added on '.$date.$name.'</span></p>';
Kamil
I write your code but don't work.
If i click on tags, I see text "There are no results for query news"
Maybe you can upload some zip file with this implementations ?
BIG THX
Matt
If you see the tags, an uploaded implementation will show you the same thing, the problem sounds like it's with the database.
If you get the text "There are no results for query news", then you either haven't set-up the tagsearch piece correctly, the word doesn't exist, or there's some other kind of problem related to the database
Are you using the full-text search option? If so, did you do step #8?
Kamil
Yes, i do step 8.
Matt
I got the file, I will check it out and get back to you.
Just a note, please remember to remove your database information before sending to me next time :)
Kamil
Matt
Pofee
This is a great script!
But i think so, it will be better,
if the tagsearch result list is be more then one page.
As category list, where work the paginator.
Have you any idea?
Sorry my ver bad english,
i hope you understand me.
Matt
I know, currently it only lists the first 100 results, though you can change that with the $limit variable.
Multiple pages/pagination unfortunately has not been written into it at this point, but I'm always looking for ideas, so maybe I will take the time some day to see about creating pagination for this.
Pofee
THX for reply.
I write the tags pagination code today.
It works, but the code is ugly or dirty,
so i hope, one day, you write it too,
better and cleaner as me. :)
Best regards: Pofee
Pear
I'm having a heck of a time getting the tags to show on the full view of an article.
They do show on a category listing. Any ideas?
Pear
Matt