We have all seen the terms and conditions pages and privacy policies on websites, most of us have not read them but they are an important part in protecting yourself from litigation from people on the internet and it helps set out a framework on how interactions between your website and the end user should occur. So you have made a website and you know you need to add them, but what now?
There are many different polices that I have come across so before you decide what you want on your website I should outline the main policies and what they are for below.
Terms and Conditions can be used as a blanket term or a specific policy. If you are running a website that provides pricing information, advice or any other service, you may want to consider adding a Terms and Conditions page or similiar page. This can absolve you of responsibility if anyone mistakenly uses your information for the wrong purposes, or wants to hold you liable for damages they have incurred by using your website or its information. Some other useful functions of a Terms and Conditions page is:
According to some, the name of the contract does not matter and that perhaps is why there are so many variations for the same thing, but read my thoughts below. You can call the contract what you want as it is a contract between you and the customer but I would stick with these rules because a good lawyer could say you tried to confuse the end user if the name was to different from what the contents purpose was.
The descriptions below are not the absoulte meaning of the titles because of the interchangability but I feel they are an apt description of what they are for.
The following articles all point towards them all being the same
Original before the internet this contract between you and the customer was called Terms and Conditions of Business which was then shortend to Terms and Conditions. This is what should be used for Bricks and Mortar business that primarily deal with a physical product. It should be noted that when you search for Terms and Conditions on Wikipedia that you are redirected to a page called Contractural term, which it probably is rather than a document name. Now we are in the age internet, people are still using Terms and Conditions to denote their agreement with the end user. I however feel that because you deliver things through a website, whether you deliver a physical product or not, maybe just a blog or advice, you are offering a service! This means that you should use Terms of Service. But what about Terms of Use I hear you say? Terms of Use is just another prose of Terms of Service instead of looking at it from the website or companies side you have looked at things from the user's point of view, or at least that is what the name implies. These terms are all interchangable in this modern world even thought I am not 100% convinced when you arbitrarily choose one of these terms it is the correct one, hence my musings here. The title must make sense to the policy it entitles.
It does now seem that Terms of Service is the most popular term name for websites. I have read some advice just to call the document Terms and not add any of the other ancillary words, I would perhaps refrain from that so the customer and laywers know what you meant. You can quite happily call the hyperlink Terms but give the document a proper title.
There are a couple of different parts to consider when you want to add legal policies to your website.
There are that many different variations used on websites all over the internet but no real standard on how to layout your 'Terms and Conditions' and 'Privacy Policy' and even how to actually layout out those policies. What I hope to achieve in this article is show you how to configure all these different parts based on your business model and website by following rules I set down in the following sections giving rise to a standard.
When filling in these documents consider using an email that is not your primary address so if spammers get hold of it you can chnage the email on the policies easily. I am not sure about the legalities of this and in fairness i do think it will cause any issue sunless you are in the middle of communication for an issue and even then the email address will be available. Use something like privacy2015@quantumwarp.com and then you can change the year if needed without killing of your main email address.
The hyperlink is the first thing a customer will look for on the home page when looking for the terms and conditions. In the USA the privacy policy law states that there should be a link on the homepage (if not all) with the word ‘Privacy’ prominently part of it pointing to the sites privacy policy. There does not seem to be a similar directive for 'terms and conditions' but we shall use the same model and even though this is a USA law there is no harm applying it to the rest of the world.
If in the UK/EU you need to select whether you want a single privacy page incorporating your cookies policy and privacy policy or if you want them as seperate pages. If you have lots of cookie information then maybe having a seperate cookies policy page.
This table will help you how decide how to build your hyperlinks for your Privacy Policy and Terms and Conditions pages link to your terms page. Select the relevant rows for your scenarion below, considering:
This will result in giving you the hyperlink that matches your ciscumstances.
Selection | Result | |||
Country |
Hyperlink Anchor Text |
Page Slug / SEF URL |
Notes |
|
1 |
All |
Terms & Conditions |
../terms |
All policies would be listed in their own right here i.e. ‘Terms of Service’. (i) single page with all listed (ii) a holding page with links to all of the polices (ideal if too large) – both will have paragraphs at the top saying they all form part of a single agreement Use ‘&’ instead of ‘and’ in the hyperlink text to make it shorter. This is the most common but is optional |
2 |
All |
Terms Of Service |
../terms |
Only use this if you need to be very specific. If you use this then i would recommend the resultant page must be titled 'Terms of Service' otherwise it could be confuding |
3 |
All |
Terms |
../terms |
Use this when there is not a lot of real estate |
|
||||
4 |
USA/Rest of World |
Privacy |
../privacy |
Use this when there is not a lot of real estate |
5 |
USA/Rest of World |
Privacy Policy |
../privacy |
|
6 |
UK/EU |
Privacy |
../privacy |
Use this when there is not a lot of real estate |
7 |
UK/EU |
Privacy Policy |
../privacy |
Do I need cookies in the link?) |
8 |
UK/EU |
Privacy & Cookies Policy Privacy & Cookies |
../privacy |
both policies on one page |
|
||||
9 |
UK/EU |
Privacy Policy |
../privacy |
separate page (opt) |
10 |
UK/EU |
Cookies Policy |
../cookies |
separate page (opt) |
Notes
My Selection
- Terms Hyperlink (option 1)
- 'Terms & Conditions' for the hyperlink anchor text
- ../terms for the page slug
- the use of ampersand keeps the link shorter
- This seems to be the most popular options and does not tie me to using ‘Terms of service’ on the ../terms page
- It allows me to change the terms at a later date without having to change the hyperlink
- It is well recognised in the USA and UK
- I have enough room for the full anchor text
- Privacy Hyperlink (option 8)
- 'Privacy & Cookies' for the hyperlink anchor text
- ../privacy for the page slug
- I have enough room for the full anchor text
- It is well recognised in the USA and UK
- I did not want a separate cookies page
- Cookies Hyperlink (n/a)
- I did not want a separate cookies page so this is not needed
We have identified your hyperlink and page slug for your selected scenario. We now need to establish the actual layout of the content, titles and sections which can be just as complex unless someone just tells you what they would do.
I have seperated this process out in to 2 sections, Terms and Conditions and Privacy Policy to keep things logical as they have different thought processes and are held on different pages. In this section we will deal with the Terms and Conditions page.
We now need to select the option by your type of business model because this has the biggest influence for the page name.
This table will help you decide how to name and layout your page titles for Terms and Conditions.
Selection | Result | |||
|
Type of Business |
<h1> / Primary Title / Page Title |
Rename TOS policy to ? and add it as a subthing (<h2> Section / hyperlink / clause ) to the main policy as defined by the page title |
Notes |
1 |
Non-interactive website |
Disclaimer |
x |
Add disclaimer only, you do not really need a 'Terms of Service'. You can use option 2 if you do want one. |
2 |
Interactive Website Only |
Terms of Service |
x |
All rules can be put in the TOS as clauses |
3 |
Website + e-commerce |
Terms of Service |
Terms of Website Use |
All rules can be put in the TOS as clauses |
4 |
Website + e-commerce + Bricks and Mortar |
Terms and Conditions / Terms of Service |
Terms of Website Use |
|
5 |
Bricks and Mortar only |
Terms and Conditions Terms and Conditions of Business Terms and Conditions of Sale |
x |
Notes
My Selection
Option 4
- The combined title prevent misunderstanding and utilses the 2 most common names for this contract
- It is universal and easy to understand
- The TOS can easily be expanded
- The website use terms still under the single policy are in a clear section
The link, page(s) and title(s) are now set. You have a couple of options that you can use to display your content not. These are the variations of layouts that you can apply to your terms page when you require more than a TOS, such as terms-of-sale which includes DSR (EU regulation, Distance Selling Regulation) to be accepted or displayed.
Example subthing names, these now come into play.
Subthings is the name I have given to the <h2> sections/hyperlinks/clauses reference points within the terms and conditions we are building here. You will use these containers once you have decided which you prefer
These are the available layouts and their pros/cons. This section is more about personal preference.
This is dealing with layout and sub title stuff, everything else is done.
Selection | Selection | Result | ||||
|
Terms page type |
Subthing policy type:
<h2>Sections Hyperlinks Clauses |
Example sites |
Notes |
||
1 |
1 page 1 policy |
Clauses |
x |
Probably not suitable if you are adding a lot of extra policies, you will need to make sure that all of the new clauses numbers match with the pre-exisiting policy, but ideal for small personal sites with interaction. |
||
2 |
1 page 1 policy |
<h2> Sections |
x |
All policy areas are added as <h2> sections under the main <h1> title of that page so they is 1 policy to agree too. There must be a maintained hierarchy You could add the sections as extra clauses in the Terms, but adding a <h2> section is cleaner when adding another policy so you don’t have to change all of the rerferences |
||
3 |
1 page 1 policy |
Hyperlinks |
All policy areas are added as sections under the main title of that page so they are 1 policy to agree too. There must be a maintained hierarchy. However, other policy terms can hyperlinked (ideal when they are long) with a paragraph at the beginning of the policy telling you of this. |
|||
4 |
1 page 1 policy |
Clauses <h2> Sections Hyperlinks |
x |
A combination of all 3 ways to add additional policies and conditions is possible but just be careful to keeps things organised. |
||
5 |
1 Index page Multiple policies |
hyperlinks |
When you have many terms and conditions you can list all of your terms and policies here as hyperlinks You might struggle having it agreed someone accepting these terms by pointing to this page. The TOS would be on its own page in this scenario. Paragraph saying all of these make up the terms and conditions? |
Notes
My Selection
Option 2
- single policy with multple sections is easy to manage
- can easyily be accepted as a single TOS
- easy to expand
We now have identified your hyperlink and page slug for your selected scenario. We now need to establish the actual layout of the content, titles and sections which can be just as complex unless someone just tells you what they would do.
I have seperated this process out in to 2 sections, Terms and Conditions and Privacy Policy to keep things logical as they have different thought processes and are held on different pages. In this section we will deal with the Privacy Policy page.
the layout of these polciies is much simplier. so the decision process is much simplier
This table will help you decide how to name and layout your page title(s) for the Cookies and Privacy policies. consider:
Selection | Result | |||
|
Type |
<h1> / Primary Title / Page Title |
<h2> Titles |
Notes |
1 |
USA/Rest of world |
Privacy Policy |
x |
x |
2 |
UK Single Page 1 policy |
Privacy and Cookies Policy |
x |
If you get a pre-done policy for the UK with Cookies as part of it, just fill in the information and you are done |
3 |
UK Single Page 1 policy 2 sections |
Privacy and Cookies Policy |
Privacy Cookies |
This allows you to get a good privacy policy yet use a cookie audit tool that generates it’s own cookie policy (atticat) and add them together easily. Also makes updating easy because you don’t have to merge them, just replace. |
4 |
UK Single Page 2 polices |
Privacy and Cookies Policies |
Privacy Policy Cookies Policy |
You might consider changing the hyperlink anchor text to ‘Privacy’ with this option. |
5 |
UK 2 Pages Privacy |
Privacy Policy |
x |
If your Privacy Policy mentions Cookies then add a link to your cookies page saying more info available and that the Cookies Policy is part of the Privacy Policy. |
6 |
UK 2 Pages Cookies |
Cookies Policy |
x |
x |
Notes
1.2 By using our website and agreeing to this policy, you consent to our use of cookies in accordance with the terms of our Cookies Policy.
My Selection
Option 3
- Single Page (UK)
- Less pages to manage
- I don’t have a lot of cookies on my site
- Less links on my homepage
- Google only has a single policy integrating cookies
- I am in the UK and need a cookies policy
- Single Policy 2 Sections (UK)
- 2 sections so I can easily manage updates to the policies
- I can use the atticat cookie audit tool to generate a cookie policies with all the reference cookies as needed rather than doing it manually.
- I also have a really good privacy policy where I can remove the cookie section
- I am in the UK and need a cookies policy
- I removed the cookie clauses/section from my Privacy Policy
- I modified a clause (1.2) that says by accepting the privacy policy I also accepted the cookies policy. This is required so the cookies policy is refrenced in the privacy policy.
- only had to change the title heirarchy slightly
Terms and conditions are usually very specific to your website and sometimes your country and state. There is a lot of common statements such as liability and do not hack my website. I have use the phrase Terms and Conditions throughout this article to loosely reference this section. As you know many websites call their terms different things and then these terms might include more things that the Terms of Service. In the tables above you have decided what actuall polices you want on your site so i will try and make the following instructions as general as possible so the workflow is the same (or as close to) for all options.
To start with these are the recommended sources. I will refer to the following documents as Terms of Service
For more sources see the links section, for both generators and static documents.
Once you have the Terms of Service by filling in the document by following the instructions or by using the generator wizard
Optional
We now have the page created for your privacy policy and the titles we are going to use but we need content. Do not just go and copy and paste content from the internet as this violates copyright and the privacy policy would most likely not be correct for your website. There are many websites where you can get privacy policies for your website legally but I will outline the recommend ones below:
For more sources see the links section, for both generators and static documents.
Once you have the Privacy Policy by filling in the document by following the instructions or by using the generator wizard
If you are in the UK or the EU you need a Cookies Policy. You can either have this on the same page as the privacy policy as a section or a seperate policy or it can be on its own page. With any of these options the process is the same except how you alter the page and section titles as required.
For more sources see the links section, for both generators and static documents.
The DMCA allows communities to be protected from users posting copyrighted materials on the site as long as that website operates a DMCA policy to remove the content as per the rules they set down. It might be an obligation to show this via a page with a DMCA decalration on it. I am not sure whether is 100% required but it doe snot harm to have it on just incase.
If you operate a website or blog where you have member-posted content, you need to have a DMCA Takedown Notice on your website to help protect yourself from both copyright holders and your members.
Firstly aquire a policy:
For more sources see the links section, for both generators and static documents.
Now follow these simple instructions:
If you do receive a DMCA Takedown request, just take the material down straight away. These companies will have a lot money and resources than you.
My Selection
- DisclaimerTemplate.com
- really easy to edit
- not sure if it 100% compatible with UK law but it cannot harm to say you will remove the dodgy content
You have now added terms and conditions to your website with a decent organisation standard and hopefully some really good content. I would encourage to review your terms and conditions and privacy policy on a regular basis to make sure you are still compliant. If you are a good sized company, and you have completed this process perhaps get a lawyer to check everything out or supply some specific terms.
These are my instructions to get the Terms and Conditions and Privacy Policy I have used on this site from SEQ Legal, how to lay them out and why.
Firstly these instructions are my interactive website based in the UK.
Resources I used
Editing the SEQ Documents
This process should be done for all of the SEQ documents you are going to use on your site.
Once edited, we no need to transfer this into a html document/web page
Create page
Adding Privacy section/policy
Adding Cookies section/policy
1.2 By using our website and agreeing to this policy, you consent to our use of cookies in accordance with the terms of our Cookies Policy.
I installed the Cookie Consent ATOM from inspire theme for getting cookie consent notification.
I created a page as per the instructions above but added the following sentence (with hyperlink) so as to facilitate easier contact. I am not sure if this breaks the contract in any way but I would like the least amount of hassle, so i would just remove the offending content.
You may contact us via the contact form so we can deal with this issue.
I added the following clause so it is easier at a later date to expand my Terms & Conditions / Terms of Service. This statement also get the user to acknowledge that hyperlinked sections are part of the TOS.
This website is operated by Bob Marley (referred to as "QuantumWarp/we/our/us"). As user of this website (referred to as "you/your") you acknowledge that any use of this website including any transactions you make ("use/using") is subject to our terms and conditions below (which includes any other important hyper-linked sections e.g. Privacy & Cookies Policy.
The following policies form the Terms of Service, this is the contract between you and us for using our website or services and the purchasing of products or services.
Atticat Cookies Policy Generator
SEQ Privacy Policy
SEQ Terms and condition of Use
After building your Terms of Service / Terms and Conditions you might find that you need to add a few extra options, policies or terms that are not covered in my article but none the less are very useful. I will outline the ones I have come across here:
The following statement adds implicit acceptance of tetms by just usign the site and it also denotes hyperlinked sections are included in the terms by hyperlinking sections and further makes this clear by giving a short list of examples.
You can of course alter this to just promote either Implicit Consent or Hyperlinked Sections
This website is operated by Bob Marley (referred to as "QuantumWarp/we/our/us"). As user of this website (referred to as "you/your") you acknowledge that any use of this website including any transactions you make ("use/using") is subject to our terms and conditions below (which includes any other important hyper-linked sections e.g. How to use this website, Returns and refunds, and Privacy policy. In addition, you will find other useful information within Customer services. Please:
- read through these terms and conditions carefully before using this website.
- print a copy for future reference.
- also read our Privacy policy section regarding your personal information.
Not all Terms of Service will come with a clause like this or indeed any e-commerce clauses. This clause is a vehicle to be able to attach other terms and conditions to your primary Terms of Service document and still allow end users to explicitly except it. You can if needed, also hyperlink 'terms and conditions of supply' and/or alter the name of the document being included. You are also not limited to adding just this one clause, you can add further clauses like this or modified clauses following the same basic layout.
Transactions concluded through our site
Contracts for the supply of [goods OR services OR information] formed through our site or as a result of visits made by you are governed by our terms and conditions of supply.
If your company is located in the USA and/or your website is hosted in the USA (not 100% about this) then you need to comply with their rules and laws. I am not sure how international law matters are handled in reguards to this as to which regions laws are applied. These extra rules are mailny to do with the Privacy Policy, I am not sure if there are any specific changes required to the Terms of Service.
To make things easy you could add compliance for all of the laws and polices that are required for the USA (including the California ones) and this should not adversely you current privacy policy as these extra policies are usually on top of what is already there.
The extra Privacy Rules are:
You can read my 'Privacy Policy Notes' for further information or just use Google as these are well known rules. My advice would be to generate a Privacy Policy with freeprivacypolicy.com as this is upto date with all of the rules and is USA centric.
In this section I will add the notes I made but did not need to go in the instructions or elsewhere in this article.
Dont use your primary email for these online policy documents just incase spammers get hold of it, use something like privacy2015@quantumwarp.com and then you can change the year if needed without killing of your main email address.
I did these notes whilst using the freeprivacypolicy.com generator to make my Privacy Policy document with it.
Acceptance of terms was a major part of this article and now I want to explain a little more.
There are 2 types of acceptance:
There might be a difference between countries and then depending on what the terms are, you might be required to get explicit consent for the terms to of been deemed as accepted. There is no harm is having the terms implicitely accepted like Argos and then explicitely accepted by the use of a checkbox during registration to your site. This should cover both aspects.
I have read that in American (USA) courts unless a click is made (i.e. accept the terms and conditions checkbox) the acceptance is not likely to be upheld. This is one reason I am striving with these rules so that acceptance is upheld for both UK and US law. There might be times where implicit terms are accepted in the USA but i do not have any example sites for that.
Enable Acceptance of TOS in Joomla
Joomla is my platform of choice but this particular feature is hidden, Joomla enabling TOS articles:
I have not come across a plugin that will allow me to get users to re-accept the terms and conditions, but this would be useful, also Joomla calls it's terms, terms of use but when you sign up to Joomla it says accept Terms of service
Why a single page is important?
This is another central point of this article was to create rules for laying out the content so that all systems were able to accept the terms. What do I mean with this statement?
Most CMS systems such as Joomla or Wordpress have facilites to accept the Terms of Service (TOS) but you will only have the ability to select one page of terms. This is where my terms rules come into play as all of the different options allowing you to make a single page of terms that can be selected and then accepted. A single page allows you to get explicit consent for all of the terms you require.
The basic rule is that the Terms of Service page should clearly reference all other required terms and policies by hyperlinks with a paragraph saying to the end user that this is the case or that all of the other policies should be part of the singular Terms of Service policy.
Do you need to click to accept terms of sale?
If digital you cn just make this part of your TOS and then is hould not be an issue. When considering Bricks and Mortar businesses I think that you would need to reference it on your invoices and it would be Implicit consent you get because they would get the terms on the invoice after paying or could look them up on your website.
Twitch is an excellent example of a large site and how it handles it slegal documents so that is why I have given it a special mention here. Blow I will outline the main points:
When using the Twitch Services, you may be subject to any additional posted guidelines or rules applicable to specific services and features that may be posted online from time to time (the “Guidelines”). One example is Twitch's Community Guidelines. Twitch may also offer certain paid services, which are subject to the Twitch Terms of Sale as well as any additional terms or conditions that are disclosed to you in connection with such services. All such terms and guidelines are incorporated into these Terms of Service by reference.
By clicking Sign Up, you are indicating that you have read and agree to the Terms of Service and Privacy Policy
The following are links that I helped me figure out my rules and how everything worked.
If you really need to write your own terms and condtions then have a look through these guides.
Some people get confused between these two documents, I hope these helps.
Large companies are always changing their terms and conditions and these sites help you see what the terms mean and when they change.
There are some apps out there that you can use to build up a Cookie Policy along with what cookies are actually used. I am not 100% you have to do this anymore.
For all of us that do not have lots of money or are not lawyers these generators will be good enough, just give the content a read over once they are created. Lawyers want you to scare you in to spending money that you do not have. The internet is great you can get a document and print it out without ever needed a laywer. When you do earn enought money to warrant the laywer fees (or you can afford it ) then you should get one but until then use a generator.
Some of these generators are by well respected companies (ie. shopify) and if they were complete bollocks they would not offer them. It is true that a generic terms and conditions cannot cover all situations, btu most of them.
You should check the generated content as a lot of them are USA centric and need adapting for the EU and UK, in either case it is always good to read the output to make they are correct for your situation.
Some of the free sites give you free terms and consitions/privacy policys etc.. but leave out vital parts of the contract unless you pay. Some of these terms left out make them almost pointless so I have added these under paid.
Some websites offer excellent templates, free and paid, that require you to manually alter them giving you the same efect as a generator.
I used these to research this article and looked at how these lot did things.
While doing my research I had to assess a couple of sites and their products.
SEQ Legal
UKWADA
Their documents are only available if you are a member
Docular (free)
I looked into how prestashop handled acceptance of terms and here are my findings.
Terms of service I agree to the terms of service and will adhere to them unconditionally. (Read the Terms of Service) which teakes you to the 'terms and conditions of use' this sort of implies 1 terms and conditions page and only accepted when a purchase is made. perhaps a european thing.
I needed a hidden Gantry section so I could publish my Javascripts and modal code without creating a load of blanks space.
Solution
/* Hide Hidden Section - So i can paste scripts into it without adding blank space */ #g-hidden { height: 0px; }
So far it works but I have not extensively tested it.
Q: I can only disable comments via the options menu. There does not seem to be anyway to control it. How can I restrict comments to registered users?
A: There is no particular option for this case when using the built-in RSFeedback! commenting system. However, you can achieve this through template overrides. This procedure is explained here.
The file you need to perform template overrides is located under this path:
/components/com_rsfeedback/views/feedback/tmpl/default.php
Open the duplicate used for template overrides and search for:
<?php echo $this->comments->form;?>
Replace the above line with:
<?php $user = JFactory::getUser(); if($user->id != 0) { ?> <?php echo $this->comments->form;?> <?php } ?>
This article will go through a pratical example of adding Komento (v3.0.4) to a component, in this case RSFeedback (v1.5.13). I will cover the various aspects of the Koneto Plugin structure and then go into the actual integration and how all of these parts interact.
These are not easy to find unless you are looking for them but combined they really helped me make this article and my plugin.
This is my upgraded com_sample.php and I highly recommend reading through it as it explains alot of things in context.
<?php /** * @package Komento * @copyright Copyright (C) 2010 - 2016 Stack Ideas Sdn Bhd. All rights reserved. * @copyright Copyright (C) 2017 Jon Brown @ QuantumWarp.com * @license GNU/GPL, see LICENSE.php * Komento is free software. This version may have been modified pursuant * to the GNU General Public License, and as distributed it includes or * is derivative of works licensed under the GNU General Public License or * other free or open source software licenses. * See COPYRIGHT.php for copyright notices and details. */ // No direct access defined('_JEXEC') or die('Restricted access'); // Always load abstract class - This file also includes extra information about the functions here and the advanced functions not covered //require_once( JPATH_ROOT . DIRECTORY_SEPARATOR . 'components' . DIRECTORY_SEPARATOR . 'com_komento' . DIRECTORY_SEPARATOR . 'komento_plugins' . DIRECTORY_SEPARATOR .'abstract.php' ); require_once( JPATH_ROOT . '/components/com_komento/komento_plugins/abstract.php' ); class KomentoComsample extends KomentoExtension { /****************************************************** * * START * [BASIC FUNCTIONS (METHODS)] * These functions are mandatory * ******************************************************/ // This property (object) stores all the required properties by Komento public $_item; // This property (array) stores all the key mappings of the required item properties to map from Komento's default key to your component's custom key public $_map = array( // not needed with custom getContentId() 'id' => 'id_field', // not needed with custom getContentTitle() 'title' => 'title_field', // not needed with custom getContentHits() 'hits' => 'hits_field', // not needed with custom getAuthorId() 'created_by' => 'created_by_field', // not needed with custom getCategoryId() 'catid' => 'catid_field', // not needed with custom getContentPermalink() 'permalink' => 'permalink_field' ); // Constructor - Add all required files for your component here and run its constructor public function __construct( $component ) { // Load all required files by component // $this->addFile( your component's files ); // $this->addFile(JPATH_ADMINISTRATOR . '/components/com_sample/config.php'); // This must be left at the end of this constructor function parent::__construct( $component ); } // This method should load the article's main properties based on article ID public function load( $cid ) { static $instances = array(); if( !isset( $instances[$cid] ) ) { // populate $this->_item with: // id_field // title_field // hits_field // created_by_field // catid_field // permalink_field // Create a Database Object $db = KT::getDBO(); // Create SQL query to load a single article $query = 'SELECT `id_field`, `title_field`, `hits_field`, `created_by_field`, `catid_field` FROM `#__ARTICLE_TABLE` WHERE `ARTICLE_ID` = ' . $db->quote( $cid ); $db->setQuery( $query ); // Run the single article query and if there are no objects to load call the onLoadArticleError event if( !$this->_item = $db->loadObject() ) { return $this->onLoadArticleError( $cid ); } // Generate the permalink for this article $this->_item->permalink_field = 'index.php?options=com_sample&view=article&id=' . $this->_item->id_field; // Call the prepareLink function and leave the rest to us // Unless you have custom SEF methods, then use "getContentPermalink" function to overwrite $this->_item->permalink_field = $this->prepareLink( $this->_item->permalink_field ); $instances[$cid] = $this->_item; } $this->_item = $instances[$cid]; return $this; } // This method should load all the article IDs filtered by category IDs public function getContentIds( $categories = '' ) { // Create a Database Object $db = KT::getDBO(); // Make sure the query is empty $query = ''; // If no categories are supplied then load all article IDs if( empty( $categories ) ) { $query = 'SELECT `id_field` FROM `#__ARTICLE_TABLE` ORDER BY `id_field`'; } // If categories are supplied then load all article IDs for articles belonging to those categories else { if( is_array( $categories ) ) { $categories = implode( ',', $categories ); } $query = 'SELECT `id_field` FROM `#__ARTICLE_TABLE` WHERE `catid_field` IN (' . $categories . ') ORDER BY `id_field`'; } // Run the query and return the results as an array $db->setQuery( $query ); return $db->loadResultArray(); } // This method should load all the category IDs of the component // Make sure you select 'Single Level' or 'Nested' categories public function getCategories() { // Create a Database Object $db = KT::getDBO(); // Single Level Categories //$query = 'SELECT `id`, `title` FROM `#__CATEGORY_TABLE`'; // Nested Categories $query = 'SELECT `id`, `title`, `level`, `parent_id` FROM `#__CATEGORY_TABLE`'; // Run query and return categories $db->setQuery( $query ); $categories = $db->loadObjectList(); // Populate category tree for Komento Admin Integration Tab for this plugin (optional) // This is used in Komento where you select which categories the plugin should be active on etc... foreach( $categories as &$row ) { // Single Level Categories //$row->level = 0; // Nested Categories $repeat = ( $row->level - 1 >= 0 ) ? $row->level - 1 : 0; // Build and Add the Category Tree entry $row->treename = str_repeat( '.   ', $repeat ) . ( $row->level - 1 > 0 ? '|_ ' : '' ) . $row->title; } return $categories; } // This method lets Komento know if this is the front page or category layout (Determine if is listing view) public function isListingView() { $views = array('featured', 'category', 'categories', 'archive', 'frontpage' ); return in_array(JRequest::getCmd('view'), $views); } // This method lets Komento know if this is the page that the comment form should be displayed on (Determine if is entry view) public function isEntryView() { return JRequest::getCmd('view') == 'article'; } // This method is the main method that appends Komento on the article public function onExecute( &$article, $html, $view, $options = array() ) { // $html is the html content generated by komento (includes listing and form) // Select 1 of the following outputs // This appends the HTML to the article object //$article->text .= $html; //return; // Return the Komento HTML code return $html; } /****************************************************** * * END * [BASIC FUNCTIONS (METHODS)] * ******************************************************/ }
This is the actual Komento plugin for RSFeedback and is very useful for comparing with my QW_com_sample.php to see what the differences are.
<?php /** * @package Komento * @copyright Copyright (C) 2010 - 2016 Stack Ideas Sdn Bhd. All rights reserved. * @copyright Copyright (C) 2017 Jon Brown @ QuantumWarp.com * @license GNU/GPL, see LICENSE.php * Komento is free software. This version may have been modified pursuant * to the GNU General Public License, and as distributed it includes or * is derivative of works licensed under the GNU General Public License or * other free or open source software licenses. * See COPYRIGHT.php for copyright notices and details. */ // No direct access defined('_JEXEC') or die('Restricted access'); // Always load abstract class - This file also includes extra information about the functions here and the advanced functions not covered //require_once( JPATH_ROOT . DIRECTORY_SEPARATOR . 'components' . DIRECTORY_SEPARATOR . 'com_komento' . DIRECTORY_SEPARATOR . 'komento_plugins' . DIRECTORY_SEPARATOR .'abstract.php' ); require_once( JPATH_ROOT . '/components/com_komento/komento_plugins/abstract.php' ); class KomentoComrsfeedback extends KomentoExtension { /****************************************************** * * START * [BASIC FUNCTIONS (METHODS)] * These functions are mandatory * ******************************************************/ // This property (object) stores all the required properties by Komento public $_item; // This property (array) stores all the key mappings of the required item properties to map from Komento's default key to your component's custom key public $_map = array( // not needed with custom getContentId() 'id' => 'id', // not needed with custom getContentTitle() 'title' => 'title', // not needed with custom getContentHits() 'hits' => 'hits', // not needed with custom getAuthorId() 'created_by' => 'user_id', // not needed with custom getCategoryId() 'catid' => 'cat_id', // not needed with custom getContentPermalink() 'permalink' => 'permalink' ); // Constructor - Add all required files for your component here and run its constructor public function __construct( $component ) { parent::__construct( $component ); } // This method should load the article's main properties based on article ID public function load( $cid ) { static $instances = array(); if( !isset( $instances[$cid] ) ) { // populate $this->_item with: // id_field // title_field // hits_field // created_by_field // catid_field // permalink_field // Create a Database Object $db = KT::getDBO(); // Create SQL query to load a single article $query = 'SELECT `id`, `title`, `hits`, `user_id`, `cat_id` FROM `#__rsfeedback_feedbacks` WHERE `id` = ' . $db->quote( $cid ); $db->setQuery( $query ); // Run the single article query and if there are no objects to load call the onLoadArticleError event if( !$this->_item = $db->loadObject() ) { return $this->onLoadArticleError( $cid ); } // Generate the permalink for this article $this->_item->permalink_field = 'index.php?options=com_sample&view=article&id=' . $this->_item->id_field; // Call the prepareLink function and leave the rest to us // Unless you have custom SEF methods, then use "getContentPermalink" function to overwrite $this->_item->permalink = $this->prepareLink( $this->_item->permalink ); $instances[$cid] = $this->_item; } $this->_item = $instances[$cid]; return $this; } // This method should load all the article IDs filtered by category IDs public function getContentIds( $categories = '' ) { // Create a Database Object $db = KT::getDBO(); // Make sure the query is empty $query = ''; // If no categories are supplied then load all article IDs if( empty( $categories ) ) { $query = 'SELECT `id` FROM `#__rsfeedback_feedbacks` ORDER BY `id`'; } // If categories are supplied then load all article IDs for articles belonging to those categories else { if( is_array( $categories ) ) { $categories = implode( ',', $categories ); } $query = 'SELECT `id` FROM `#__rsfeedback_feedbacks` WHERE `cat_id` IN (' . $categories . ') ORDER BY `id`'; } // Run the query and return the results as an array $db->setQuery( $query ); return $db->loadResultArray(); } // This method should load all the category IDs of the component // Make sure you select 'Single Level' or 'Nested' categories public function getCategories() { // Create a Database Object $db = KT::getDBO(); // Single Level Categories $query = 'SELECT `id`, `name` FROM `#__rsfeedback_categories`'; // Run query and return categories $db->setQuery( $query ); $categories = $db->loadObjectList(); // Populate category tree for Komento Admin Integration Tab for this plugin (optional) // This is used in Komento where you select which categories the plugin should be active on etc... foreach( $categories as &$row ) { // Single Level Categories $row->level = 0; // Build and Add the Category Tree entry $row->treename = str_repeat( '.   ', $repeat ) . ( $row->level - 1 > 0 ? '|_ ' : '' ) . $row->name; } return $categories; } // This method lets Komento know if this is the front page or category layout (Determine if is listing view) public function isListingView() { $views = array('category', 'categories', 'feedbacks'); return in_array(JRequest::getCmd('view'), $views); } // This method lets Komento know if this is the page that the comment form should be displayed on (Determine if is entry view) public function isEntryView() { return JRequest::getCmd('view') == 'feedback'; } // This method is the main method that appends Komento on the article public function onExecute( &$article, $html, $view, $options = array() ) { // $html is the html content generated by komento (includes listing and form) // Return the Komento HTML code return $html; } /****************************************************** * * END * [BASIC FUNCTIONS (METHODS)] * ******************************************************/ }
If you have gone through the resources above (recommend) you will see that the functions are grouped into:
The plugin can also be placed into either of the following locations and note that the name is changed depending where you put it.
The objects declared at the top of the plugin $_item and $_map are self explanatory.
I will now go through the functions in the order they appear in the integration plugin. My notes will give you more of a practical insight into how they work. where an example is needed I will use my RSFeedback itegration code.
The QW_com_sample.php is fully annotated so if I missed anything just use that as a reference.
This function is where you include and files/dependencies that you need from the component, if any. This function then runs KT class constructor with the component name as a option so it has the relevant objects and resources for that component.
The parent::__construct( $component ); must be run at the end of the function so all the relevant assets have been called before it is intialised.
This function loads a single article into the $_item object using the mappings we defined in $_map.
This is where the configuration fun begins. There are several parts to this function that need to be dealt with.
QW Sample Version
// This method should load the article's main properties based on article ID public function load( $cid ) { static $instances = array(); if( !isset( $instances[$cid] ) ) { // populate $this->_item with: // id_field // title_field // hits_field // created_by_field // catid_field // permalink_field // Create a Database Object $db = KT::getDBO(); // Create SQL query to load a single article $query = 'SELECT `id_field`, `title_field`, `hits_field`, `created_by_field`, `catid_field` FROM `#__ARTICLE_TABLE` WHERE `ARTICLE_ID` = ' . $db->quote( $cid ); $db->setQuery( $query ); // Run the single article query and if there are no objects to load call the onLoadArticleError event if( !$this->_item = $db->loadObject() ) { return $this->onLoadArticleError( $cid ); } // Generate the permalink for this article $this->_item->permalink_field = 'index.php?options=com_sample&view=article&id=' . $this->_item->id_field; // Call the prepareLink function and leave the rest to us // Unless you have custom SEF methods, then use "getContentPermalink" function to overwrite $this->_item->permalink_field = $this->prepareLink( $this->_item->permalink_field ); $instances[$cid] = $this->_item; } $this->_item = $instances[$cid]; return $this; }
Create SQL query to load a single article
This query needs to be changed to load a single articles record for the components database. You can change the field names to match the components which can be found by looking at your database, locating the article table for your component and then rading the fild names from the top.
// Create SQL query to load a single article $query = 'SELECT `id_field`, `title_field`, `hits_field`, `created_by_field`, `catid_field` FROM `#__ARTICLE_TABLE` WHERE `ARTICLE_ID` = ' . $db->quote( $cid ); $db->setQuery( $query );
So in the case of RSFeedback the code above will be changed to
// Create SQL query to load a single article $query = 'SELECT `id`, `title`, `hits`, `user_id`, `cat_id` FROM `#__rsfeedback_feedbacks` WHERE `id` = ' . $db->quote( $cid ); $db->setQuery( $query );
You will note that #__ is a Joomla substitution string for the table prefix and is absolutely needed. You now have configured the article to be loaded as needed except for the permalink. There might be some components that store their permalink in the articles record and if so just add the extra clause into the SQL statement above.
The Komento plugin for com_content.php has a more advanced article lookup where the various items are stored in different tables.
A permalink is the Non-SEF URL that Joomla uses to load pages and pretty much do most things.
To see a components permalink you need to turn off Search Engine Friendly URLs and Use URL Rewriting in Joomla's config and then browse to an article or category page for that component.
Menu Item Type Examples: RSFeedback - Add Category https://quantumwarp.com/index.php?option=com_rsfeedback&view=category&layout=edit&Itemid=1027 RSFeedback - Add Feedback https://quantumwarp.com/index.php?option=com_rsfeedback&view=feedback&layout=edit&Itemid=1027 RSFeedback Feedbacks View (List all Feedbacks) https://quantumwarp.com/index.php?option=com_rsfeedback&view=feedbacks&Itemid=1027 RSFeedback - List Categories https://quantumwarp.com/index.php?option=com_rsfeedback&view=categories&Itemid=1027 RSFeedback - Single Feedback https://quantumwarp.com/index.php?option=com_rsfeedback&view=feedback&id=2&Itemid=1027 Browsing Examples: RSFeedback Single Category https://quantumwarp.com/index.php?option=com_rsfeedback&view=feedbacks&cat_id=1&Itemid=1027
The Itemid=1027 - This is the menu item that my RSFeedback is displayed under. The other varibles are all straight forward.
Now we know what we need to build for Komento to understand what page to dispaly the comments on lets get on with it. there are several different methods that I have come across for build the permalink and I will list them below.
This is bar far the easiest and is the one that is in QW_com_sample
// Generate the permalink for this article $this->_item->permalink_field = 'index.php?options=com_sample&view=article&id=' . $this->_item->id_field; // Call the prepareLink function and leave the rest to us // Unless you have custom SEF methods, then use "getContentPermalink" function to overwrite $this->_item->permalink_field = $this->prepareLink( $this->_item->permalink_field );
As you can see it is literally is the permalink written out with the articles ID dynamically added at the end and then processed through a Komento function. The RSFeedback version lools like:
// Generate the permalink for this article $this->_item->permalink_field = 'index.php?options=com_sample&view=article&id=' . $this->_item->id_field; // Call the prepareLink function and leave the rest to us // Unless you have custom SEF methods, then use "getContentPermalink" function to overwrite $this->_item->permalink = $this->prepareLink( $this->_item->permalink );
As you can see they are exactly the same. RSFeedback uses simple permalink for its feedbacks. You will see we did not have to add the menu's Itemid as the compoenent already takes care of this. Not all compoenents will have a simple permalink like RSFeedback.
This example is taken from the JDownloads Komento integration plugin that ships in the Komento package and is in the folder /komento_plugins/ .
This function's output will override $_map->permalink but as you can see it allows for a more complex permalink to be built from various data sources much the same as JDownloads will build the links itself.
JDownloads Plugin Version
public function getContentPermalink() { $link = 'index.php?option=' . $this->component . ''; $pieces = array( 'option=' . $this->component, 'Itemid=' . $this->getItemId(), 'view=download', 'catid=' . $this->getCategoryId(), 'id=' . $this->getContentId() ); $link = $this->prepareLink( 'index.php?' . implode( '&', $pieces ) ); return $link; }
Example permalink:
JDownloads Single Download permalink https://quantumwarp.com/index.php?option=com_jdownloads&view=download&id=41:stackideas&catid=19&Itemid=859
I have not used this method so you will need to experiment, but it should not be that difficult now that you know what this 'Extended Function' does
So the function for RSFeedback is:
// This method should load the article's main properties based on article ID public function load( $cid ) { static $instances = array(); if( !isset( $instances[$cid] ) ) { // populate $this->_item with: // id_field // title_field // hits_field // created_by_field // catid_field // permalink_field // Create a Database Object $db = KT::getDBO(); // Create SQL query to load a single article $query = 'SELECT `id`, `title`, `hits`, `user_id`, `cat_id` FROM `#__rsfeedback_feedbacks` WHERE `id` = ' . $db->quote( $cid ); $db->setQuery( $query ); // Run the single article query and if there are no objects to load call the onLoadArticleError event if( !$this->_item = $db->loadObject() ) { return $this->onLoadArticleError( $cid ); } // Generate the permalink for this article $this->_item->permalink_field = 'index.php?options=com_sample&view=article&id=' . $this->_item->id_field; // Call the prepareLink function and leave the rest to us // Unless you have custom SEF methods, then use "getContentPermalink" function to overwrite $this->_item->permalink = $this->prepareLink( $this->_item->permalink ); $instances[$cid] = $this->_item; } $this->_item = $instances[$cid]; return $this; }
Again I have not used this method but I noticed that it used a router file (route.php). This example uses the com_content.php (Joomla Content) integration plugin that ships with Komento and is in the folder /komento_plugins/ .
Firstly the router file route.php for the component (com_content is actually a component in Joomla that is responsible for your articles)
public function __construct($component) { // Add com_content's router file $file = JPATH_ROOT . '/components/com_content/helpers/route.php'; $this->addFile($file); parent::__construct($component); }
It has a much more advanced getContentPermalink() function that utilises the route.php (router file for the com_content component)
public function getContentPermalink() { $slug = $this->_item->alias ? ($this->_item->id.':'.$this->_item->alias) : $this->_item->id; $catslug = $this->_item->category_alias ? ($this->_item->catid.':'.$this->_item->category_alias) : $this->_item->catid; $parent_slug = $this->_item->category_alias ? ($this->_item->parent_id.':'.$this->_item->parent_alias) : $this->_item->parent_id; $link = ContentHelperRoute::getArticleRoute($slug, $catslug); $link = $this->prepareLink($link); return $link; }
This function will load all article IDs for the specified categories. I am not 100% this is used in RSFeedback but anyway.
We need to update the SQL so it loads article IDs for the specified catgories.
QW Sample Version
// This method should load all the article IDs filtered by category IDs public function getContentIds( $categories = '' ) { // Create a Database Object $db = KT::getDBO(); // Make sure the query is empty $query = ''; // If no categories are supplied then load all article IDs if( empty( $categories ) ) { $query = 'SELECT `id_field` FROM `#__ARTICLE_TABLE` ORDER BY `id_field`'; } // If categories are supplied then load all article IDs for articles belonging to those categories else { if( is_array( $categories ) ) { $categories = implode( ',', $categories ); } $query = 'SELECT `id_field` FROM `#__ARTICLE_TABLE` WHERE `catid_field` IN (' . $categories . ') ORDER BY `id_field`'; } // Run the query and return the results as an array $db->setQuery( $query ); return $db->loadResultArray(); }
Dont forget to get the new field names by using phpMyAdmin to find out where the categories are stored and then work out the new SQL statement.
RSFeedback Version
// This method should load all the article IDs filtered by category IDs public function getContentIds( $categories = '' ) { // Create a Database Object $db = KT::getDBO(); // Make sure the query is empty $query = ''; // If no categories are supplied then load all article IDs if( empty( $categories ) ) { $query = 'SELECT `id` FROM `#__rsfeedback_feedbacks` ORDER BY `id`'; } // If categories are supplied then load all article IDs for articles belonging to those categories else { if( is_array( $categories ) ) { $categories = implode( ',', $categories ); } $query = 'SELECT `id` FROM `#__rsfeedback_feedbacks` WHERE `cat_id` IN (' . $categories . ') ORDER BY `id`'; } // Run the query and return the results as an array $db->setQuery( $query ); return $db->loadResultArray(); }
This function is used to build the category tree in Komento Integration admin so you can select what categories you want Kommento to work on.
This is quite an important function and you need to know whether your component has a 'single level' of categories or 'nested' categories (most joomla components do). The sample function below is rigged up for nested categories and might require the help of an additional function such as setLevel( $pid, $level, $categories, &$result ) to determine level. See the integration plugin com_jdownloads.php on how it was done as I dont think this example in QW_com_sample.php is complete.
QW Sample Version
// This method should load all the category IDs of the component // Make sure you select 'Single Level' or 'Nested' categories public function getCategories() { // Create a Database Object $db = KT::getDBO(); // Single Level Categories //$query = 'SELECT `id`, `title` FROM `#__CATEGORY_TABLE`'; // Nested Categories $query = 'SELECT `id`, `title`, `level`, `parent_id` FROM `#__CATEGORY_TABLE`'; // Run query and return categories $db->setQuery( $query ); $categories = $db->loadObjectList(); // Populate category tree for Komento Admin Integration Tab for this plugin (optional) // This is used in Komento where you select which categories the plugin should be active on etc... foreach( $categories as &$row ) { // Single Level Categories //$row->level = 0; // Nested Categories $repeat = ( $row->level - 1 >= 0 ) ? $row->level - 1 : 0; // Build and Add the Category Tree entry $row->treename = str_repeat( '.   ', $repeat ) . ( $row->level - 1 > 0 ? '|_ ' : '' ) . $row->title; } return $categories; }
JDownloads Plugin (com_jdownloads.php)
public function getCategories() { $sql = KT::sql(); $sql->select( '#__jdownloads_categories' ) ->column( 'id', 'id' ) ->column( 'title', 'title' ) ->column( 'parent_id' ) // ->where( 'published', 1 ) ->order( 'ordering' ); $categories = $sql->loadObjectList(); $result = array(); $this->setLevel( 0, 0, $categories, $result ); return $result; } private function setLevel( $pid, $level, $categories, &$result ) { foreach( $categories as &$category ) { if( (int) $category->parent_id === (int) $pid ) { $category->level = $level; $category->treename = str_repeat( '.   ', $level ) . ( $level > 0 ? '|_ ' : '' ) . $category->title; $result[] = $category; $this->setLevel( $category->id, $level + 1, $categories, $result ); } } }
RSFeedback has a very simple category storage as it is only a single level and not nested. If categories are in a nested format look at the com_content.php plugin or any of the other pluign files for examples.
I have altered the sample code just by
$row->level = 0; - This is to always set a level because this does not exist in a single level category system and it keeps the category tree building routine the same. You could in theory remove it but would make the code a bit messy ans should RSFeedback ever be upgraded to nested categories it would make sthings a lot easier to figure out.
RSFeedback Version
// This method should load all the category IDs of the component // Make sure you select 'Single Level' or 'Nested' categories public function getCategories() { // Create a Database Object $db = KT::getDBO(); // Single Level Categories $query = 'SELECT `id`, `name` FROM `#__rsfeedback_categories`'; // Run query and return categories $db->setQuery( $query ); $categories = $db->loadObjectList(); // Populate category tree for Komento Admin Integration Tab for this plugin (optional) // This is used in Komento where you select which categories the plugin should be active on etc... foreach( $categories as &$row ) { // Single Level Categories $row->level = 0; // Build and Add the Category Tree entry $row->treename = str_repeat( '.   ', $repeat ) . ( $row->level - 1 > 0 ? '|_ ' : '' ) . $row->name; } return $categories; }
This method lets Komento know if this is the front page or category layout (Determine if is listing view)
This is very simple to understand. We need to define what pages are categories and what are articles. This particular function tells Komento what are category or listing style pages and basically dont show commenting system on these.
QW Sample Version
// This method lets Komento know if this is the front page or category layout (Determine if is listing view) public function isListingView() { $views = array('categories', 'category', 'feedbacks'); return in_array(JRequest::getCmd('view'), $views); }
All you need to do is find the permalinks (addressed earlier) for all of the pages you do not want to show comments on and add them into the views array. I would do this by creating a menu item for every type of option available for RSFeedback and make a list of the permalinks into 2 groups, those you want comments on (should just be single feedback page) and a list of those you so not want comments on.
Of these you do not want comments on look at the permalink and you will see a GET parameter view=xxxx where xxxx is the view type. Add all of these view types into the $views = array() statement. Does not matter if there are duplicates just add the view type in once.
RSFeedback Version
// This method lets Komento know if this is the front page or category layout (Determine if is listing view) public function isListingView() { $views = array('category', 'categories', 'feedbacks'); return in_array(JRequest::getCmd('view'), $views); }
This is the opposite of above. This method lets Komento know if this is the page that the comment form should be displayed on (Determine if is entry view).
Basically follow the procedure from above but reverse it selecting only the views that you want comments on. I dont know why you need to specify view types you want comments on and not on rather that just the pages you want them on but i am sure there is a reason. There usually is only type of page(view) you want the comments to be displayed on.
QW Sample Version
// This method lets Komento know if this is the page that the comment form should be displayed on (Determine if is entry view) public function isEntryView() { return JRequest::getCmd('view') == 'article'; }
RSFeedback Verison
// This method lets Komento know if this is the page that the comment form should be displayed on (Determine if is entry view) public function isEntryView() { return JRequest::getCmd('view') == 'feedback'; }
With RSFeedback there is only 1 page type you want to display comments on, the Single feedback page.
public function onExecute( &$article, $html, $view, $options = array() )
This function outputs the HMTL code upon execution. You can also use it to append code to an article object. Tehre is not much more to this fuction.
QW Sample Version
// This method is the main method that appends Komento on the article public function onExecute( &$article, $html, $view, $options = array() ) { // $html is the html content generated by komento (includes listing and form) // Select 1 of the following outputs // This appends the HTML to the article object //$article->text .= $html; //return; // Return the Komento HTML code return $html; }
I uses the straight HMTL output for RSFeedback because the code in RSFeedback is geared up to recieve the HTML rather being added to the article object.
RSFeedback Version
// This method is the main method that appends Komento on the article public function onExecute( &$article, $html, $view, $options = array() ) { // $html is the html content generated by komento (includes listing and form) // Return the Komento HTML code return $html; }
We have now been through the basics of the Komento plugin with an emphasis on RSFeedback but these instruction like those before can be easily applied to other components.
RSFeedback supports several commenting systems out of the box (Inbuilt Comment System / RSComments / JComments / Jom Comments) but not Komento. Komento will have to be added into RSFeedback by altering some of it's core code.
Make sure you have backups and do all of this work on a test website first before working on your live site.
Because RSFeedback already has these comment systems installed I extracted the files and then did a text search to search for 'rscomments' and this return the following 3 files.
Extracted Installation Package Location
Location when installed in Joomla
Purpose of files
NB: File 3 does not make anydifference but is include for completeness
We now have the location in the code where we need to make changes you should go and have a quick look at the files to see how the comment systems are integrated. With systems like this is it is failry easy to add another commenting system as there will be standard that adds each commenting system in the same way, there will just be a slight difference in the final call to the commenting system and the included files that are need to call the commenting system.
This file adds the commenting systems as selections in the RSFeedback Joomla admin.
{add picture here}
Add the bottom of each of the groups of code I have simply added an entry for Komento giving it the option number 5, this is important for the next changes as it is the reference number we will be using for the Konto commenting system in RSFeeedback.
The following line is added to the bottom of the first group and is used as a boolean check to see if Komento is installed. You could probably use bootstrap.php instead but I am not sure about this and is not that important. Using koment.php certainly makes it easier to follow
$komento = file_exists(JPATH_SITE.'/components/com_komento/komento.php');
The second group of code in the fuction builds up the array for the RSFeedback admin and returns the results. Commenting systems that are not present will appear greyed out. Add the following line:
$commentsystem[] = JHTML::_('select.option', '5', JText::_('Komento') , 'value' , 'text', !$komento);
This will give you the full file shown below:
/** * @package RSFeedback! * @copyright (C) 2010-2014 www.rsjoomla.com * @license GPL, http://www.gnu.org/copyleft/gpl.html */ defined('_JEXEC') or die('Restricted access'); jimport('joomla.form.formfield'); class JFormFieldCommentingSystems extends JFormField { protected $type = 'CommentingSystems'; public function getInput() { $jcomment = file_exists(JPATH_SITE.'/components/com_jcomments/jcomments.php'); $jomcomment = file_exists(JPATH_SITE.'/plugins/content/jom_comment_bot.php'); $rscomment = file_exists(JPATH_SITE.'/components/com_rscomments/rscomments.php'); $komento = file_exists(JPATH_SITE.'/components/com_komento/komento.php'); $commentsystem = array(); $commentsystem[] = JHTML::_('select.option', '0', JText::_( 'COM_RSFEEDBACK_COMMENTS_DISABLED' ) ); $commentsystem[] = JHTML::_('select.option', '1', JText::_( 'COM_RSFEEDBACK_DEFAULT_COMMENTS' ) ); $commentsystem[] = JHTML::_('select.option', '2', JText::_('RSComments!') , 'value' , 'text', !$rscomment); $commentsystem[] = JHTML::_('select.option', '3', JText::_('JComments') , 'value' , 'text' , !$jcomment); $commentsystem[] = JHTML::_('select.option', '4', JText::_('Jom Comments') , 'value' , 'text', !$jomcomment); $commentsystem[] = JHTML::_('select.option', '5', JText::_('Komento') , 'value' , 'text', !$komento); $html = JHTML::_('select.genericlist', $commentsystem, 'jform[feedback_commenting]', '', 'value', 'text', $this->value); return $html; } }
Find the public static function DisplayComments($id) and add the following code at the end of the switch list. You will note it is option 5.
This is the code that will return the HTML from Komento (which has the comments for that Feedback item and the new comment form etc..) which is then assigned to the $comments->listing object which is the generic container RSFeedback uses to hold the comments from the various systems and is then this object that gets rendered below the Feedback giving you the comments.
case '5' : require_once( JPATH_ROOT . '/components/com_komento/bootstrap.php' ); $comments->listing = KT::commentify('com_rsfeedback', $id); $comments->form = ''; break;
This is a very similiar arrangement to rsfeedback.php except it is not used anywhere and all the code is within public function getComments(). The code here is slightly different but would do the same job if it was used.
add the following lines at the end of the switch list in the function public function getComments().
//Komento integration case 5: if (file_exists(JPATH_ROOT . '/components/com_komento/bootstrap.php')) : require_once(JPATH_ROOT . '/components/com_komento/bootstrap.php'); $comments .= KT::commentify('com_rsfeedback', $this->data->IdFeedback, $options); endif; break;
Firstly all of these are referenced in:
{Joomla Route}/administrator/components/com_komento/includes/komento.php
What is this all about. KT:: is the new name for the Komento class and it is this that should be used going forwards when making plugins even thought the official instructions (at time of writing) tell you to use Komento:: which is confusing. However the following code at the bottom of komento.php allows the legacy use of these class names.
class KMT extends KT {} class Komento extends KT {}
So going forwards just use KT:: so for example
require_once(JPATH_ROOT . '/components/com_komento/bootstrap.php'); Komento::commentify('com_sample', $article, $options);
should be
require_once(JPATH_ROOT . '/components/com_komento/bootstrap.php'); KT::commentify('com_sample', $article, $options);
NB: You can call KT:commentify() because it is a static function. I will not go into this further but you might notice a couple of these in the code.
We need to get the comments count from comment and feed this information back to RSFeedback so it can be displayed.
The function that Komento uses is getCount($component, $cid), in the KT:: class, in the file administrator/components/com_komento/includes/komento.php approx line 1355
$commentsModel = KT::model('Comments'); // Create a Comments object $commentCount = $commentsModel->getCount($component, $cid); // Return the Comment count
The function RSFeedback uses to display the comment count is getFeedbackCommentsCount($id) in the file components/com_rsfeedback/helpers/rsfeedback.php approx line 189
Again with previous codes there is a switch choice and we just need to add the Komento code in as option 5.
case '5' : require_once( JPATH_ROOT . '/components/com_komento/bootstrap.php' ); // Intialize Komento $commentsModel = KT::model('Comments'); // Create a Comments object $comments = $commentsModel->getCount('com_rsfeedback', $id); // Return the Comment count break;
The last thing to do is translate the new Komento Integration Tab and the dropdown menu option used in various places for our plugin in Komento by adding these translation strings.
COM_KOMENTO_COM_RSFEEDBACK="RSFeedback" COM_KOMENTO_SETTINGS_TAB_COM_RSFEEDBACK_SETTINGS="RSFeedback"
Thre are 2 ways of adding the translation string into joomla
We have now completed all of the steps required to integrate Komento into RSFeedback and have covered most aspects of this process. So he is a quick recap of what to do.
I am unable to directly move the database from my skeleton template on my staging server to a new target server. This is due to my staging server being newer and using MariaDB than the target server and that the default table type when using MariaDB is 'Aria' which the target server does not support.
Results
All of these exported files fail to import on the target with the following SQL error when importing in phpMyAdmin
Error SQL query: CREATE TABLE `xxxx_imageshow_external_source_picasa` ( `external_source_id` int( 11 ) unsigned NOT NULL AUTO_INCREMENT , `external_source_profile_title` varchar( 255 ) DEFAULT NULL , `picasa_username` varchar( 255 ) DEFAULT '', `picasa_thumbnail_size` char( 30 ) DEFAULT '144', `picasa_image_size` char( 30 ) DEFAULT '1024', PRIMARY KEY ( `external_source_id` ) ) ENGINE = Aria DEFAULT CHARSET = utf8 PAGE_CHECKSUM =1; MySQL said: Documentation #1064 - You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'PAGE_CHECKSUM=1' at line 8
If you use an intermediate server such as xampp you can convert the 'Aria' tabels in to 'MyISAM' which will then allow for a successful import.
Instructions
The skeleton database is now migrated and there are no 'Aria' type tables as the have all been converted to 'MYISAM'. You should also note that when using xampp on Windows all table names are changed to lowercase.
If you export the database in phpMyAdmin rather than exporting the tables there will be a database create rule and some header code is missing.
Error SQL query: -- -- Database: `skellyimport` -- CREATE DATABASE IF NOT EXISTS `skellyimport` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; MySQL said: Documentation #1044 - Access denied for user 'futured1'@'localhost' to database 'skellyimport'
To fix this, edit the SQL file and remove the following before importing
-- -- Database: `skellyimport` -- CREATE DATABASE IF NOT EXISTS `skellyimport` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; USE `skellyimport`; -- --------------------------------------------------------
Error SQL query: /*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; MySQL said: Documentation #1231 - Variable 'character_set_client' can't be set to the value of 'NULL'
The missing headers causes this error when importing into the target server. I am not sure what they do. But the difference between the phpMyAdmin Database and Table exports seems to be:
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; /*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; /*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; /*!40101 SET NAMES utf8mb4 */;
I also did a quick google search and found the following articles on the subject saying the same thing that the missing headers is what causes this particular error
Links
When you run RSSOwl it just never starts and no process is spawned.
The cause and the fix are easy.
Cause = RSSOwl requires Javascript 32-bit installed
Fix = Install Javascript 32 bit
Links
This is an email I sent with information on caching by htaccess.
Hi Vlado
Just a quick update and the information on caching I have.
I mentioned that the jotcache component showed entries even after I had emptied the joomla cache. You advised me to hit refresh. This worked. Perhaps an automatic refresh when the component is launched might help.
The Expires plugin fails with the normal joomla cache as well so I will contact the plugin author about this as it appears to be a fault or missing feature with this plugin.
Cache
I am still experimenting with cache for my website. I do not know how much you know about this so if my reply is basic please don’t be offended.
I am currently setting everything to one hour so I can test things without leaving unwanted cached items on peoples browsers. I intend to move these to 1 day when I have finshed developing. Google prefers expires (because more compatible) and to have them set to one month, this might have an effect on SEO.
My cache is set both in the htaccess file and in the expires plugin and jotcache (different type of cache and you already know its feature and power)
htacess
This is the rule I am working with at the minute, I have included both types of cache for testing but cache-control overrides expires anyway. I have also put them inside a filesmatch rather than globally, this would be fine aswell. last modified is unset because I am using cache-control to set freshness of the files and it is an overhead that is not needed. This assume fairly static content. However if you set a max-age of only an hour it is not really an issue a user having out of date content
## My Cache Rule - check whether ExpiresActive is required here # All specified files can be cached in public/private proxies and browsers etc..and when it expires must revalidate <FilesMatch "\.(ico|pdf|flv|gif|jpg|JPG|jpeg|JPEG|png|PNG|swf|css|js|js\.gz|gz|htm|html|xml|txt)$"> Header set Cache-Control "max-age=3600, public, must-revalidate" ExpiresActive On ExpiresDefault "now plus 1 hour" Header unset Last-Modified </FilesMatch>
This rule set below is the same as above but the cache control is slightly different, it has a private option set, this means that no public proxy or cache can store this data, only a cache that the user has private access to ie internet explorer cache
## My Shop Cache Rule - check whether ExpiresActive is required here # All specified files can be cached in browsers but not public caches/proxies etc..and when it expires must revalidate # bbc.co.uk uses private <FilesMatch "\.(ico|pdf|flv|gif|jpg|JPG|jpeg|JPEG|png|PNG|swf|css|js|js\.gz|gz|htm|html|xml|txt)$"> Header set Cache-Control "max-age=3600, private, must-revalidate" ExpiresActive On ExpiresDefault "now plus 1 hour" Header unset Last-Modified </FilesMatch>
You mention in your email - (a problem with this at the minute is joomla does not set a Last Modified Header, I need to look in to this further but the theory still stands)
For general usage I think here is necessary that browser at least make a ping to server and this one responds with 304 code.
This can be achived better by using a cache control setting off
Header set Cache-Control "max-age=3600, no-store, public, must-revalidate"
The no-store actually means that the browser cannot use the content in its browser without first revalidating the content with the original source. However this method with not depend on the session still being open like with the 'Use Browser Cache option'. When using this either ETag or Last Modified header need to be set, as ETAGs are problematic everyone uses Last Modified Header, so in this case I would not unset Last Modified asa it is needed to revalidate content.
My settings that I am will be testing on my Expires plugin:
1 hour, Public, Must Revalidate
I will also give you these links which I found very helpful in understanding cache:
I hope this helps. I am currently writing a htaccess files with a really good cache section in it. If you would like a copy of it I can send it you. I would send it now but I don’t want the email to end up in spam.
One last question. Does joomlas cache/jotcache store html headers in the cache or just the html page content?
Thanks
Shoulders
The margin property can have from one to four values.
This issue came about because I needed to image some Atari ST floppy disks and the only OS that would run the specialised Makedisk App was XP. I loaded XP in a Vitrual Box and then plugged in my USB floppy drive and mounted it. The floppy drive then mounted as B: . It did this becasue I had a virtual floppy disk as part of the vitual machine so the external floppy disk drive then took the next letter which is B:
Makedisk would probably work with B: drive but annoyed me. I thenshutdown the virtual machine and then rebooted. The USB floppy drive still shows as B: because it is registered in the windows registry.
This issue is not just related to virtual machines nor XP but I have verified the solutions on XP. This assumes your USB floppy drive come up as B:
HKEY_LOCAL_MACHINE\SYSTEM\MountedDevices
\DosDevices\B:
This is one of the most confusing things in copyright law for the lay person but I will try and explain it here.
The "fair use" exemption to (U.S.) copyright law was created to allow things such as commentary, parody, news reporting, research and education about copyrighted works without the permission of the author. That's vital so that copyright law doesn't block your freedom to express your own works -- only the ability to appropriate other people's. Intent, and damage to the commercial value of the work are important considerations.
Links