If you've done any search engine optimization work you know about keywords. Keywords don't do much directly for users, but are included as meta data to help search engines understand what the designer/developer/content producer thought was important about the page being indexed. Anyone who works in SEO will tell you that relevant content is king. Keep your content relevant, link to relevant sources and provide relevant meta data. What do I mean by relevant meta data? I mean 3 meta tags that should be included on all pages: title, description and keywords. These 3 components can provide a lot of information about each individual page to the search engine spiders.
One of the things I noticed after installing BlogCFC and setting up this site was that I could set and update keywords for the entire site. Knowing the importance of those keywords, I thought this was great and during setup gave a few generic keywords to be used. But then I realized an issue: my blog is not about 1 specific topic so 1 set of keywords could actually prove to be a negative!
If you have delved into Google's webmaster tools you may have seen reports that show pages that have duplicate keyword content. Google, and I dare to guess Bing, Yahoo and Blekko. apparently don't like it when every page on your site has the same keywords. And that stands to reason; is EVERY page about the EXACT same thing? Probably not. So, I set about rectifying this issue in my BlogCFC installation. Several hours after I did it, when I couldn't get to sleep, I decided to write-up how I did it. This is my first attempt at writing about my coding adventures. I hope I didn't forget anything. I'm going to break this into 2 parts. This part will cover the admin side and part 2 will cover the implementation on the client side.
First things first, I needed a place to store my new keywords, so I went to the database (SQL Server 2005 in my case) and in the tblblogentries table, added a nullable, nvarchar(200) field named pageKeywords. It is important to make it nullable because no previous entries will have a value for this field.
Data storage out of the way, I had to populate it! this is my first foray into the BlogCFC code, so I had to do a little looking around. IN short order I discovered that I would need to amend 2 functions in org.camden.blog.cfc and then update admin/entry.cfm to ask for the data and pass it along to the functions.
Update addEntry function The first function I updated was addEntry. I'm not going to paste the whole function as the modifications were minor and should be easy to do should you choose. I'm passing in some new data, so I added a new parameter
I also created a var-scoped local variable. I know CF9 offers the local scope, but I'm running CF8.
Because I'm going to add a text field to the form, argument.pageKeywords should always be defined. I went ahead and assigned the local variable final_pageKeywords the default, site-wide values. this way, if I decide not to specify distinct keywords, the data will still be populated. This has the benefit of presenting the list to me later should I choose to edit the entry, but let's not get ahead of ourselves.
Then I added the following snippet:
2 <cfset final_pagekeywords = arguments.pageKeywords>
3</cfif>
At this point, I considered appending or prepending my desired keywords into the preassigned final_pagekeywords to make a more robust list. I decided against it as I want to make sure my keywords are relevant to this page.
Finally, I updated the insert query to the following, which just added the pageKeyword field and final_pagekeyWords value:
2 insert into tblblogentries(id,title,body,posted
3 <cfif len(arguments.morebody)>,morebody</cfif>
4 <cfif len(arguments.alias)>,alias</cfif>
5 ,username,blog,allowcomments,enclosure,summary,subtitle,keywords,duration,filesize,mimetype,released,views,mailed,pageKeywords)
6 values(
7 <cfqueryparam value="#id#" cfsqltype="CF_SQL_VARCHAR" maxlength="35">,
8 <cfqueryparam value="#arguments.title#" cfsqltype="CF_SQL_VARCHAR" maxlength="100">,
9 <cfif instance.blogDBTYPE is "ORACLE">
10 <cfqueryparam cfsqltype="cf_sql_clob" value="#arguments.body#">,
11 <cfelse>
12 <cfqueryparam value="#arguments.body#" cfsqltype="CF_SQL_LONGVARCHAR">,
13 </cfif>
14
15 <cfqueryparam value="#arguments.posted#" cfsqltype="CF_SQL_TIMESTAMP">
16 <cfif len(arguments.morebody)>
17 <cfif instance.blogDBType is "ORACLE">
18 ,<cfqueryparam cfsqltype="cf_sql_clob" value="#arguments.morebody#">
19 <cfelse>
20 ,<cfqueryparam value="#arguments.morebody#" cfsqltype="CF_SQL_LONGVARCHAR">
21 </cfif>
22 </cfif>
23 <cfif len(arguments.alias)>
24 ,<cfqueryparam value="#arguments.alias#" cfsqltype="CF_SQL_VARCHAR" maxlength="100">
25 </cfif>
26 ,<cfqueryparam value="#getAuthUser()#" cfsqltype="CF_SQL_VARCHAR" maxlength="50">,
27 <cfqueryparam value="#instance.name#" cfsqltype="CF_SQL_VARCHAR" maxlength="50">,
28 <cfif instance.blogDBType is not "MYSQL" AND instance.blogDBType is not "ORACLE">
29 <cfqueryparam value="#arguments.allowcomments#" cfsqltype="CF_SQL_BIT">
30 <cfelse>
31 <!--- convert yes/no to 1 or 0 --->
32 <cfif arguments.allowcomments>
33 <cfset arguments.allowcomments = 1>
34 <cfelse>
35 <cfset arguments.allowcomments = 0>
36 </cfif>
37 <cfqueryparam value="#arguments.allowcomments#" cfsqltype="CF_SQL_TINYINT">
38 </cfif>
39 ,<cfqueryparam value="#arguments.enclosure#" cfsqltype="CF_SQL_VARCHAR" maxlength="255">
40 ,<cfqueryparam value="#arguments.summary#" cfsqltype="CF_SQL_VARCHAR" maxlength="255">
41 ,<cfqueryparam value="#arguments.subtitle#" cfsqltype="CF_SQL_VARCHAR" maxlength="100">
42 ,<cfqueryparam value="#arguments.keywords#" cfsqltype="CF_SQL_VARCHAR" maxlength="100">
43 ,<cfqueryparam value="#arguments.duration#" cfsqltype="CF_SQL_VARCHAR" maxlength="10">
44 ,<cfqueryparam value="#arguments.filesize#" cfsqltype="CF_SQL_NUMERIC">
45 ,<cfqueryparam value="#arguments.mimetype#" cfsqltype="CF_SQL_VARCHAR" maxlength="255">
46 ,<cfif instance.blogDBType is not "MYSQL" and instance.blogDBType is not "ORACLE">
47 <cfqueryparam value="#arguments.released#" cfsqltype="CF_SQL_BIT">
48 <cfelse>
49 <!--- convert yes/no to 1 or 0 --->
50 <cfif arguments.released>
51 <cfset arguments.released = 1>
52 <cfelse>
53 <cfset arguments.released = 0>
54 </cfif>
55 <cfqueryparam value="#arguments.released#" cfsqltype="CF_SQL_TINYINT">
56 </cfif>
57 ,0
58 ,<cfif instance.blogDBType is not "MYSQL" AND instance.blogDBType is not "ORACLE">
59 <cfqueryparam value="false" cfsqltype="CF_SQL_BIT">
60 <cfelse>
61 <cfqueryparam value="0" cfsqltype="CF_SQL_TINYINT">
62 </cfif>
63 ,<cfqueryparam cfsqltype="cf_sql_varchar" value="#final_pagekeywords#" maxlength="200">
64 )
65 </cfquery>
Updating saveEntry function While in blog.cfc, I made similar updates to the saveEntry function. I'll only post the line I added to the update sql statement to save time. I copied/pasted the code from addEntry to add the arguments.pageKeywords parameter, the var-scoped final_pageKeywords and the if-statement to determine the value for frinal_pageKeywords.
Add the line below to end of the update statement, just like any other value (don't forget the comma at the beginning of the line).
Update admin/entry.cfm Entry.cfm does some processing at the top to allow for displaying values that have already been entered. So, I needed to define a new variable for the page to hold my keywords when the page loads pre-populated with data. Actually I had to do it twice because variables are defined based on whether or not the entry has already been saved or not. For simplicity here is the entire if-else statement that includes my additions.
2 <cfset entry = application.blog.getEntry(url.id,true)>
3 <cfif len(entry.morebody)>
4 <cfset entry.body = entry.body & "<more/>" & entry.morebody>
5 </cfif>
6
7 <cfparam name="form.title" default="#entry.title#">
8 <cfparam name="form.body" default="#entry.body#">
9 <cfparam name="form.posted" default="#entry.posted#">
10 <cfparam name="form.alias" default="#entry.alias#">
11 <cfparam name="form.allowcomments" default="#entry.allowcomments#">
12 <cfparam name="form.oldenclosure" default="#entry.enclosure#">
13 <cfparam name="form.oldfilesize" default="#entry.filesize#">
14 <cfparam name="form.oldmimetype" default="#entry.mimetype#">
15 <cfparam name="form.released" default="#entry.released#">
16 <cfparam name="form.duration" default="#entry.duration#">
17 <cfparam name="form.keywords" default="#entry.keywords#">
18 <cfparam name="form.subtitle" default="#entry.subtitle#">
19 <cfparam name="form.summary" default="#entry.summary#">
20 <cfparam name="form.pageKeywords" default="#entry.pageKeywords#">
21
22 <cfif form.released>
23 <cfparam name="form.sendemail" default="false">
24 <cfelse>
25 <cfparam name="form.sendemail" default="true">
26 </cfif>
27
28
29 <!--- handle case where form submitted, cant use cfparam --->
30 <cfif not isDefined("form.save") and not isDefined("form.preview")>
31 <cfset form.categories = structKeyList(entry.categories)>
32 <cfset variables.relatedEntries = application.blog.getRelatedBlogEntries(url.id, true, true, true)>
33 <cfset form.relatedEntries = valueList(relatedEntries.id)>
34 </cfif>
35
36
37 <cfelse>
38
39 <!--- look for savedtitle, savedbody from cookie, but only if not POSTing --->
40 <cfif not structKeyExists(form, "title") and structKeyExists(cookie, "savedtitle")>
41 <cfset form.title = cookie.savedtitle>
42 </cfif>
43 <cfif not structKeyExists(form, "body") and structKeyExists(cookie, "savedbody")>
44 <cfset form.body = cookie.savedbody>
45 </cfif>
46
47 <cfif not isDefined("form.save") and not isDefined("form.return") and not isDefined("form.preview")>
48 <cfset form.categories = "">
49 </cfif>
50 <cfparam name="form.title" default="">
51 <cfparam name="form.body" default="">
52 <cfparam name="form.alias" default="">
53 <cfparam name="form.posted" default="#dateAdd("h", application.blog.getProperty("offset"), now())#">
54 <cfparam name="form.allowcomments" default="">
55 <cfparam name="form.oldenclosure" default="">
56 <cfparam name="form.oldfilesize" default="0">
57 <cfparam name="form.oldmimetype" default="">
58 <!--- default released to false if no perms to release --->
59 <cfparam name="form.released" default="#application.blog.isBlogAuthorized('ReleaseEntries')#">
60 <cfparam name="form.duration" default="">
61 <cfparam name="form.keywords" default="">
62 <cfparam name="form.subtitle" default="">
63 <cfparam name="form.summary" default="">
64 <cfparam name="form.relatedEntries" default="">
65 <cfparam name="form.sendemail" default="true">
66 <cfparam name="form.pageKeywords" default="">
67 </cfif>
I named the variable form.pageKeywords and if the field has already been populated, I set it to #entry.pageKeywords#, otherwise it is set to blank.
When the form is submitted, I need to tell it to pass this new value to our functions addEntry (for first time saving) and saveEntry (after making changes). A quick search of entry.cfm for 'addEntry' finds the following line and you will see that I have just added our value pageKeywords to the end of the parameters list, recalling that this parameter has already been defined in blog.cfc.
Another quick search for 'saveEntry' yields the following line just above the addEntry code similarly updated.
Now I just needed to actually update the form to get the information for our functions. In entry.cfm, search for this comment:
That will put you right at the top of the "Additional Settings" tab. I then copied and pasted one of the existing divs into the outer div like so:
2 <fieldset class="inlineLabels">
3 <div class="ctrlHolder">
4 <label for="pagekeywords">Page Keywords: </label>
5 <input type="text" name="pagekeywords" id="pagekeywords" value="#form.pagekeywords#" maxlength="200" class="textInput">
6 </div>
7 <div class="ctrlHolder">
8.
9.
10.
These simple changes will now save and update new page independent keywords for your entries. In part 2, I will describe the much simpler code I used to actually display the new keywords n my pages while maintaining the site-wide keyword list for non-entry pages.
#1 by Steph Riggs on 3/9/14 - 11:34 AM
#2 by Spook SEO on 6/4/14 - 3:10 AM