Changeset 728099
- Timestamp:
- 06/18/2013 11:35:41 PM (13 years ago)
- Location:
- dlbs-send-a-link/trunk
- Files:
-
- 1 added
- 6 edited
-
dsl-captcha.php (added)
-
dsl-page.html (modified) (5 diffs)
-
dsl-templates.html (modified) (4 diffs)
-
dsl.css (modified) (2 diffs)
-
dsl.js (modified) (9 diffs)
-
dsl.php (modified) (29 diffs)
-
readme.txt (modified) (6 diffs)
Legend:
- Unmodified
- Added
- Removed
-
dlbs-send-a-link/trunk/dsl-page.html
r726535 r728099 2 2 * Page template for dlb's Send-A-Link (dsl) 3 3 * 4 * This file is based onpage.php from the theme folder.4 * This file should be based on the structure seen in file page.php from the theme folder. 5 5 * 6 6 * It includes the form in one div and the confirmation in a separate div. 7 * The confirm div will initially be hidden with style display:none;. 8 * Javascript later shows the confirm with style display:block; as it hides 9 * the form with style display:none;. 7 * The confirm div is initially hidden with style display:none;. 8 * Later it is shown with style display:block as the form is hidden with style display:none;. 10 9 * 11 * This file is used by dsl.php, and is expected to be found 12 * in the same folder as dsl.php. To locate it elsewhere, change 13 * the DSL_OTHER_TEMPLATES definition in the configuration section of dsl.php. 10 * This file is used by dsl.php, and is expected to be found 11 * in the same folder as dsl.php. 14 12 * 15 * Placeholders available are as follows. To use, they must be enclosed in #'s:13 * The following placeholders are available. To use, they must be enclosed in #'s: 16 14 * - dsl-post-title 17 15 * - dsl-recipient-name … … 39 37 <div id="content"> 40 38 <!--***************************************************** 41 * The form template starts here 39 * The form template starts here. 40 All the hidden fields are grouped in the first paragraph. 41 The unusual label on the hidden "dsl_pid" field is to handle a general form-level error message. 42 The empty spans in the labels are to accept error messages specific to the corresponding inputs. 43 Although text can be customized, the arrangement of the markup is important, 44 including the order of attributes within the tags. 42 45 --> 43 46 <div class="post" id="dsl-form-div"> … … 48 51 <form id="dsl-form" action="" method="post" name="dsl-form" onsubmit="dslSend(this); return false;"> 49 52 <p> 50 <input type="hidden" name="dsl_nonce", value="#wp-nonce#" /> 51 <input type="hidden" name="dsl_pid" value="#dsl-post-id#" /> 52 <input type="hidden" name="dsl_secure", value="" /> 53 <input type="hidden" name="dsl_captcha_random" value="#captcha-random#" /> 54 <input type="hidden" name="dsl_back", value="#dsl-back-link#" /> 53 <input name="dsl_nonce" type="hidden" value="#wp-nonce#" /> 54 <input name="dsl_secure" type="hidden" value="" /> 55 <input name="dsl_captcha_random" type="hidden" value="#captcha-random#" /> 56 <input name="dsl_back" type="hidden" value="#dsl-back-link#" /> 57 <label for="dsl_pid" ><span class="dsl-error-field"></span></label> 58 <input name="dsl_pid" type="hidden" value="#dsl-post-id#" /> 55 59 </p> 56 <p><label for="dsl_rname" >Recipient's name:< /label><br />57 <input name="dsl_rname" type="text"maxlength="#max-name-chars#" /></p>58 <p><label for="dsl_raddress" >Recipient's email address:< /label><br />59 <input name="dsl_raddress" type="text" /></p>60 <p><label for="dsl_sname" >Your name:< /label><br />61 <input name="dsl_sname" type="text"maxlength="#max-name-chars#" /></p>62 <p><label for="dsl_saddress" >Your email address:< /label><br />63 <input name="dsl_saddress" type="text" /></p>64 <p><label for="dsl_comments" >Your comments (optional):< /label><br />65 <textarea name="dsl_comments" maxlength="#max-comment-chars#" /></textarea></p>60 <p><label for="dsl_rname" >Recipient's name:<span class="dsl-error-field"></span></label><br /> 61 <input name="dsl_rname" type="text" maxlength="#max-name-chars#" /></p> 62 <p><label for="dsl_raddress" >Recipient's email address:<span class="dsl-error-field"></span></label><br /> 63 <input name="dsl_raddress" type="text" /></p> 64 <p><label for="dsl_sname" >Your name:<span class="dsl-error-field"></span></label><br /> 65 <input name="dsl_sname" type="text" maxlength="#max-name-chars#" /></p> 66 <p><label for="dsl_saddress" >Your email address:<span class="dsl-error-field"></span></label><br /> 67 <input name="dsl_saddress" type="text" /></p> 68 <p><label for="dsl_comments" >Your comments (optional):<span class="dsl-error-field"></span></label><br /> 69 <textarea name="dsl_comments" maxlength="#max-comment-chars#" /></textarea></p> 66 70 <div id="dsl-captcha"> 67 <p><label for="dsl_captcha" >Security check:</label></p> 68 <div> 69 #captcha-image# 70 <input name="dsl_captcha" type="text" autocomplete="off" value="" /> 71 <p><label for="dsl_captcha" >Security check:<span class="dsl-error-field"></span></label></p> 72 <div>#captcha-image# 73 <input name="dsl_captcha" type="text" value="" autocomplete="off" /> 71 74 <br /> 72 75 <a href="javascript:captchas_image_reload('captchas.net')">Reload</a> … … 75 78 </div> 76 79 </div> 77 <p class="button"><input type="submit" name="dsl_submit" value="#dsl-submit-button#" /></p> 80 <p class="button"> 81 <label for="dsl_submit"><span class="dsl-error-field"></span></label> 82 <input name="dsl_submit" type="submit" value="#dsl-submit-button#" /></p> 78 83 </form> 79 84 <p>Or, you can return to <a href="#dsl-back-link#">the article page</a> without sending a link.</p> … … 82 87 </div> 83 88 <script type="text/javascript"> 84 // this allows the tab key to go from the comments field to the captcha entry field without stopping on the captcha image89 // this Javascript statement llows the tab key to go from the comments field to the captcha entry field without stopping on the captcha image 85 90 document.getElementById('dsl-captcha').getElementsByTagName('div')[0].getElementsByTagName('a')[0].tabIndex=10; 86 91 </script> 87 92 <!--***************************************************** 88 * The confirmation page template starts here93 * The confirmation page template starts here 89 94 * 90 * Javascript will insert the confirmation message91 * it receives into div.postentry95 * This is just an empty div that gets filled in later 96 * with the contents of the email message sent. 92 97 --> 93 98 <div class="post" id="dsl-confirm-div"> -
dlbs-send-a-link/trunk/dsl-templates.html
r726535 r728099 1 1 <!--***************************************************** 2 * Templates for dlb's Send-A-Link (dsl)2 * Message and confirmation templates for dlb's Send-A-Link (dsl) 3 3 * 4 4 * Contains templates in separate sections for: 5 * - Email send error message6 5 * - Confirmation page 7 * - Confirmation page navigation links8 6 * - Email message 9 7 * 10 8 * This file is used by dsl.php, and is expected to be found 11 * in the same folder as dsl.php. To locate it elsewhere, change 12 * the DSL_PAGE_TEMPLATE definition in the configuration section of dsl.php. 9 * in the same folder as dsl.php. 13 10 * 14 * Placeholders available are as follows. To use, they must be enclosed in #'s:11 * The following placeholders are available. To use, they must be enclosed in #'s: 15 12 * - dsl-post-title 16 13 * - dsl-recipient-name … … 34 31 --> 35 32 <!--***************************************************** 36 * Error template is shown if an unexplained error occurs 37 * while sending the email message 38 --> 39 <error> 40 <p> 41 Unfortunately, an error occurred while attempting to send the message.<br /> 42 We apologize for the inconvenience and appreciate your patience.<br /> 43 Please try again. 44 </p> 45 </error> 46 <!--***************************************************** 47 * Confirm template is shown after successfully sending email 33 * Confirm template: 34 * shown after successfully sending email 48 35 --> 49 36 <confirm> 50 37 <p> 51 38 Here is a copy of the message that was sent to 52 #dsl-recipient-name# (#dsl-recipient-address#):39 #dsl-recipient-name# at (#dsl-recipient-address#): 53 40 </p> 54 41 <div class="message"> … … 56 43 <p>#dsl-message-body#</p> 57 44 </div> 58 </confirm>59 <!--*****************************************************60 * Nav template is shown after both the error and confirm templates61 -->62 <nav>63 45 <p> 64 46 Now, you can <a href="" onclick="location.reload(); return false;">send another message</a>,<br /> 65 47 or, you can return to <a href="#dsl-back-link#">the article page</a>. 66 48 </p> 67 </ nav>49 </confirm> 68 50 <!--***************************************************** 69 * Message template is used to construct the email message 51 * Message template: 52 * used to construct the email message 70 53 --> 71 54 <message> … … 90 73 </html> 91 74 </message> 92 <!--*****************************************************93 * Error alert template is shown to tell user about errors94 -->95 <error-alert>96 Please correct the highlighted error(s) and #dsl-submit-button# again97 </error-alert> -
dlbs-send-a-link/trunk/dsl.css
r726535 r728099 6 6 * to fit within 320 px wide. 7 7 * This is currently expressed in pixels, but 8 * will be recast as rem or pct or em later.8 * should be re-specified as a pct of the enclosing container. 9 9 * 10 * This file is used by dsl.php, and is expected to be found 11 * in the same folder as dsl.php. To locate it elsewhere, change 12 * the DSL_CSS_FILE definition in the configuration section of dsl.php. 10 * This file is used by dsl.php, and is expected to be found 11 * in the same folder as dsl.php. 12 * 13 * Additional styles to modify or override these can be 14 * specified in a file of this same name located in the theme folder. 13 15 * 14 16 * See http://www.ajaxload.info/ to make a … … 79 81 } 80 82 #dsl-captcha input { 81 width: 160px;83 width:9em; 82 84 margin:15px 0; 83 85 } -
dlbs-send-a-link/trunk/dsl.js
r726535 r728099 2 2 * Javascript for dlb's Send-A-Link (dsl) 3 3 * 4 * Primarily handles AJAX communication of user input and5 * server response to eliminate need for re-sending entire page.4 * Handles AJAX communication of user input and 5 * server responses to eliminate need for re-sending entire page. 6 6 * 7 7 * This file is used by dsl.php, and is expected to be found 8 * in the same folder as dsl.php. To locate it elsewhere, change 9 * the DSL_JAVASCRIPT_FILE definition in the configuration section of dsl.php. 8 * in the same folder as dsl.php. 10 9 * 11 10 * This is written with plenty of white-space and comments for clarity, … … 24 23 // Create AJAX object 25 24 // See explanation of AJAX setup at http://www.tizag.com/ajaxTutorial/ajaxform.php 26 var ajax, params , testurl, realurl, url, out;25 var ajax, params; 27 26 try{ 28 27 ajax = new XMLHttpRequest(); // Opera 8.0+, Firefox, Safari … … 44 43 ajax.onreadystatechange = function(){ 45 44 if(ajax.readyState == 4){ // indicates that response has been completely received from the server 46 // Send the form and the server's responseto the function that does the work45 // pass control to the function that does the work 47 46 dslProcessResponse(form, ajax.response); 48 47 } 49 48 } 50 // Formatthe form input for sending to server as a POST request49 // Collect inputs from the form input for sending to server as a POST request 51 50 // Note that the global variable ajaxData was provided by the server via a wp_localize_script call 52 51 params = 'action=' + ajaxData.action + '&' + dslGetFormData(form); 53 // Establish type of request (POST vs GET), and the URL to which the request will be sent, 54 // and headers to tell data format 52 // Establish type of request (i.e., using POST, not GET) and the URL 55 53 ajax.open("POST", ajaxData.url, true); 54 // Establish headers to tell data type 56 55 ajax.setRequestHeader("Content-type", "application/x-www-form-urlencoded") 57 56 // Send request to the server … … 73 72 var i, params=''; 74 73 // loop through all elements of the form 74 // this simple routine works for text inputs 75 // but would have to be extended to handle checkboxes, radios, etc. 75 76 for(i=0; i<form.elements.length; i++){ 76 77 if(form.elements[i].name && form.elements[i].value) { … … 93 94 // convert the response from a JSON string to javascript objects 94 95 response = JSON.parse(response); 95 // check if a 2nd nonce was received that says CAPTCHA was previously satisfied,96 // and if so, store that nonce it in a form element, and hide the captcha div97 if (typeof response.secure != 'undefined') {98 form.elements["dsl_secure"].value = response.secure;99 document.getElementById('dsl-captcha').style.display = 'none';100 }101 96 // Either show errors or show confirmation 102 if ( response.reset) {97 if (typeof response.reset != 'undefined') { 103 98 // server says to reset the page 104 dslDoReset(); 99 // do so showing the form-level error message 100 dslDoReset(response.errors['dsl_pid']); 105 101 } else if (typeof response.errors != 'undefined') { 106 102 // got errors 107 103 // show the messages 108 104 dslShowErrors(form, response.errors); 105 // check if a 2nd nonce was received that says CAPTCHA was previously satisfied, 106 // and if so, store that nonce it in a form element, and hide the captcha div 107 if (typeof response.secure != 'undefined') { 108 form.elements["dsl_secure"].value = response.secure; 109 document.getElementById('dsl-captcha').style.display = 'none'; 110 } 109 111 // scroll top of form into view 110 112 document.getElementById('dsl-form-div').scrollIntoView(); … … 119 121 // then select all of the DIVs within it via .getElementsByTagName, 120 122 // then index to the first of them via [0] 121 // then access property innerHTML, 122 // and set it equal to the formatted response123 // then access property innerHTML, and 124 // load the formatted response from server into it 123 125 cDiv.getElementsByTagName('DIV')[0].innerHTML = response.confirms.body; 124 // show the confirm ationdiv126 // show the confirm div 125 127 cDiv.style.display = 'block'; 126 128 // scroll top of page into view 127 document.getElement ById('page').scrollIntoView();129 document.getElementsByTagName('body')[0].scrollIntoView(); 128 130 } else { 129 // unexplained error, so reset 130 dslDoReset( );131 // unexplained error, so reset using error message from the global sent by the server 132 dslDoReset(ajaxData.errorGeneral); 131 133 } 132 134 } … … 136 138 * Displays an alert for a processing error and reloads the page 137 139 */ 138 function dslDoReset( ) {140 function dslDoReset(alertMsg) { 139 141 // display message for a processing error 140 alert ( 'A general processing error occurred.\n\rWe apologize for the inconvenience and appreciate your patience.\n\rPlease try again.');142 alert (alertMsg); 141 143 // force a page reload from the server 142 144 document.location.reload(true); 145 } 146 /******************************************************** 147 * dslShowErrors() 148 * 149 * Shows each error message in the label element of the associated input element. 150 * Expects that the errors object contains "key":value pairs, 151 * where key is the name given to each input element. 152 */ 153 function dslShowErrors(form, errors){ 154 var field, spans; 155 // assocate labels to elements 156 dslAssociateLabels(form); 157 // loop through all of the error messages 158 for (field in errors){ 159 // use the "key" for this error to select the form element 160 // then access its label attribute, 161 // then get all the spans within it 162 // then find one with the right class 163 // and put the error message into its innerHTML 164 spans = form.elements[field].label.getElementsByTagName('span'); 165 for (i=0; i<spans.length; i++) { 166 if(spans[i].className == 'dsl-error-field') { 167 spans[i].innerHTML = '<br />' + errors[field]; 168 break; 169 } 170 } 171 } 143 172 } 144 173 /******************************************************** … … 164 193 } 165 194 /******************************************************** 166 * dslShowErrors()167 *168 * Shows each error message in the label element of the associated input element.169 * Expects that the errors object contains "key":value pairs,170 * where key is the name given to each input element.171 */172 function dslShowErrors(form, errors){173 var field;174 // assocate labels to elements175 dslAssociateLabels(form);176 // loop through all of the error messages177 for (field in errors){178 // use the "key" for this error to select the form element179 // then access its label attribute,180 // and set its innerHTML property to the error message181 // the spans around the error message allow it to be identified for deletion182 form.elements[field].label.innerHTML += '<span class="dsl-error-field"><br />' + errors[field] + '</span>';183 }184 }185 /********************************************************186 195 * dslClearErrors() 187 196 * … … 193 202 // select all the span elements on the form 194 203 errors = form.getElementsByTagName('SPAN'); 195 // loop thru them in reverse order so that removal of one doesn't renumber the rest196 for (i= errors.length-1; i>=0; i--){204 // Loop thru them 205 for (i=0; i<errors.length; i++){ 197 206 // check each span to see if it is an error message 198 207 if(errors[i].className == 'dsl-error-field') { 199 // this one is, so delete it by accessing it's parent 200 // and using the remove method 201 errors[i].parentNode.removeChild(errors[i]); 202 } 203 } 204 } 208 // this is one so clear its value 209 errors[i].innerHTML = ''; 210 } 211 } 212 } -
dlbs-send-a-link/trunk/dsl.php
r726856 r728099 3 3 Plugin Name: dlb's Send-A-Link 4 4 Plugin URI: http://wordpress.org/plugins/dlbs-send-a-link/ 5 Description: Send-A-Link allows visitors to send someone an email containing a link to the post or page and some comments.6 Version: 1.05 Description: dlb's Send-A-Link allows visitors to send someone an email containing a link to the post or page. 6 Version: 0.95 7 7 Author: Dave Bezaire 8 8 Author URI: http://davebezaire.com/ … … 27 27 * Globals and constants that can be customized 28 28 */ 29 // Input validation: maximum number of characters in the comments 30 define ( 'DSL_MAX_COMMENT_CHARS', 250); 31 // Input validation: minimum number of characters in the name 32 define ( 'DSL_MIN_NAME_CHARS', 1); 33 // Input validation: maximum number of characters in the name 34 define ( 'DSL_MAX_NAME_CHARS', 30); 35 // Input validation: minimum interval between sends in seconds 36 define ( 'DSL_MIN_SEND_INTERVAL', 30 ); 37 // The string shown in DSL_SUBMIT_BUTTON_VALUE is 38 // shown on and returned by the form's submit button. 39 // This is a global because it is a trigger used in an IF statement. 40 // Changing it in this definition will automatically insert it into the form template 41 // and in the corresponding IF statement so that changing it 42 // will not require any other alteration in the program. 43 // It is also shown in the DSL_ERROR_ALERT message. 44 define ( 'DSL_SUBMIT_BUTTON_VALUE', 'Send it!' ); 45 // Alert displayed to tell user that an error needs attention 46 define ( 'DSL_ERROR_ALERT', 'Please correct the highlighted error(s) and ' . DSL_SUBMIT_BUTTON_VALUE . ' again' ); 47 // Message for an unexplained error 48 define ( 'DSL_GENERAL_ERROR_MESSAGE', 'Unfortunately, a general processing error occurred. We apologize for the inconvenience and appreciate your patience. Please try again.' ); 49 // Message for an unexplained error while sending mail 50 define ( 'DSL_SENDING_ERROR_MESSAGE', 'Unfortunately, an error occurred while attempting to send the message. We apologize for the inconvenience and appreciate your patience. Please try again.' ); 51 // Message for exceeding maximum characters 52 define ( 'DSL_MAX_CHARS_ERROR', 'Must be no more than %d characters' ); 53 // Message for too few characters 54 define ( 'DSL_MIN_CHARS_ERROR', 'Must be at least %d character(s)' ); 55 // Message for invalid email address 56 define ( 'DSL_INVALID_EMAIL_ERROR', 'Must be a valid email address' ); 57 // Message for invalid characters in email address 58 define ( 'DSL_INVALID_EMAIL_CHARS', 'May not contain invalid characters' ); 59 // Message for invalid characters in name 60 define ( 'DSL_INVALID_NAME_CHARS', 'Must contain only: A-Z, a-z, 0-9, . , _ - " \' %' ); 61 // Message for invalid characters in comments 62 define ( 'DSL_INVALID_COMMENTS_CHARS', 'Must not contain invalid characters (only limited HTML is allowed)' ); 63 // Message for invalid nonce 64 define ( 'DSL_INVALID_NONCE', 'Security check failed' ); 65 // Message for incorrect CAPTCHA response 66 define ( 'DSL_INCORRECT_CAPTCHA_ERROR', 'Must match characters in image' ); 67 // Message for internal CAPTCHA error 68 define ( 'DSL_INTERNAL_CAPTCHA_ERROR', 'Internal captcha error' ); 69 // Message for violating minimum sending interval 70 define ( 'DSL_MIN_SEND_INTERVAL_MESSAGE', 'Must wait at least %d seconds between sends<br />' ); 71 // Message for invalid show value 72 define ( 'DSL_INVALID_SHOW_VALUE', 'Invalid value for show in dslLink' ); 73 // Default text for link 74 define ( 'DSL_DEFAULT_LINK_TEXT', ' Send a link to this article' ); 75 // Default link icon file name 76 define ( 'DSL_DEFAULT_ICON_FILE', 'email.gif' ); 77 /******************************************************** 78 * Globals and constants that should not be changed 79 */ 80 //Global data array used throughout 81 $dslData = array(); 29 82 // The following four definitions use two pairs of WordPress functions 30 83 // to produce references to the folder in which this plugin file is located, … … 38 91 // URL to the theme folder is used to load scripts and styles 39 92 define ( 'DSL_THEME_URL', get_stylesheet_directory_uri() . '/' ); 40 // We only look for the JS file in the plugin folder; it can not be empty 41 define ( 'DSL_JAVASCRIPT_FILE', 'dsl.js' ); 42 // The following three definitions specify the names of the customization files. 93 // The following definitions specify the names of the customization files. 43 94 // By default they are located relative to the plugin folder, 44 // but they can be overridden by a copy in theme folder. 95 // but they can be overridden by a copy in the theme folder. 96 // filename for the captcha configuration parameters 97 define ( 'DSL_CAPTCHA_CONFIG', 'dsl-captcha.php' ); 45 98 // filename for form/confirm page template 46 99 define ( 'DSL_PAGE_TEMPLATE', 'dsl-page.html' ); 47 100 // filename for other templates 48 101 define ( 'DSL_OTHER_TEMPLATES', 'dsl-templates.html' ); 49 // filename of styles; the can be the empty string (i.e. '') which might be appropriate 50 // if the styles are included in the themes's CSS file 102 // filename for CSS styles 51 103 define ( 'DSL_CSS_FILE', 'dsl.css' ); 52 // value that is shown on and returned by the form's submit button 53 define ( 'DSL_SUBMIT_BUTTON_VALUE', 'Send it!' ); 54 // validation requirement for maximum number of characters in the comments 55 define ( 'DSL_MAX_COMMENT_CHARS', 250); 56 // validation requirement for minimum number of characters in the name 57 define ( 'DSL_MIN_NAME_CHARS', 1); 58 // validation requirement for maximum number of characters in the name 59 define ( 'DSL_MAX_NAME_CHARS', 30); 60 61 /******************************************************** 62 * dslMakeCaptcha() 63 * 64 * Returns a Captchas.Net object 65 * Uses the web service at http://captchas.net 66 * See Captcha comparisons at http://davebezaire.com/tst/captcha/captcha.html 67 * 68 * All Captchas.Net confirmation parameters are in this routine. 69 */ 70 function dslMakeCaptcha(){ 71 // load the library that was copied from http://captchas.net 72 require_once(DSL_PLUG_PATH .'CaptchasDotNet.php'); 73 // ID provided in registration 74 $id = 'dcapltchba'; 75 // private key provided in registration 76 $key = 'rConlz8OdN77va2jsYyOeW3VOJCACT4qgCJzZeoh'; 77 // storage location for strings??? not really sure how this works 78 $store = '/tmp/captchasnet-random-strings'; 79 // maximum time (seconds) that a captcha can be valid 80 $time = '3600'; 81 // characters to use in the image 82 $alphabet = 'abcdghkmnpqrstvwyz345'; 83 // number of characters in the image 84 $count = '4'; 85 // size in pixels 86 $height = '80'; 87 $width = '140'; 88 // RGB hex value 89 $color = 'A0590A'; 90 // create and return the object 91 return new CaptchasDotNet ($id, $key, $store, $time, $alphabet, $count, $width, $height, $color); 92 } 93 /******************************************************** 94 * Globals and constants that MUST NOT be customized 95 */ 96 //Global data array used throughout 97 $dslData = array(); 98 // calculate location of other templates file 99 define ( 'DSL_OTHER_TEMPLATES_CALCULATED' , dslCustomized( 'PATH', DSL_OTHER_TEMPLATES ) ); 100 /******************************************************** 101 * dslShortcode() 102 * 103 * Adds shortcode "dsl-link" which produces the html tags to display a 104 * link to the email request form. 104 // filename for the Javascript; we only look for it in the plugin folder 105 define ( 'DSL_JAVASCRIPT_FILE', 'dsl.js' ); 106 // Define our table name in the WordPress mySQL database 107 // using the site's table prefix that we get from the $wpdb global. 108 global $wpdb; 109 define ( 'DSL_LOG_TABLE' , $wpdb->prefix . 'dslLog' ); 110 // Set plugin-database version 111 define ( 'DSL_DB_VERSION', 'dbv1' ); 112 // Define the name used to store the db version in the database 113 define ( 'DSL_NAME_OF_DB_VERSION_SETTING', 'dsl_db_version'); 114 // Set number of seconds to retain log records in database 115 // e.g., 60 * 60 & 24 is 24 hours 116 define ( 'DSL_LOG_KEEP_SECONDS', 60 * 60 * 24 ); 117 /******************************************************** 118 * dslLink() 119 * 120 * Adds shortcode "dsl-link" and function dslLink() 121 * which produce the html tags to display a 122 * link to the email request form. 123 * 124 * The shortcode is used within the contents of a page or post. 125 * The function is used in templates. 105 126 * 106 * Th islink sends the ID of the post or page in which it is clicked.127 * The resulting link sends the ID of the post or page in which it is clicked. 107 128 * It also sends the URL of the page (which could be either a single post or a 108 129 * page of many posts) for use in a back link. … … 113 134 * e.g., [dsl-link text="Link it!" iconfile="myIcon.gif" show="both"]. 114 135 */ 115 add_shortcode("dsl-link", "dsl Shortcode");116 function dsl Shortcode($atts){136 add_shortcode("dsl-link", "dslLink"); 137 function dslLink( $atts = array() ){ 117 138 // Establish parameter names and defaults. 118 139 // These are the values that can be provided within the shortcode brackets, 119 140 // and the default values used if they are not specified. 120 141 $defaults = array( 121 'show' => ' text', // one of 'icon', 'text', 'both', 'debug'122 'icon_file' => "email.gif",// filename, including path if not same place as this script123 'text' => ' Send a link to this article'// link text142 'show' => 'both', // one of 'icon', 'text', 'both' 143 'icon_file' => DSL_DEFAULT_ICON_FILE, // filename, including path if not same place as this script 144 'text' => DSL_DEFAULT_LINK_TEXT // link text 124 145 ); 125 146 // Merge the defaults with the inputs from the shortcode … … 137 158 // Next we add the current page URL to the query 138 159 $query .= '&dsl_back=' . "http://" . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']; 139 // and build the anchor tag 140 $anchor = '<a href="' . $link_page . $query . '" title="' . $text . '">'; 141 // Now we build the image tag 160 // Use WordPress esc_attr to be sure there are no invalid or dangerous characters in text 161 $text = esc_attr($text); 162 // Build the anchor tag 163 // Use the WordPress esc_url functions for extra safety against sending out invalid or dangerous characters 164 $anchor = '<a href="' . esc_url($link_page . $query) . '" title="' . $text . '">'; 165 // Build the image tag 166 // Check that specified icon file exists by calling PHP function getimagesize() 167 // which returns an array with the size already formatted for an html tag at index 3 168 // or FALSE if file does not exist. 169 // If file doesn't exist, fall back to default. 170 if ( false === ( $imgsiz = getimagesize(DSL_PLUG_PATH . $icon_file) ) ) { 171 $icon_file = $defaults['icon_file']; 172 $imgsiz = getimagesize(DSL_PLUG_PATH . $icon_file); 173 } 142 174 $imgurl = DSL_PLUG_URL . $icon_file; 143 // Next we use the PHP getimagesize function, which returns an array with the 144 // size already formatted for an html tag at index 3, 145 $imgsiz = getimagesize(DSL_PLUG_PATH . $icon_file); 146 // Here we put together the entire img tag 147 $img = '<img src="' . $imgurl . '" ' . $imgsiz[3] . ' />'; 175 // Put together the entire img tag 176 // Escape the url for safety, but trust the output from getimagesize to be valid 177 $img = '<img src="' . esc_url($imgurl) . '" ' . $imgsiz[3] . ' />'; 148 178 // Now we use the value of the $show parameter to determine what to output 149 179 switch ($show) { 150 case 'debug': 151 $output = "from my code"; 152 $output .= ", the post id is " . $post_id; 153 $link_icon_text = $anchor . $img . $text . '</a>'; 154 $link_icon_only = $anchor . $img . '</a>'; 155 $link_text_only = $anchor . $text . '</a>'; 156 $output .= " Here are the links: " . $link_icon_only . " and " . $link_icon_text. " and " . $link_text_only; 157 break; 180 // special option for debug 181 // comment this out for production 182 // case 'debug': 183 // $output = "from my code"; 184 // $output .= ", the post id is " . $post_id; 185 // $link_icon_text = $anchor . $img . $text . '</a>'; 186 // $link_icon_only = $anchor . $img . '</a>'; 187 // $link_text_only = $anchor . $text . '</a>'; 188 // $output .= " Here are the links: " . $link_icon_only . " and " . $link_icon_text. " and " . $link_text_only; 189 // break; 158 190 case 'both': 159 191 $output = $anchor . $img . $text . '</a>'; … … 166 198 break; 167 199 default: 168 $output = "Invalid value for show";200 $output = DSL_INVALID_SHOW_VALUE; 169 201 } 170 202 return $output; … … 173 205 * dslMain() 174 206 * 175 * This is the entry point for all requests for adlb's Send-A-Link.207 * This is the entry point for all requests to dlb's Send-A-Link. 176 208 * 177 209 * WordPress calls this routine every time it outputs any page or post, … … 179 211 * WordPress also calls this routine whenever it receives an AJAX request that 180 212 * specifies "action" equal to "dsl-form" because of the calls to add_action() 181 * with 'wp_ajax_nopriv' and 'wp_ajax'. 213 * with 'wp_ajax_nopriv' (called when a registered user is logged in) and 214 * 'wp_ajax' (called for an unregistered visitor). 182 215 * 183 216 * This routine checks for existance of parameters in the URL query and POST variables 184 * that indicate a dsl page request and handles the page. It quickly determineif217 * that indicate a dsl page request and handles the request. It quickly determines if 185 218 * the requested page needs this routine or not, and simply returns if not needed. 186 219 * … … 188 221 * message, with the confirmation message initially hidden. When the AJAX request sends 189 222 * the user input, this routine validates it sends an AJAX response which can 190 * either be error message(s) or a confirmation message. 223 * either be error message(s) or a confirmation message. If the user does not have 224 * Javascript turned on, then the response is sent by the browser as a regular POST 225 * instead of an AJAX, so this routine will send its response as a new page. 191 226 * 192 227 * When there are no errors, this routine actually sends the email and records it in a log. … … 194 229 * The trigger for this routine to take over is existance of query parameter 'dsl_pid' 195 230 * which must contain the ID of a page or post, e.g., http://Bezaires.com?dsl_pid=1234 196 *197 231 */ 198 232 add_action( 'template_redirect', 'dslMain' ); … … 201 235 function dslMain() { 202 236 global $dslData; 203 // load the inputs into $dslData237 // load the inputs into the $dslData array 204 238 dslGetInputs(); 205 239 // quickly return if we do not handle this type of request 240 // return if we don't have a URL query parameter of 'dsl_pid' 206 241 if( ! isset($dslData['dsl_pid']) ) return; 242 // insure received value is an integer, 243 $dslData['dsl_pid'] = filter_var($dslData['dsl_pid'], FILTER_SANITIZE_NUMBER_INT); 244 // get the post using it as a post id number 207 245 $dslData['post'] = get_post($dslData['dsl_pid']); 208 // must be an accessiblepost or a page246 // return if we did not find a post or a page 209 247 if (NULL == $dslData['post'] || ('post' != $dslData['post']->post_type && 'page' != $dslData['post']->post_type) ) return; 210 // Value of the form's submit button tells us 211 // whether to show the page or to process the inputs 212 switch ($dslData['dsl_submit']) { 213 case "": 248 // Now we know that request is of the type we handle 249 // Check value of back link 250 // Replace it with one pointing to the post if: 251 // - it was not provided at all 252 // - it contains invalid URL characters 253 // - it does not contain the URL of this blog 254 if ( 255 ( ! isset( $dslData['dsl_back'] ) ) 256 || filter_var( $dslData['dsl_back'], FILTER_SANITIZE_URL ) != ( $dslData['dsl_back'] ) 257 || ( false === strpos( $dslData['dsl_back'], home_url() ) ) 258 ) { 259 // Replace by setting it to a URL that points to the post/page 260 $dslData['dsl_back'] = get_permalink( $dslData['post'] ); 261 } 262 // setup array to accumulate the responses we'll send back 263 $msg = array(); 264 // Check for security or structural errors that may impede later processing including 265 // the submit button, the WordPress nonce, and the IP address. 266 // If nonce fails, consider the entire request a suspect security risk. 267 // If an unknown submit button value, not sure what we have, so don't trust it. 268 // Not sure how we get an invalid IP address, but it's a bad thing if we do. 269 // For all of these cases, send a general error message and reset the form fields. 270 if ( ( $dslData['dsl_submit'] != '' && $dslData['dsl_submit'] != DSL_SUBMIT_BUTTON_VALUE ) 271 || ( $dslData['dsl_submit'] != '' && ( ! wp_verify_nonce( $dslData['dsl_nonce'], 'dsl-form' ) ) ) 272 || ( false === $dslData['ip'] ) 273 ) { 274 // flag to force values to be reset 275 $msg['reset'] = true; 276 // the $errors array generally contains error messsages with the array key 277 // equal to the name of the form field that contains the error. to handle 278 // this form-level error, we put the message into the hidden post id field, 279 // which is at the top of the form, so that is where it will be displayed. 280 $errors['dsl_pid'] = DSL_GENERAL_ERROR_MESSAGE; 281 } 282 // If no errors yet, we know we have a valid request. 283 // The driving parameters have been tested, 284 // and inputs have been recorded in the $dslData array. 285 // Use the submit button value to determine 286 // whether to show the form or to process the inputs. 287 if ( ( count($errors) == 0 ) 288 && ( $dslData['dsl_submit'] == '') 289 ) { 214 290 // there was no submit button value, 215 // so show a page with the form and confirmation sections. 216 // First tell WordPress to run the function that adds the scripts and styles to the page 217 add_action('wp_enqueue_scripts', 'dslEnq'); 218 // read template from file 219 $input = file_get_contents( dslCustomized( 'PATH', DSL_PAGE_TEMPLATE ) ); 220 // perform substitutions for placeholders 221 dslSubstitutePlaceholders(&$input); 222 // send page headers 223 get_header(); 224 // send template 225 echo $input; 226 // send footers and sidebar 227 get_sidebar(); 228 get_footer(); 229 break; 230 case DSL_SUBMIT_BUTTON_VALUE: 231 // submit button value says we got a response from the user 232 // check inputs for errors 233 $errors = dslValidateInputs(); 234 // build array of responses to send via AJAX 235 $msg = array(); 236 if (isset($errors['dsl_nonce'])) { 237 // major security breach was found 238 // send back a reset 239 // to cause Javascript to report an internal error and generate a reload 240 $msg['reset'] = true; 241 } elseif (count($errors) > 0) { 242 // errors were found, so add the 243 // error messages to the response array 244 $msg['errors'] = $errors; 245 if (! isset($errors['dsl_captcha']) ) { 246 // No error with CAPTCHA, so we believe we are dealing with a person. 247 // Create a new nonce to use as a stand-in 248 // so that user doesn't have to do CAPTCHA again when correcting other errors 249 // and add it to the response array 250 $msg['secure'] = wp_create_nonce( 'dsl-form-noCaptcha' ); 251 } 252 } else { 253 // no errors were found, so ready to send email 254 // format mail fields 255 dslPrepareMail(); 256 // send the email 257 $dslData['mail-result'] = mail( $dslData['mail-to'], 258 $dslData['mail-subject'], 259 $dslData['mail-message'], 260 $dslData['mail-headers']); 261 // add the confirms object to the response array 262 $msg['confirms'] = array( 263 'status' => $dslData['mail-result'], 264 'body' => dslFormatConfirm() 265 ); 266 } 267 // add the received values to the response array for debugging 268 // comment this out for production 269 $msg['values'] = $dslData; 270 // convert all of the responses in the array into a JSON string 271 $msg = json_encode($msg); 272 // send header and response string 273 header( "Content-Type: application/json" ); 274 echo $msg; 275 break; 276 default: 277 echo 'A general processing error occurred (i.e., received a dsl_submit value of |' . $dslData['dsl_submit'] . '|<br />' . 278 'We apologize for the inconvenience and appreciate your patience. Please try again.'; 279 break; 291 // so show the blank form and then exit 292 dslShowPage('blank'); 293 exit; 294 } 295 // If no errors yet, we know we have received inputs from the user 296 if (count($errors) == 0) { 297 // Insure the proper version of the database table is installed 298 dslSetupDatabase(); 299 // get log record for this IP address 300 $dslData['log'] = dslReadLog( $dslData['ip'] ); 301 // check inputs for errors 302 $errors = dslValidateInputs(); 303 } 304 // If no errors yet, try to send the mail 305 // If fails, load an error message to the $errors array 306 // If succeeds, load confirm to the $msg array 307 // and record in the log 308 if (count($errors) == 0) { 309 dslSendMail(); 310 if ( TRUE !== $dslData['mail-result'] ) { 311 // the send failed for no known reason, 312 // so load a form-level error message 313 $errors['dsl_pid'] = DSL_SENDING_ERROR_MESSAGE; 314 } else { 315 // add the confirms object to the response array 316 $msg['confirms'] = array( 317 'status' => $dslData['mail-result'], 318 'body' => dslFormatConfirm() 319 ); 320 // update log for IP with current time 321 dslUpdateLog( $dslData['ip'], time() ); 322 // clear old records out of the log 323 // (this should really be a CRON job or a mySQL trigger someday) 324 dslFlushLog(); 325 } 326 } 327 // At this point, we either have message(s) in the $error array, 328 // or we have successfully sent the email and noted it in the $msg array 329 // If there are errors, put them into $msg array 330 // and turn off CAPTCHA 331 if (count($errors) > 0) { 332 $msg['errors'] = $errors; 333 if ( (! $msg['reset']) && (! isset($errors['dsl_captcha'])) ) { 334 // No reset errors and no CAPTCHA error, 335 // so we believe we are dealing with a person. 336 // Create a new nonce to use as a stand-in 337 // so that user doesn't have to do CAPTCHA again when correcting other errors 338 // and add it to the response array 339 $msg['secure'] = wp_create_nonce( 'dsl-form-noCaptcha' ); 340 } 341 } 342 // At this point we have the $msg array containing all our responses 343 // add the received values to the response array for debug 344 // comment this out for production 345 //$msg['values'] = $dslData; 346 // decide whether user has Javascript enabled and respond accordingly 347 if ( isset($dslData['action']) && $dslData['action'] == 'dsl-form' ) { 348 // we received the response via AJAX, so send it back that way 349 // convert all of the responses in the array into a JSON string 350 $msg = json_encode($msg); 351 // send header and response string 352 header( "Content-Type: application/json" ); 353 echo $msg; 354 } else { 355 // user doesn't have Javascript available, so resend entire page 356 dslShowPage('with-feedback', $msg); 280 357 } 281 358 exit(); … … 284 361 * dslGetInputs() 285 362 * 286 * Pulls all of the inputs received via URL query or POST valuesinto an array363 * Pulls all of the inputs received via URL query or POST into an array 287 364 */ 288 365 function dslGetInputs(){ … … 297 374 $tmp = $_REQUEST; 298 375 foreach ($tmp as $key => $value) { 299 if (substr($key, 0, 3) == 'dsl' ) {376 if (substr($key, 0, 3) == 'dsl' || $key == 'action') { 300 377 // All of our variables begin with "dsl", so we find and store them. 378 // We also need the "action" that WordPress uses with AJAX 301 379 // It seems that stripslashes() is needed because WordPress apparently does an addslashes() 302 380 // which puts a backslash before every apostrophe. This is similar to a vestigal … … 306 384 } 307 385 } 386 // add the user's IP address 387 $dslData['ip'] = dslGetIP(); 388 } 389 /******************************************************** 390 * dslGetIP() 391 * 392 * Gets ip address from any of multiple locations, 393 * or returns FALSE if none can be found. 394 * Verifies against invalid IP formats or characters with 395 * PHP function filter_var() 396 * 397 * copied from http://www.stevekamerman.com/2006/06/storing-ip-addresses-in-mysql-with-php/ 398 */ 399 function dslGetIP() { 400 if (filter_var($_SERVER["HTTP_CLIENT_IP"], FILTER_VALIDATE_IP)) { 401 return $_SERVER["HTTP_CLIENT_IP"]; 402 } 403 foreach (explode(",",$_SERVER["HTTP_X_FORWARDED_FOR"]) as $ip) { 404 if (filter_var(trim($ip), FILTER_VALIDATE_IP)) { 405 return $ip; 406 } 407 } 408 if (filter_var($_SERVER["HTTP_PC_REMOTE_ADDR"], FILTER_VALIDATE_IP)) { 409 return $_SERVER["HTTP_PC_REMOTE_ADDR"]; 410 } elseif (filter_var($_SERVER["HTTP_X_FORWARDED"], FILTER_VALIDATE_IP)) { 411 return $_SERVER["HTTP_X_FORWARDED"]; 412 } elseif (filter_var($_SERVER["HTTP_FORWARDED_FOR"], FILTER_VALIDATE_IP)) { 413 return $_SERVER["HTTP_FORWARDED_FOR"]; 414 } elseif (filter_var($_SERVER["HTTP_FORWARDED"], FILTER_VALIDATE_IP)) { 415 return $_SERVER["HTTP_FORWARDED"]; 416 } elseif (filter_var($_SERVER["REMOTE_ADDR"], FILTER_VALIDATE_IP)) { 417 return $_SERVER["REMOTE_ADDR"]; 418 } else { 419 return false; 420 } 308 421 } 309 422 /******************************************************** 310 423 * dslValidateInputs() 311 424 * 312 * Given anarray of field values which has indices equal to the field names,425 * Given the global array of field values which has indices equal to the field names, 313 426 * check for valid inputs. 314 427 * Returns an array of error messages with indices equal to the field names. … … 316 429 function dslValidateInputs(){ 317 430 global $dslData; 318 $errors = array(); 319 320 // Check the WordPress nonce 321 // If this fails, consider the entire request suspect 322 if ( ! wp_verify_nonce( $dslData['dsl_nonce'], 'dsl-form' ) ) { 323 $errors['dsl_nonce'] = 'was invalid'; 324 } 325 431 326 432 // Check security field (anti-bot) 327 if ( isset($dslData['dsl_secure'])) {433 if ( isset($dslData['dsl_secure']) && $dslData['dsl_secure'] != '' ) { 328 434 // check the second nonce that was sent after a captcha was previously satisfied 329 435 if (false === wp_verify_nonce( $dslData['dsl_secure'], 'dsl-form-noCaptcha' )) { 330 $errors[dsl_secure] = 'Security check failed';436 $errors[dsl_secure] = DSL_INVALID_NONCE; 331 437 } 332 438 } else { … … 334 440 $captcha = dslMakeCaptcha(); 335 441 if ( ! $captcha->validate($dslData['dsl_captcha_random']) ) { 336 $errors['dsl_captcha'] = 'Internal captcha error';442 $errors['dsl_captcha'] = DSL_INTERNAL_CAPTCHA_ERROR; 337 443 } elseif ( ! $captcha->verify($dslData['dsl_captcha']) ) { 338 $errors['dsl_captcha'] = 'Must match characters in image';444 $errors['dsl_captcha'] = DSL_INCORRECT_CAPTCHA_ERROR; 339 445 } 446 } 447 448 // Check for sufficient time since last send by this IP address 449 if ( (time() - $dslData['log']->time) < DSL_MIN_SEND_INTERVAL ) { 450 $errors['dsl_submit'] = sprintf(DSL_MIN_SEND_INTERVAL_MESSAGE, DSL_MIN_SEND_INTERVAL ); 340 451 } 341 452 … … 348 459 dslCheckName('dsl_rname', $errors); // recipient's 349 460 350 // Check for valid comments 351 $invalidCommentChars = "/[<>]/"; 461 // Check for valid comments using rules that WordPress applies to comments 352 462 if (strlen(trim($dslData['dsl_comments'])) > DSL_MAX_COMMENT_CHARS ) { 353 $errors['dsl_comments'] = 'Must be no more than ' . DSL_MAX_COMMENT_CHARS . ' characters';354 } elseif ( 1 === preg_match($invalidCommentChars, $dslData['dsl_comments'])) {355 $errors['dsl_comments'] = 'Must not contain < or >';463 $errors['dsl_comments'] = sprintf(DSL_MAX_CHARS_ERROR, DSL_MAX_COMMENT_CHARS); 464 } elseif ( $dslData['dsl_comments'] != wp_kses( $dslData['dsl_comments'], wp_kses_allowed_html( 'comment' ) ) ) { 465 $errors['dsl_comments'] = DSL_INVALID_COMMENTS_CHARS; 356 466 } 357 467 … … 369 479 global $dslData; 370 480 if ( $dslData[$fieldName] != filter_var($dslData[$fieldName], FILTER_SANITIZE_EMAIL)) { 371 $errors[$fieldName] = 'May not contain invalid characters';481 $errors[$fieldName] = DSL_INVALID_EMAIL_CHARS; 372 482 } elseif ( ! filter_var($dslData[$fieldName], FILTER_VALIDATE_EMAIL)) { 373 $errors[$fieldName] = 'Must be a valid email address';483 $errors[$fieldName] = DSL_INVALID_EMAIL_ERROR; 374 484 } 375 485 } … … 384 494 function dslCheckName($fieldName, &$errors){ 385 495 global $dslData; 386 $invalidNameChars = "/[^ a-zA-Z0-9._%-', ]/"; // invalid chars are those NOT in the list496 $invalidNameChars = "/[^ a-zA-Z0-9._%-',\"]/"; // invalid chars are those NOT in the list 387 497 if (strlen(trim($dslData[$fieldName])) < DSL_MIN_NAME_CHARS) { 388 $errors[$fieldName] = 'Must be at least ' . DSL_MIN_NAME_CHARS . ' character(s)';498 $errors[$fieldName] = sprintf( DSL_MIN_CHARS_ERROR, DSL_MIN_NAME_CHARS ); 389 499 } elseif (strlen(trim($dslData[$fieldName])) > DSL_MAX_NAME_CHARS) { 390 $errors[$fieldName] = 'Must be no more than ' . DSL_MAX_NAME_CHARS . ' characters';500 $errors[$fieldName] = sprintf( DSL_MAX_CHARS_ERROR, DSL_MAX_NAME_CHARS ); 391 501 } elseif (1 === preg_match($invalidNameChars, $dslData[$fieldName])) { 392 $errors[$fieldName] = 'Must contain only: A-Z, a-z, 0-9, . , _ - \' %'; 393 } 502 $errors[$fieldName] = DSL_INVALID_NAME_CHARS; 503 } 504 } 505 /******************************************************** 506 * dslShowPage() 507 * 508 * Shows page with form and hidden confirmation 509 * 510 * Normally shows the blank form and then AJAX/Javascript 511 * handles responses and displaying feedback. 512 * But, if no Javascript on client, this routine has to 513 * show the page with feedback. 514 * 515 * $type parameter can be either: 516 * - 'blank' to show the blank form 517 * - 'with-feedback' to show the form with error or confirmation 518 */ 519 function dslShowPage($type, $msg = null){ 520 global $dslData; 521 // First tell WordPress to run the function that adds the scripts and styles to the page 522 add_action('wp_enqueue_scripts', 'dslEnq'); 523 // read template from file 524 $input = file_get_contents( dslCustomized( 'PATH', DSL_PAGE_TEMPLATE ) ); 525 // perform substitutions for placeholders 526 dslSubstitutePlaceholders($input); 527 // if no Javascript, format the response into the template 528 if ($type == 'with-feedback') { dslFormatFeedback($input, $msg); } 529 // send page headers 530 get_header(); 531 // send template 532 echo $input; 533 // send footers and sidebar 534 get_sidebar(); 535 get_footer(); 536 } 537 /******************************************************** 538 * dslFormatFeedback() 539 * 540 * When there is no Javascript on the client, we need to do everthing 541 * here that dsl.js normally handles 542 * 543 * Accepts the array of responses that normally would have been sent via AJAX 544 * and a reference to the blank template, and modifies the template string directly 545 */ 546 function dslFormatFeedback(&$blank, $msg){ 547 global $dslData; 548 if ( $msg['reset'] ) { 549 // perform reset, showing the form-level error 550 dslFormatReset($blank, $msg['errors']['dsl_pid']); 551 } elseif ( $msg['errors'] != null ) { 552 // redisplay the form with error messages 553 // put error messages into label spans 554 foreach ($msg['errors'] as $field => $txt) { 555 $pattern = '#(<label.*?for="' . $field . '".*?>.*?<span.*?class="dsl-error-field".*?>).*?</span>#s'; 556 $replacement = '$1<br />' . $txt . '</span>'; 557 $blank = preg_replace($pattern, $replacement, $blank); 558 } 559 // put values into inputs 560 $fieldsToDo = array('dsl_rname', 'dsl_raddress', 'dsl_sname', 'dsl_saddress'); 561 foreach ($fieldsToDo as $fld){ 562 $pattern = '#<input([^>]*?name="' . $fld . '")#s'; 563 $replacement = '<input value="' . htmlentities($msg['values'][$fld]) . '" $1'; 564 $blank = preg_replace($pattern, $replacement, $blank); 565 } 566 // put value into textarea 567 $pattern = '#(<textarea[^>]*?name="dsl_comments"[^>]*?>)(</textarea>)#'; 568 $replacement = '$1' . htmlentities($msg['values']['dsl_comments']) . '$2'; 569 $blank = preg_replace($pattern, $replacement, $blank); 570 // turn off CAPTCHA is already satisfied 571 if ( $msg['secure'] != null ) { 572 // put value of secure into input value 573 $pattern = '#(<input[^>]*?name="dsl_secure"[^>]*?value=)"("[^>]*?>)#'; 574 $replacement = '$1"' . $msg['secure'] . '$2'; 575 $blank = preg_replace($pattern, $replacement, $blank); 576 // hide captcha div 577 $pattern = '#(<div[^>]*?id="dsl-captcha")([^>]*?>)#'; 578 $replacement = '$1 style="display:none;" $2'; 579 $blank = preg_replace($pattern, $replacement, $blank); 580 } 581 } elseif ( $msg['confirms'] != null ) { 582 // send the confirm page 583 // use display:none to hide the form 584 $pattern = '#(<div[^>]*?id="dsl-form-div")([^>]*?>)#'; 585 $replacement = '$1 style="display:none;" $2'; 586 $blank = preg_replace($pattern, $replacement, $blank); 587 // use display:block to show the confirm 588 $pattern = '#(<div[^>]*?id="dsl-confirm-div")([^>]*?>)#'; 589 $replacement = '$1 style="display:block;" $2'; 590 $blank = preg_replace($pattern, $replacement, $blank); 591 // put the confirm text into the div 592 $pattern = '#(<div[^>]*?id="dsl-confirm-div"[^>]*?>.*?<div [^>]*?>)#s'; 593 $replacement = '$1' . $msg['confirms']['body']; 594 $blank = preg_replace($pattern, $replacement, $blank); 595 } else { 596 // general error reset 597 dslFormatReset( $blank, DSL_GENERAL_ERROR_MESSAGE ); 598 } 599 } 600 /******************************************************** 601 * dslFormatReset() 602 * 603 * do reset by not filling in values 604 * and not showing errors other than 605 * the general processing error 606 */ 607 function dslFormatReset(&$blank, $txt) { 608 // do reset by not filling in values 609 // and not showing errors other than 610 // the general processing error 611 $field = 'dsl_pid'; 612 $pattern = '#(<label.*?for="' . $field . '".*?>.*?<span.*?class="dsl-error-field".*?>).*?</span>#s'; 613 $replacement = '$1<br />' . $txt . '</span>'; 614 $blank = preg_replace($pattern, $replacement, $blank); 394 615 } 395 616 /******************************************************** … … 406 627 global $dslData; 407 628 // Each enqueue call causes WordPress to put the associated link or script 408 // tags into the <head> section of the page 409 // Always enqueue the base dsl styles from the plugin folder 629 // tags into the <head> section of the page. The first parameter is a handle so 630 // that task can be referred to later. 631 // First enqueue the base dsl styles file from the plugin folder. 410 632 wp_enqueue_style( 'dsl-css', DSL_PLUG_URL . DSL_CSS_FILE ); 411 // Enqueue the style overrides from the theme folder 633 // Enqueue the style overrides from the theme folder. 634 // The 3rd parameter is the handle from the previous call, and thus insures 635 // that this one comes later. 412 636 if ( file_exists(DSL_THEME_PATH . DSL_CSS_FILE) ) { 413 637 wp_enqueue_style( 'dsl-css-from-theme', DSL_THEME_URL . DSL_CSS_FILE, array('dsl-css') ); 414 638 } 415 wp_enqueue_script( 'dsl-js', DSL_PLUG_URL . DSL_JAVASCRIPT_FILE ); // js file 416 // The localize call puts a script into the <HEAD> section of the page 417 // that creates an object (ajaxData) containing an array of key:value pairs. 418 // Thus, in javascript ajaxData is a global variable that contains some 419 // values that the script needs in order to send a response via AJAX. 420 // Read templates from file to get the error-alert 421 $input = file_get_contents(DSL_OTHER_TEMPLATES_CALCULATED); 422 $input = dslGetSection($input, 'error-alert'); 423 dslSubstitutePlaceholders(&$input); 639 // Enqueue the Javascript file 640 wp_enqueue_script( 'dsl-js', DSL_PLUG_URL . DSL_JAVASCRIPT_FILE ); 641 // The localize call will put a script tag into the <HEAD> section of the page 642 // to create a Javascript object (ajaxData in this case) containing an array of key:value pairs. 643 // Thus, in the Javascript, ajaxData becomes a global variable that we use to send some data 644 // from the server. 424 645 wp_localize_script( 'dsl-js', 'ajaxData', 425 646 array( … … 427 648 'url' => admin_url( 'admin-ajax.php' ), 428 649 // Javascript includes this 'action' to tell WordPress which routine 429 // will handle the response as defined in the add_action('wp-ajax_')650 // will handle the AJAX response as defined in the add_action('wp-ajax_') call 430 651 'action' => 'dsl-form', 431 652 // this is text for the error alert 432 'errorAlert' => $input 653 'errorAlert' => sanitize_text_field( DSL_ERROR_ALERT ), 654 // this is text for an unexplained error 655 'errorGeneral' => sanitize_text_field( DSL_GENERAL_ERROR_MESSAGE ) 433 656 ) 434 657 ); … … 437 660 * dslCustomized() 438 661 * 439 * Given a path/filename relative to the theme or plugin folder, 440 * Returns a reference to it in the theme folder if the file exists there, 441 * or to it in the plugin folder if file exists there, or 662 * Return URL or PATH to file in theme folder or plugin folder 663 * 664 * Given a filename with path relative to the plugin folder, 665 * returns a reference to it in the theme folder if the file exists there, 666 * or to it in the plugin folder if it exists there, or 442 667 * FALSE if it exists in neither place. 668 * 443 669 * $type can be either URL or PATH 444 670 */ 445 671 function dslCustomized($type, $file){ 672 // check in theme folder 446 673 $tmp = DSL_THEME_PATH . $file; 447 674 if ( file_exists($tmp) ) { 675 // found it, so return URL or PATH to it 448 676 $tmp = ($type == 'PATH') ? $tmp : DSL_THEME_URL . $file; 449 677 } else { 678 // check in plugin folder 450 679 $tmp = DSL_PLUG_PATH . $file; 451 680 if ( file_exists($tmp) ) { 681 // found it, so return URL or PATH 452 682 $tmp = ($type == 'PATH') ? $tmp : DSL_PLUG_URL . $file; 453 683 } else { 684 // not found, so return false 454 685 $tmp = FALSE; 455 686 } … … 458 689 } 459 690 /******************************************************** 460 * dslPrepareMail() 691 * dslMakeCaptcha() 692 * 693 * Returns a Captchas.Net object 694 * 695 * This routine reads in all of the CAPTCHA customization parameters 696 * from a configuration file. 697 * The $id and $key must be replaced with values received by registering 698 * at http://captchas.net. 699 * 700 * Uses the web service at http://captchas.net 701 * See Captcha comparisons at http://davebezaire.com/tst/captcha/captcha.html 702 */ 703 function dslMakeCaptcha(){ 704 // load the configuration file, hopefully from the theme folder, 705 // with fallback to that in the plugin folder 706 include ( dslCustomized( 'PATH', DSL_CAPTCHA_CONFIG ) ); 707 // load the library that was copied from http://captchas.net 708 require_once(DSL_PLUG_PATH .'CaptchasDotNet.php'); 709 // create and return the object 710 return new CaptchasDotNet ($id, $key, $store, $time, $alphabet, $count, $width, $height, $color); 711 } 712 /******************************************************** 713 * dslSendMail() 714 * 715 * Sends email message 461 716 * 462 717 * Reads the email message template from an external file, 463 718 * formats the various parts of the email message, and 464 * returns them in the global array. The body of the message is 465 * also returned separately so that it can be 466 * displayed on the confirmation page. 467 */ 468 function dslPrepareMail(){ 469 global $dslData; 470 // read template from file 471 $input = file_get_contents(DSL_OTHER_TEMPLATES_CALCULATED); 719 * puts them in the global array. The body of the message is 720 * also included separately so that it can be 721 * displayed on the confirmation page. Then sends the email. 722 * 723 * Result of the mail send call is also put in the global array 724 */ 725 function dslSendMail(){ 726 global $dslData; 727 // read templates from file 728 $input = file_get_contents(dslCustomized( 'PATH', DSL_OTHER_TEMPLATES )); 729 // get the message section from the templates 472 730 $input = dslGetSection( $input, 'message'); 473 731 // perform substitutions for placeholders 732 // this puts in the names, receiver's address, sender's comments, post title, etc. 474 733 dslSubstitutePlaceholders(&$input); 475 // remove comments paragraphs if no comments 476 if (trim($dslData['dsl_comments']) == '') {$input = preg_replace("#<p\s*?class=\"comment\".*?</p>#s", "", $input);} 734 // if no comments, remove comments paragraphs 735 if (trim($dslData['dsl_comments']) == '') { 736 $input = preg_replace("#<p\s*?class=\"comment\".*?</p>#s", "", $input); 737 } 477 738 // extract the body for display on confirmation page 478 739 preg_match("#<body>(.*)</body>#s", $input, $tmp); … … 482 743 $msgSubj = $tmp[1]; 483 744 // subject is plain text, so convert emphasis tags to quotes, and remove htmlentities() 745 // and sanitize as text 484 746 $msgSubj = str_replace('<em>', '"', str_replace('</em>', '"', $msgSubj)); 485 747 $msgSubj = html_entity_decode($msgSubj); 748 $msgSubj = sanitize_text_field($msgSubj); 486 749 // To send HTML mail, the Content-type headers must be set 487 // Note that the charset is also mentioned in the <HEAD> above750 // Note that the charset is also mentioned in the <HEAD> in the template 488 751 $hdrs = 'MIME-Version: 1.0' . "\r\n"; 489 752 $hdrs .= 'Content-type: text/html; charset=utf-8' . "\r\n"; 490 $hdrs .= 'From:' . $dslData['dsl_sname'] . ' <' . $dslData['dsl_saddress'] . '>' . "\r\n"; 753 // add senders name and address in the headers 754 $hdrs .= 'From:' . sanitize_text_field($dslData['dsl_sname']) . ' <' . $dslData['dsl_saddress'] . '>' . "\r\n"; 491 755 // Store the results in the $dslData array 492 756 $dslData['mail-to'] = $dslData['dsl_raddress']; … … 495 759 $dslData['mail-message-body'] = $msgBody; 496 760 $dslData['mail-headers'] = $hdrs; 761 // send the email 762 $dslData['mail-result'] = mail( $dslData['mail-to'], 763 $dslData['mail-subject'], 764 $dslData['mail-message'], 765 $dslData['mail-headers']); 497 766 } 498 767 /******************************************************** … … 504 773 global $dslData; 505 774 // read template from file 506 $input = file_get_contents(DSL_OTHER_TEMPLATES_CALCULATED); 507 // select either error or confirm section 508 $out .= dslGetSection( $input, ($dslData['mail-result'] !== true) ? 'error' : 'confirm'); 509 // add on the navigation section 510 $out .= dslGetSection($input, 'nav'); 775 $input = file_get_contents(dslCustomized( 'PATH', DSL_OTHER_TEMPLATES )); 776 // get the confirm section 777 $out .= dslGetSection( $input, 'confirm'); 511 778 // perform the substitutions 512 dslSubstitutePlaceholders( &$out);779 dslSubstitutePlaceholders($out); 513 780 return $out; 514 781 } … … 516 783 * dslGetSection() 517 784 * 518 * Returns the contents of a section ofthe template file785 * Returns the contents of a section from the template file 519 786 */ 520 787 function dslGetSection($str, $tag) { … … 522 789 // the leading and trailing #'s are required delimiters 523 790 // the trailing 's' after the last # says that dot should match newlines 524 // the \s* followed by \S consumes white space, including newlines, between the end and start525 // of the tag brackets and included content791 // the \s* followed by \S consumes white space, including newlines, 792 // between the end and start of the tag brackets and included content 526 793 $pattern = '#<' . $tag . '>\s*(\S.*\S)\s*</' . $tag . '>#s'; 527 794 preg_match($pattern, $str, $match); // stores the first capture group in $match[1] … … 547 814 // Substitute current values for the placeholders 548 815 $template = str_replace("#dsl-post-title#", htmlentities($dslData['post']->post_title), $template); 549 $template = str_replace("#dsl-recipient-name#", $dslData['dsl_rname'],$template);816 $template = str_replace("#dsl-recipient-name#", sanitize_text_field($dslData['dsl_rname']), $template); 550 817 $template = str_replace("#dsl-recipient-address#", $dslData['dsl_raddress'], $template); 551 $template = str_replace("#dsl-sender-name#", $dslData['dsl_sname'],$template);818 $template = str_replace("#dsl-sender-name#", sanitize_text_field($dslData['dsl_sname']), $template); 552 819 $template = str_replace("#dsl-post-id#", $dslData['dsl_pid'], $template); 553 820 $template = str_replace("#dsl-post-author#", $author, $template); 554 821 $template = str_replace("#dsl-post-date#", $date, $template); 555 $template = str_replace("#dsl-back-link#", $dslData['dsl_back'],$template);822 $template = str_replace("#dsl-back-link#", esc_url($dslData['dsl_back']), $template); 556 823 $template = str_replace("#dsl-comments#", nl2br($dslData['dsl_comments']), $template); 557 $template = str_replace("#dsl-message-subject#", $dslData['mail-subject'],$template);824 $template = str_replace("#dsl-message-subject#", sanitize_text_field($dslData['mail-subject']), $template); 558 825 $template = str_replace("#dsl-message-body#", $dslData['mail-message-body'], $template); 559 $template = str_replace("#dsl-submit-button#", DSL_SUBMIT_BUTTON_VALUE, $template);826 $template = str_replace("#dsl-submit-button#", DSL_SUBMIT_BUTTON_VALUE, $template); 560 827 $template = str_replace("#wp-nonce#", wp_create_nonce('dsl-form'), $template); 561 828 $template = str_replace("#captcha-random#", $captcha->random (), $template); 562 829 $template = str_replace("#captcha-image#", $captcha->image(), $template); 563 830 $template = str_replace("#captcha-audio#", $captcha->audio_url (), $template); 564 $template = str_replace("#max-comment-chars#", DSL_MAX_COMMENT_CHARS, $template); 565 $template = str_replace("#min-name-chars#", DSL_MIN_NAME_CHARS, $template); 566 $template = str_replace("#max-name-chars#", DSL_MAX_NAME_CHARS, $template); 831 $template = str_replace("#max-comment-chars#", DSL_MAX_COMMENT_CHARS, $template); 832 $template = str_replace("#min-name-chars#", DSL_MIN_NAME_CHARS, $template); 833 $template = str_replace("#max-name-chars#", DSL_MAX_NAME_CHARS, $template); 834 } 835 /******************************************************** 836 * dslSetupDatabase() 837 * 838 * Do one-time, initial setup tasks. 839 * 840 * WordPress calls this routine when the user presses the "Activate" link 841 * in the Administrator's Plugins menu due to the register_activation_hook() call. 842 * Since there is no hookable event when an upgraded version is installed, 843 * we also call this routine on every run of the script after 844 * we know we will be handling the page request. 845 * 846 * This routine sets up a database table to log 847 * the IP address and time of each send. 848 */ 849 register_activation_hook( __FILE__, 'dslSetupDatabase' ); 850 function dslSetupDatabase() { 851 global $wpdb; 852 // check if the currently installed database version 853 // matches what will be installed by this routine, 854 // and if so, simply return 855 if ( get_option(DSL_NAME_OF_DB_VERSION_SETTING) == DSL_DB_VERSION ) return; 856 // Define the command that will tell WordPress to create our table. 857 // Note that this command is not strict SQL because the WordPress routines process it first. 858 // This table will be used to remember sends for just a very short time, a day at the most. 859 // It will store the user's IP address and the time of each send as follows: 860 // - idnum is a unique key for each record added to the table 861 // - ip is the address stored as a long integer by using PHP function ip2long() 862 // - time is the time of the last send stored as a UNIX time stamp which is in seconds 863 // - comment is for testing and learning about mySQL and myPhpAdmin 864 // idnum is used as the unique, primary index 865 // ip is indexed because it will be the search term on every query 866 // time is indexed because it will be the sort term on every query 867 $sqlCmd = "CREATE TABLE " . DSL_LOG_TABLE . " ( 868 idnum int NOT NULL AUTO_INCREMENT, 869 ip int, 870 time int, 871 comment text, 872 UNIQUE KEY idnum (idnum), 873 KEY ip (ip), 874 KEY time (time) 875 );"; 876 // Load the WordPress upgrade module which knows how to deal with structuring tables. 877 // In particular, it handles a "create" query by analysing what is already in place 878 // and changing it as necessary. 879 require_once( ABSPATH . 'wp-admin/includes/upgrade.php' ); 880 // the WordPress dbDelta function executes our command 881 $e = dbDelta ( $sqlCmd ); 882 // Put a setting in the WordPress options table with the Plugin's database version number 883 add_option( DSL_NAME_OF_DB_VERSION_SETTING, $wpdb->escape(DSL_DB_VERSION) ); 884 } 885 /******************************************************** 886 * dslDeActivate() 887 * 888 * Deletes the plugin's database table and option. 889 * 890 * WordPress calls this routine when the user presses the "Deactivate" link 891 * in the Administrator's Plugins menu. 892 */ 893 register_deactivation_hook( __FILE__, 'dslDeActivate' ); 894 function dslDeActivate() { 895 global $wpdb; 896 // delete options created by plugin 897 delete_option( DSL_NAME_OF_DB_VERSION_SETTING ); 898 // define command that will tell MySQL to delete the table created by plugin 899 $sqlCmd = 'DROP TABLE IF EXISTS ' . DSL_LOG_TABLE .';'; 900 // send command thru the WordPress query object 901 $e = $wpdb->query( $sqlCmd ); 902 } 903 /******************************************************** 904 * dslUpdateLog 905 * 906 * Adds a log record for IP address with time of last send 907 * and optional comment 908 * See http://stackoverflow.com/questions/2754340/inet-aton-and-inet-ntoa-in-php 909 * for info about storing an IP address in MySQL. 910 */ 911 function dslUpdateLog($ip, $time, $comment = '') { 912 global $wpdb; 913 // setup data to be stored 914 $fields = array( 915 'ip' => ip2long( $ip ), // IP address converted to long integer 916 'time' => $time, // current time as UNIX timestamp 917 'comment' => $wpdb->escape( $comment ) // optional comment 918 ); 919 // Tell WordPress to insert the record 920 $wpdb->insert( DSL_LOG_TABLE, $fields ); 921 } 922 /******************************************************** 923 * dslReadLog($ip) 924 * 925 * Returns a log object with: 926 * - the most recent log record for the given IP address, or 927 * - if no existing record for that IP, a dummy record with time equal to zero. 928 */ 929 function dslReadLog($ip) { 930 global $wpdb; 931 // Define sql query to get all records for this IP and sort them 932 // by time descending 933 $sqlCmd = ' SELECT * FROM ' . DSL_LOG_TABLE . 934 ' WHERE ip = ' . ip2long($ip) . 935 ' ORDER BY time DESC' 936 ; 937 // Tell WordPress to execute the query and return the top row 938 $logRow = $wpdb->get_row( $sqlCmd ); 939 // If row was found, convert IP back from integer to string 940 // If not found, create a dummy row with time of zero 941 if ( $logRow != null ) { 942 $logRow->ip = long2ip($logRow->ip); 943 return $logRow; 944 } else { 945 return (object)array('ip' => $ip, 'time' => 0, 'comment' => 'dummy'); 946 } 947 } 948 /******************************************************** 949 * dslFlushLog($ip) 950 * 951 * Deletes old records from the log 952 * 953 * Old are those earlier than the current time 954 * less the DSL_LOG_KEEP_SECONDS. 955 */ 956 function dslFlushLog() { 957 global $wpdb; 958 // calculate the earliest time to be kept 959 $deleteTime = time() - DSL_LOG_KEEP_SECONDS; 960 // Define sql command to find earlier records and delete them 961 $sqlCmd = ' DELETE FROM ' . DSL_LOG_TABLE . 962 ' WHERE time < ' . $deleteTime 963 ; 964 // Tell WordPress to execute the query 965 $wpdb->query( $sqlCmd ); 567 966 } 568 967 ?> -
dlbs-send-a-link/trunk/readme.txt
r726782 r728099 5 5 Requires at least: 3.5.1 6 6 Tested up to: 3.5.1 7 Stable tag: 0. 457 Stable tag: 0.95 8 8 License: GPLv2 or later 9 9 License URI: http://www.gnu.org/licenses/gpl-2.0.html … … 12 12 13 13 == Description == 14 *dlb's Send-A-Link* allows visitors to send someone an email containing a link to the post or page. This is a preliminary, developmental release, and will be replaced by the first full release no later than June 30, 2013. Key features include:14 *dlb's Send-A-Link* allows visitors to send someone an email containing a link to the post or page. This version, 0.95, is the final candidate for release, and will be superceded by version 1.0 after testing is complete, no later than June 25, 2013. 15 15 16 - A shortcode can be put in your template(s) to show visitors an icon or text link to send an email. 16 Key features include: 17 18 - Show visitors a link (icon and/or text) to send an email by adding a function call to your templates or a shortcode to your pages/posts. 17 19 - The input form, confirmation page, and email message are all based on easily modifiable HTML templates. 20 - Spam protection includes CAPTCHA verification and limiting any given IP address to two messages per minute. 18 21 - *Send-A-Link* loads fast because code is compact and does not use jQuery. 19 - *Send-A-Link* uses Javascript/AJAX for fast, inobtrusive form handling .20 - Built and tested on WordPress v3.5.1; although it might work on earlier versions, it has not been test on them.22 - *Send-A-Link* uses Javascript/AJAX for fast, inobtrusive form handling, but it degrades gracefully to provide full functionality to clients without Javascript. 23 - Built and tested on WordPress v3.5.1; although it might work on earlier versions, it has not been tested on them. 21 24 22 The *Send-A-Link* code is heavily commented, especially regarding WordPress plugin interfaces and AJAX features. This makes it a suitable starting point for novice developers to explore their own programming interests. I have attempted to make the code very understandable, but I do not claim this to be *model* code because I am not an experienced developer myself. In fact, I would greatly appreciate constructive criticism. 23 24 In the future, I willrelease new versions with the following features, in approximately this order:25 *dlb's Send-A-Link* code is heavily commented, especially regarding WordPress plugin interfaces and AJAX features. This makes it a suitable starting point for novice developers to explore their own programming interests. I have attempted to make the code very understandable. However, I do not claim this to be *model* code because I am not an experienced developer myself. In fact, I would greatly appreciate constructive criticism. 26 27 In the future, I plan to release new versions with the following features, in approximately this order: 25 28 26 29 - Privacy reassurance that email addresses are not stored, but IP's are 27 - Log mail sent with user's IP address to a file28 - Throttle sends to some number per time period from a given IP29 - Provide full functionality without Javascript30 30 - Localize to be ready for translation, including delivery of po and mo files and some instruction on how to use POEDIT to customize the wording, regardless of language. This could especially apply to the error messages. 31 - Attempt to use PHP's DOM handling routines for more robust and reliable formatting of responses when client does not have Javascript available 32 - Use a stored procedure to flush log daily to reduce number of times it is done 31 33 - Rewrite using class construction 32 - Add admin screens to modify things now defined as constants and templates 33 - Move logging into database 34 - Add admin screens to specify some things now defined in constants and templates 34 35 - Make it easy to turn CAPTCHA off or on 35 36 - Make it easy to use other CAPTCHAs … … 41 42 The manual method requires several steps: (1) Download the zip file to your computer. (2) Unzip the file. (3) Upload the `dlbs-send-a-link` folder to your `wp-content/plugins` directory. (4) Go to `Plugins > Installed Plugins` in your blog's Administration menu and click the `Activate` link under *dlb's Send-A-Link*. 42 43 43 Either way, you simply insert the shortcode `[dsl-link]` in your posts and pages wherever you want to show a link or icon to your visitors offering the opportunity for them to send a message. In your templates, you can insert the code: `if( function_exists('dslShortcode') ) { echo dslShortcode( array('show'=>'both') ); }` 44 Customization of *dlb's Send-A-Link* is further described in the FAQ. Generally, it will be necessary to copy `dsl-page.html`, `dsl-templates.html`, and `dsl.css` from the plugin folder to your theme folder. Modify `dsl-page.html` to match the structure of your theme's `page.php` file. Modify `dsl-templates.html` to reflect the URL of your blog. Modify `dsl.css` to match the look & feel of your blog. 44 45 45 Optionally, you can customize *dlb's Send-A-Link* as described in the FAQ. 46 You need to register at http://captchas.net (It's free!) to obtain your own `Username` and `Secret Key`. Copy file `dsl-captcha.php` from the plugin folder to the theme folder. Change the values of `$id` and `$key` from "demo" and "secret" to your own `Username` and `Secret Key` respectively. 47 48 Finally, insert the shortcode `[dsl-link]` in your posts/pages wherever you want to show a link or icon to your visitors offering the opportunity to send a message. In your templates, insert the function `dslLink()`, typically as: `if( function_exists('dslLink') ) { echo dslLink(); }`. See FAQ to customize it. 49 46 50 47 51 == Frequently Asked Questions == … … 57 61 = Can I change the icon and text in the link? = 58 62 59 Yes, by using these parameters in the shortcode or in the template code: `[dsl-link show="both" iconfile="Your-Icon.gif" text="Your Text" ]`. The value of `show` can be one of "icon", "text", or "both" to display only the icon, only the text or both. The value of `iconfile` must be the name of your file with a path relative to the plugin folder. The value of `text` will be the title which is typically shown as a tooltip, and will be displayed if `show` is either "text" or "both". 63 Yes. The parameters for shortcode `[dsl-link]` and function `dslLink()` are as follows: `show` can be "icon", "text", or "both" to display only the icon, only the text or both; `iconfile` is the name of your icon file, with a path relative to the plugin folder; `text` is shown as a tooltip and on the link if `show` is either "text" or "both". In the shortcode, enter them like this: `[dsl-link show="both" text="Your Text" iconfile="myIcon.gif" ]`. In the function, enter them like this: `dslLink( array( "show" => "both" , "text" => "Your Text" , "iconfile" => "myIcon.gif" ) )`. 64 65 = How can I change to wording on the form's submit button? = 66 67 This can only be changed in file `dsl.php`. Search for the definition of `DSL_SUBMIT_BUTTON_VALUE`. This change will need to be made again after an upgrade to a new version. 68 69 = How can I change the maximum number of characters allowed in names and comments? = 70 71 This can only be changed in file `dsl.php`. Search for the definitions of `DSL_MAX_COMMENT_CHARS`, `DSL_MAX_NAME_CHARS`, and `DSL_MIN_NAME_CHARS`. These changes will need to be made again after an upgrade to a new version. 72 73 = How can I change the minimum time between sends? = 74 75 This can only be changed in file `dsl.php`. Search for the definition of `DSL_MIN_SEND_INTERVAL`. This change will need to be made again after an upgrade to a new version. 60 76 61 77 = How can I change the wording in the error messages? = 62 78 63 In this version the error messages must be changed by editing file `dsl.php`, which is heavily commented to make it easier to navigate and alter the code. A future release will make this easier by using the WordPress localization system.79 In this version of the plugin, the error messages must be changed by editing file `dsl.php`, which is heavily commented to make it easier to navigate and alter the code. A future release will make this easier by using the WordPress localization system. These changes will need to be made again after an upgrade to a new version. 64 80 65 81 == Screenshots == … … 69 85 == Changelog == 70 86 87 = 1.0 = 88 * Degrades gracefully to provide full functionality to users without Javascript 89 * Loads CAPTCHA configuration file from theme folder 90 91 = 0.5 = 92 * Implements activate and deactivate routines to provide a database table for logging 93 * Logs IP address and time for every mail sent 94 * Flushes all log records more than one day old 95 * Limits send rate from an IP address to one message every 30 seconds 96 * Moved the last bit of visible text from Javascript file to template file 97 71 98 = 0.45 = 72 * Added to the WordPress.org Plugin Directory73 * Improved the installation instructions in readme.txt99 * Added *dlb's Send-A-Link* to the WordPress.org Plugin Directory 100 * Improved the formatting and installation instructions in readme.txt 74 101 75 102 = 0.4 = 76 Initial release: 77 78 * Allows visitors to send someone an email containing a link to the post or page. 103 * Initial release 104 * Allows visitors to send someone an email containing a link to the post/page. 79 105 * Visitor can optionally include comments in the message. 80 * Provides a shortcode to put into templates to show an icon or text link to send an email.106 * Provides a function to put into templates and a shortcodeto to put into posts/pages to show an icon or text link to send an email. 81 107 * The input form, confirmation page, and email message are based on HTML templates 82 * Customized CSS styles and templates can be stored in the theme 108 * Customized CSS styles and templates can be stored in the theme folder 83 109 * Uses Javascript/AJAX for fast, inobtrusive form handling. 84 110 * Loads fast, based on compact code that does not use jQuery. … … 88 114 == Upgrade Notice == 89 115 116 = 0.95 = 117 This is the final candidate for release, and will be superceded by version 1.0 after testing is complete, no later than June 25, 2013. 118 90 119 = 0.45 = 91 120 Minor testing and `readme.txt` changes only. No need to apply this upgrade. 92 121 93 122 = 0.4 = 94 Initial release.123 Initial developmental release.
Note: See TracChangeset
for help on using the changeset viewer.