Skip to content

Instantly share code, notes, and snippets.

@franz-josef-kaiser
Created June 10, 2011 15:59
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save franz-josef-kaiser/1019141 to your computer and use it in GitHub Desktop.
Save franz-josef-kaiser/1019141 to your computer and use it in GitHub Desktop.
WordPress Plugin to check your DataBase for injected links
<?php
/**
Plugin Name: AntiScript deamon
Plugin URI: https://github.com/franz-josef-kaiser
Description: Removes script-links to spam sites from your post content after your site got hacked. Please go to <a href="tools.php?page=script_deamon.php">Tools &rarr; Remove Hack</a>. Thank you. Proudly brought to you by <a href="http://example.com">Franz Josef Kaiser</a>.
Version: 0.1
Author: Franz Josef Kaiser
Author URI: https://github.com/franz-josef-kaiser
License: GPL2
WP-Version: Tested in 2.7.1, 2.9.2, 3.0
==================================================================================================
* Copyright (C) 2010 Franz Josef Kaiser
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
==================================================================================================
*/
define( DEAMON, 'AntiScript deamon' );
/**
* Register Admin Parent Page
*/
function dmn_add_admin_page() {
add_management_page(
__('Remove hacked content', 'deamon_lang'),
__('Remove Hack', 'deamon_lang'),
'administrator',
'script_deamon.php',
'dmn_page_constructor');
}
add_action('admin_menu','dmn_add_admin_page');
/**
* Styles: go in admin_head hook
*/
add_action( 'admin_head', 'dmn_print_styles' );
function dmn_print_styles() {
echo '<style>';
echo '.dmn-infected { color: red; }';
echo '.dmn-not-infected { color: #97bf0d; }';
echo '.info-box { background-color: #fff; border: 1px solid #dfdfdf; margin: 0 0 20px; padding: 0 0 0 10px; -mox-border-radius: 6px 6px 6px 6px; -webkit-border-radius: 6px 6px 6px 6px; }';
echo '</style>';
}
/**
* LANGUAGES: load textdomain
*/
define('PLUGIN_LANG_DIR', ABSPATH . 'wp-content/plugins/script_deamon/lang');
load_theme_textdomain('deamon_lang');
$locale = get_locale();
$locale_file = PLUGIN_LANG_DIR . '/$locale.php';
if ( file_exists($locale_file) && is_readable($locale_file) ) {
require_once($locale_file);
} else {
return;
}
/**
* Shorten the content lenght - no need to display everything
* @see dmn_list_infected_posts()
*/
function dmn_shorten_post_content( $post_content, $max_length = 25 ) {
if( strlen( $post_content ) < $max_length ) {
return $post_content;
}
$regex = "/(.{1,$limit})\b/";
preg_match( $regex, $post_content, $matches );
return $matches[1];
}
/**
* Case insensitive search inside an array
* @see dmn_list_infected_posts()
*/
function array_insearch( $needle, array $haystack ) {
$search_me = new IteratorIterator( new ArrayIterator( $haystack ));
foreach( $search_me as $key => $value ) {
if ( strcasecmp( $value, $needle ) === 0 ) {
return $value;
}
}
return FALSE;
}
/**
* List all posts and show which are infected
*/
function dmn_list_infected_posts() {
/*******************************
* Text Strings (translatable)
*******************************/
$dmn_opt_text_3b = __('
Note: Couldn\'t find anything? Than maybe it\'s still hidden in the DB.<br />
In this case you should inspect your (posts-)table via ex. PhpMyAdmin manually and search for something like:<br />
<code>script src="http://example.com/etc</code><br />
at the end of posts, pages, attachments and revisions.<br />
... But mabye your blog just isn\'t infected.
', 'deamon_lang');
echo '<table class="widefat" rules="rows">';
echo __('<caption><b class="dmn-infected">Infected</b> - vs. - <i class="dmn-not-infected">Not infected</i></caption>', 'deamon_lang');
echo '<thead>';
echo '<tr>';
echo '<th class="manage-column" scope="col">ID</th>';
echo '<th class="manage-column" scope="col">Title</th>';
echo '<th class="manage-column" scope="col">Link in Content</th>';
echo '<th class="manage-column" scope="col">Type</th>';
echo '</tr>';
echo '</thead>';
echo '<tfoot>';
echo '<tr>';
echo '<th class="manage-column" scope="col">ID</th>';
echo '<th class="manage-column" scope="col">Title</th>';
echo '<th class="manage-column" scope="col">Link in Content</th>';
echo '<th class="manage-column" scope="col">Type</th>';
echo '</tr>';
echo '</tfoot>';
echo '<tbody>';
// to be sure we got nothing left from some ghost query
wp_reset_query();
// Start our new query
global $wpdb;
$dmn_list_of_posts = $wpdb->get_results("SELECT * FROM $wpdb->posts");
// We don't want the script to execute in the Admin-UI
$dmn_allowed_protocols_in_post = array('script' => array('' => array()));
// Let's deliver some information, about what & how much was infected
$dmn_inf_post_count = '0';
$dmn_inf_page_count = '0';
$dmn_inf_rev_count = '0';
$dmn_inf_att_count = '0';
$dmn_inf_total_count = '0';
// We need an array to save the infected post IDs
$dmn_inf_post_ids = array();
// We need an another array to save the infecting URls
$dmn_inf_urls = array();
// Now make the table rows with the post data
foreach ( $dmn_list_of_posts as $possibly_infected_post ) {
$dmn_post_content = $possibly_infected_post->post_content;
// INFECTED
if ( preg_match("/^<script src='http:.|script>/i", $dmn_post_content) ) {
$extract_mal_script_part_one = explode('<script src="http://', $dmn_post_content);
$extract_mal_script_part_two = explode('">', $extract_mal_script_part_one['1']);
echo '<tr class="dmn-infected">';
echo '<td>' . $possibly_infected_post->ID . '</td>';
echo '<td><b>' . $possibly_infected_post->post_title . '</b></td>';
echo '<td>' . $extract_mal_script_part_two['0'] . '</td>';
echo '<td>' . $possibly_infected_post->post_type . '</td>';
echo '</tr>';
// Count the different post types separate
if ( $possibly_infected_post->post_type == 'post' ) {
$dmn_inf_post_count++;
}
else if ( $possibly_infected_post->post_type == 'page' ) {
$dmn_inf_page_count++;
}
else if ( $possibly_infected_post->post_type == 'revision' ) {
$dmn_inf_rev_count++;
}
else if ( $possibly_infected_post->post_type == 'attachment' ) {
$dmn_inf_att_count++;
}
$dmn_inf_total_count++;
// Fill the array with yet another infected ID
array_push($dmn_inf_post_ids, $possibly_infected_post->ID);
if( array_insearch( $extract_mal_script_part_two['0'], $dmn_inf_urls ) != $extract_mal_script_part_two['0'] ) {
array_push( $dmn_inf_urls, $extract_mal_script_part_two['0'] );
}
// NOT INFECTED
} else {
echo '<tr class="dmn-not-infected">';
echo '<td>' . $possibly_infected_post->ID . '</td>';
echo '<td><i>' . $possibly_infected_post->post_title . '</i></td>';
echo '<td>' . dmn_shorten_post_content( wp_kses_bad_protocol($possibly_infected_post->post_content, $allowed_protocols_in_post), 50 ) . '</td>';
echo '<td>' . $possibly_infected_post->post_type . '</td>';
echo '</tr>';
}
}
// We need to save the IDs somewhere, so we use the options table.
add_option('dmn_infected_ids', $dmn_inf_post_ids, $deprecated = '', $autoload = 'yes');
add_option('dmn_inf_urls', $dmn_inf_urls, $deprecated = '', $autoload = 'yes');
// Show the stats
$dmn_total_count = count($dmn_list_of_posts);
echo '<p>';
if ( $dmn_inf_total_count == $dmn_total_count ) {
printf( __('<h3>All of your %1$s posts, pages, attachments & revision are infected!</h3>', 'deamon_lang'), $dmn_total_count);
}
else if ( $dmn_inf_total_count == '0' ) {
echo '<h3>' . __('It seems, that your post haven\'t been infected. Happy blogging, lucky one! :)', 'deamon_lang') . '</h3>';
}
else {
printf(
__('<b>Possibly Infected Content:</b> %1$s posts, %2$s pages, %3$s revisions, %4$s attachments = %5$s of total %6$s', 'deamon_lang'),
'<i>' . $dmn_inf_post_count . '</i>',
'<i>' . $dmn_inf_page_count . '</i>',
'<i>' . $dmn_inf_rev_count . '</i>',
'<i>' . $dmn_inf_att_count . '</i>',
'<b>' . $dmn_inf_total_count,
$dmn_total_count . '</b>'
);
}
echo '</p>';
echo '</tbody>';
echo '</table>';
echo '<div class="info-box"><p><i>' . $dmn_opt_text_3b . '</i></p></div>';
} // END dmn_list_infected_posts
/**
* Flattens our array
* @see dmn_list_mal_ids()
*/
function array_flatten( $array, $flat = array() ){
if ( !$array || !is_array( $array ) ) {
return '';
}
foreach( $array as $key => $value ) {
if ( is_array( $value ) ) {
$flat = array_flatten( $value, $flat );
}
else {
$flat[$k] = $value;
}
}
return $flat;
}
/**
* List all malicious IDs
*/
function dmn_list_mal_ids() {
// Translation:
$list_mal_ids_headline = __('List of infected IDs that were saved as option in the database', 'deamon_lang');
$list_of_inf_ids = get_option('dmn_infected_ids');
array_flatten( $list_of_inf_ids );
$last_in_list_of_inf_ids = array_pop( $list_of_inf_ids );
echo '<table class="widefat"><thead><tr><th>' . $list_mal_ids_headline . '</th></tr></thead><tbody><tr><td>';
foreach( array_values( $list_of_inf_ids ) as $key => $value ) {
echo $value . ', ';
}
echo $last_in_list_of_inf_ids;
echo '</td></tr></tbody></table>';
}
/**
* Delete the infecting urls and infected IDs option from the db, so it can be redone.
*/
function dmn_undo_opt_arrays() {
delete_option('dmn_infected_ids');
delete_option('dmn_inf_urls');
}
/**
* Delete the script out of the post table
*/
// Note what will happen when you pull the trigger.
function dmn_kill_mal_script_note() {
// Translation:
$dmn_opt_text_3d = __('
Hitting the button below will clean up your database.<br />
Note, that this can\'t be undone.<br />
<b>Please be patient.</b> This may take a while, depending on the total amount of content in your database. Think in minutes.
', 'deamon_lang');
echo '<div class="info-box"><p>' . $dmn_opt_text_3d . '</p></div>';
}
// The infamous KillScript function
function dmn_kill_mal_script() {
// to be sure we got nothing left from some ghost query
wp_reset_query();
// Start our new query
global $wpdb;
$dmn_list_of_all_posts = $wpdb->get_results("SELECT * FROM $wpdb->posts");
// Load the infected urls
$dmn_inf_urls = get_option('dmn_inf_urls');
foreach( $dmn_list_of_all_posts as $single_post_in_list ) {
unset( $dmn_clean_post );
$dmn_clean_post = array();
$dmn_single_post_content = $single_post_in_list->post_content;
foreach( $dmn_inf_urls as $inf_url ) {
$search_in_content = '<script src="http://' . $inf_url . '"></script>';
$replace_in_content = '';
$dmn_single_post_content = str_ireplace( $search_in_content, $replace_in_content, $dmn_single_post_content );
}
$dmn_clean_post['ID'] = $single_post_in_list->ID;
$dmn_clean_post['post_content'] = $dmn_single_post_content;
wp_update_post( $dmn_clean_post );
}
}
/**
* Admin-UI Page Construtor
*/
function dmn_page_constructor() {
/*******************************
* Text Strings (translatable)
*******************************/
# Step 1
$dmn_opt_title_1 = __('Your' . ' ' . DEAMON . ': ' . 'Introduction', 'deamon_lang');
$dmn_opt_text_1a = __('
<h3>Welcome to Step 1!</h3><br/>
<p>
<b>This is a short introduction about what will happen in the following steps:</b>
</p>
<hr />
<p>
<i>Step 2)</i> Take a look at the infected posts (can be: published posts, revisions, pages & attachments.) and save them to the database when performaing step 2.<br/>
<i>Step 3)</i> Get the infected post IDs from the database and see if we have got some.<br />
<i>Step 4)</i> Clean the infected post content from the malicius script.<br />
<i>Step 5)</i> Clean the options table in the database from the options we added in step 4. <u>Finished</u>.
</p>
','deamon_lang');
$dmn_button_value_1a = __('Step 2: Show infected posts &raquo;', 'deamon_lang');
# Step 2
$dmn_opt_title_2 = __( DEAMON . ': ' . 'post_table analysis', 'deamon_lang');
$dmn_opt_text_2a = __('
<h3>Step 2</h3><br/>
<p><i>Note: Below is a list of post/page/att./rev.<br />
We tried to catch the infected IDs and saved them to the Database.</i><br />
<a href="#go_to_step_three">You can perform Step 3 at the end of the list.</a></p>
', 'deamon_lang');
$dmn_button_value_2a = __('Step 3: Check if we have saved the infected IDs &raquo;', 'deamon_lang');
$dmn_button_value_2b = __('&laquo; Go back to Step 1', 'deamon_lang');
# Step 3
$dmn_opt_title_3 = __( DEAMON . ': ' . 'Check the DB', 'deamon_lang');
$dmn_opt_text_3a = __('
<h3>Step 3</h3><br/>
<p>Show the list of infected IDs, if they are in the DB.<br /></p>
<p><i>Note: This will permanently delete all script-strings containing the url entered in the input in the post_content fields of your posts database table.<br />
The Plug-In searches and replaces every string with an empty string. This may take some time, so please be patient.<br />
You can deactivate the plugin after the database was cleaned. No options will be left in the options table of your database.</i><br />
<a href="#go_to_step_four">You can perform Step 4 at the end of the list.</a></p>
', 'deamon_lang');
$dmn_button_value_3a = __('Step 4: Go & kill the malicious Script, deamon! &raquo;', 'deamon_lang');
$dmn_button_value_3b = __('&laquo; Go back to Step 2', 'deamon_lang');
# Step 4
$dmn_opt_title_4 = __( DEAMON . ': ' . 'Script removed!', 'deamon_lang');
$dmn_opt_text_4a = __('
<h3>Step 4</h3><br/>
<p><i>Now we can go and clean up the database. Your deamon can clean up for you and delete the options needed during the hack removal process.<br /></p>
', 'deamon_lang');
$dmn_button_value_4a = __('Last Step: Go & clean up the database, deamon! &raquo;', 'deamon_lang');
$dmn_button_value_4b = __('&laquo; Go back to Step 3', 'deamon_lang');
# Step 5
$dmn_opt_title_5 = __( DEAMON . ': ' . 'Finished!', 'deamon_lang');
$dmn_opt_text_5a = __('
<h3>Gratulations!</h3><br/>
<p>You have finished the removal of the hacked content in your posts-table. Additionally the deamon has cleaned up his room
and deleted everything he had to add to the database during the script removal process.</p>
<p>Please be shure to secure your blog as much as possible. To reach this goal, we recommend to do (as a minimum) the following:<br />
</p>
', 'deamon_lang');
/*******************************
* Requests
* @see dmn_hook
*******************************/
# STEP 2
if ( $_REQUEST['do_step_two'] ) {
echo '<div id="message" class="updated fade"><p>Your' . ' ' . DEAMON . ' ' . 'says: ' . __('posts are listed now. You successfully performed', 'deamon_lang') . ' ' . '<strong>' . __('Step 1!', 'deamon_lang') . '</strong></p></div>';
add_action('dmn_hook', 'dmn_list_infected_posts');
}
if ( $_REQUEST['undo_step_two'] ) {
echo '<div id="message" class="updated fade"><p>Your' . ' ' . DEAMON . ' ' . 'says: ' . __('you went back to', 'deamon_lang') . ' ' . '<strong>' . __('Step 1.', 'deamon_lang') . '</strong></p></div>';
remove_action('dmn_hook', 'dmn_list_infected_posts');
}
# STEP 3
if ( $_REQUEST['do_step_three'] ) {
echo '<div id="message" class="updated fade"><p>Your' . ' ' . DEAMON . ' ' . 'says: ' . __('post-IDs are saved into the options table now. You successfully performed', 'deamon_lang') . ' ' . '<strong>' . __('Step 2!', 'deamon_lang') . '</strong></p></div>';
remove_action('dmn_hook', 'dmn_list_infected_posts');
add_action('dmn_hook', 'dmn_list_mal_ids');
add_action('dmn_hook', 'dmn_kill_mal_script_note');
}
if ( $_REQUEST['undo_step_three'] ) {
echo '<div id="message" class="updated fade"><p>Your' . ' ' . DEAMON . ' ' . 'says: ' . __('you went back to', 'deamon_lang') . ' ' . '<strong>' . __('Step 2.', 'deamon_lang') . '</strong></p></div>';
remove_action('dmn_hook', 'dmn_list_mal_ids');
remove_action('dmn_hook', 'dmn_kill_mal_script_note');
add_action('dmn_hook', 'dmn_list_infected_posts');
dmn_undo_opt_arrays(); // in case we want to list the posts again and save newer entries to the database
}
# STEP 4
if ( $_REQUEST['do_step_four'] ) {
echo '<div id="message" class="updated fade"><p>Your' . ' ' . DEAMON . ' ' . 'says: ' . __('infected posts', 'deamon_lang') . ' ' . '<strong>' . __('have been killed!', 'deamon_lang') . '</strong></p></div>';
remove_action('dmn_hook', 'dmn_list_mal_ids');
remove_action('dmn_hook', 'dmn_kill_mal_script_note');
add_action('dmn_hook', 'dmn_kill_mal_script');
}
if ( $_REQUEST['undo_step_four'] ) {
echo '<div id="message" class="updated fade"><p>Your' . ' ' . DEAMON . ' ' . 'says: ' . __('you went back to', 'deamon_lang') . ' ' . '<strong>' . __('Step 3.', 'deamon_lang') . '</strong></p></div>';
remove_action('dmn_hook', 'dmn_list_infected_posts');
add_action('dmn_hook', 'dmn_list_mal_ids');
add_action('dmn_hook', 'dmn_kill_mal_script_note');
}
# STEP 5: Finish
if ( $_REQUEST['do_step_five'] ) {
echo '<div id="message" class="updated fade"><p>Your' . ' ' . DEAMON . ' ' . 'says: ' . __('Database cleaned!', 'deamon_lang') . ' ' . '<strong>' . __('You\'re finished!', 'deamon_lang') . '</strong></p></div>';
remove_action('dmn_hook', 'dmn_kill_mal_script');
add_action('dmn_hook', 'dmn_undo_opt_arrays');
}
/*******************************
* Page Constructor
*******************************/
echo '<div class="wrap">';
echo '<div id="icon-tools" class="icon32"><br /></div>';
echo '<div>';
// HEAD LINE
// Step 1
if ( !has_action('dmn_hook', 'dmn_list_infected_posts') && !has_action('dmn_hook', 'dmn_list_mal_ids') && !has_action('dmn_hook', 'dmn_kill_mal_script') && !has_action('dmn_hook', 'dmn_undo_opt_arrays') ) {
echo '<h2>' . $dmn_opt_title_1 . '</h2>';
}
// Step 2
else if ( has_action('dmn_hook', 'dmn_list_infected_posts') ) {
echo '<h2>' . $dmn_opt_title_2 . '</h2>';
}
// Step 3
else if ( has_action('dmn_hook', 'dmn_list_mal_ids') ) {
echo '<h2>' . $dmn_opt_title_3 . '</h2>';
}
// Step 4
else if ( has_action('dmn_hook', 'dmn_kill_mal_script') ) {
echo '<h2>' . $dmn_opt_title_4 . '</h2>';
}
// Step 5
else if ( has_action('dmn_hook', 'dmn_undo_opt_arrays') ) {
echo '<h2>' . $dmn_opt_title_5 . '</h2>';
}
// RWD-BUTTONS for different steps
echo '<form method="post">';
// (Go Back to:) Step 1
if ( has_action('dmn_hook', 'dmn_list_infected_posts') ) { // Start unlist button-form: above list
echo '<p id="go_to_step_one" class="submit">';
echo '<input class="button-secondary" name="undo_step_two" type="submit" value="' . $dmn_button_value_2b . '" />';
echo '<input type="hidden" name="action" value="undo_step_two" />';
echo '</p>';
}
// Step 2
else if ( has_action('dmn_hook', 'dmn_list_mal_ids') ) {
echo '<p id="go_to_step_two" class="submit">';
echo '<input class="button-secondary" name="undo_step_three" type="submit" value="' . $dmn_button_value_3b . '" />';
echo '<input type="hidden" name="action" value="undo_step_three" />';
echo '</p>';
}
// Step 3
else if ( has_action('dmn_hook', 'dmn_kill_mal_script') ) {
echo '<p id="go_to_step_three" class="submit">';
echo '<input class="button-secondary" name="undo_step_four" type="submit" value="' . $dmn_button_value_4b . '" />';
echo '<input type="hidden" name="action" value="undo_step_four" />';
echo '</p>';
}
echo '</form>'; // End unlist button-form
// INSTRUCTIONS
// Step 1
if ( !has_action('dmn_hook', 'dmn_list_infected_posts') && !has_action('dmn_hook', 'dmn_list_mal_ids') && !has_action('dmn_hook', 'dmn_kill_mal_script') && !has_action('dmn_hook', 'dmn_undo_opt_arrays') ) {
echo '<div class="info-box"><p>' . $dmn_opt_text_1a . '</p></div>';
}
// Step 2
else if ( has_action('dmn_hook', 'dmn_list_infected_posts') ) {
echo '<div class="info-box"><p>' . $dmn_opt_text_2a . '</p></div>';
}
// Step 3
else if ( has_action('dmn_hook', 'dmn_list_mal_ids') ) {
echo '<div class="info-box"><p>' . $dmn_opt_text_3a . '</p></div>';
}
// Step 4
else if ( has_action('dmn_hook', 'dmn_kill_mal_script') ) {
echo '<div class="info-box"><p>' . $dmn_opt_text_4a . '</p></div>';
}
// Step 4
else if ( has_action('dmn_hook', 'dmn_undo_opt_arrays') ) {
echo '<div class="info-box"><p>' . $dmn_opt_text_5a . '</p></div>';
}
echo '<form method="post">'; // Start form
/*******************************
* THE CONTENT HOOK
* THE HEART OF THE PLUGIN.
*******************************/
do_action('dmn_hook');
// FWD-BUTTONS for different steps
// (Go to:) Step 2
if ( !has_action('dmn_hook', 'dmn_list_infected_posts') && !has_action('dmn_hook', 'dmn_list_mal_ids') && !has_action('dmn_hook', 'dmn_kill_mal_script') && !has_action('dmn_hook', 'dmn_undo_opt_arrays') ) {
echo '<p class="submit">';
echo '<input class="button-primary" name="do_step_two" type="submit" value="' . $dmn_button_value_1a . '" />';
echo '<input type="hidden" name="action" value="do_step_two" />';
echo '</p>';
}
// Step 3
else if ( has_action('dmn_hook', 'dmn_list_infected_posts') ) {
echo '<p id="go_to_step_three" class="submit">';
echo '<input class="button-primary" name="do_step_three" type="submit" value="' . $dmn_button_value_2a . '" />';
echo '<input type="hidden" name="action" value="do_step_three" />';
echo '</p>';
}
// Step 4
else if ( has_action('dmn_hook', 'dmn_list_mal_ids') ) {
echo '<p class="submit">';
echo '<input class="button-primary" name="do_step_four" type="submit" value="' . $dmn_button_value_3a. '" />';
echo '<input type="hidden" name="action" value="do_step_four" />';
echo '</p>';
}
// Step 5
else if ( has_action('dmn_hook', 'dmn_kill_mal_script') ) {
echo '<p class="submit">';
echo '<input class="button-primary" name="do_step_five" type="submit" value="' . $dmn_button_value_4a. '" />';
echo '<input type="hidden" name="action" value="do_step_five" />';
echo '</p>';
}
echo '</form>'; // End form}
echo '</div>'; // .wrap
echo '<div class="clear"></div>';
} // END dmn_page_constructor()
/*******************************
* That's all folks!
*******************************/
?>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment