• Welcome to the Chevereto user community!

    Here users from all over the world gather around to learn the latest about Chevereto and contribute with ideas to improve the software.

    Please keep in mind:

    • 😌 This community is user driven. Be polite with other users.
    • 👉 Is required to purchase a Chevereto license to participate in this community (doesn't apply to Pre-sales).
    • 💸 Purchase a Pro Subscription to get access to active software support and faster ticket response times.

Show separate banners for NSFW and Whitelisted images

lonepress

Chevereto Member
Fair warning: this guide is pretty involved, and will probably take you a couple hours to complete even if you know what you're doing editing code. If you don't know what you're doing, you probably should find someone to help you who does ;).

Chevereto lets you specify code snippets for banner ads to show on image pages. But ad programs like adsense don't allow you to display ads on NSFW images. You can disable banners for NSFW images, but there are still 2 problems:
  1. You might want to show different ads on NSFW images (for example, an ad network that allows NSFW content but generates a lower RPM).
  2. Ads will be shown on images where the user failed to flag a NSFW image as NSFW, which has the potential to get your adsense account banned.
This guide will show you how to create two new sets of ad banner settings to show next to images that are either NSFW, or manually whitelisted as SFW by an admin.

You will be able to:
  • Edit banner code for the two new banner types in the dashboard, next to the existing banner code.
  • Filter images to show any combination of SFW/NSFW and Whitelisted/Not whitelisted when viewing images in the dashboard.
  • Select multiple images in the dashboard, and whitelist them all (un-whitelist them) using the Action dropdown menu.
Before you start:
  • You really should be at least somewhat comfortable modifying PHP code.
  • You need to be comfortable editing chevereto code, including some core stuff outside of the theme directory. This will introduce some complexity with new chevereto releases, as you will need to make sure that conflicts introduced in the future are merged with your code correctly.
  • Just in case it goes horribly wrong: back up your database and website files.
I strongly recommend:
  • Trying this on a development site before editing your live website code. There's a small chance I've left something out, and you don't want to use your production site as a guinea pig.
  • Using a source code revision control system like git to track your changes to chevereto code.
    • I personally use git, and keep a separate branch (called "chev-virgin") with NO changes to the chevereto code.
    • When a new release comes out, I apply it in that branch, then use git to merge it into my development branch, easily identifying conflicts along the way.
    • Learning git it outside the scope of this guide, but if you're going to get into editing core chevereto code without a way to track, rollback, and merge your changes with new releases, you're going to have a hard time.
I'm going to go through each file, and specify each block of code that needs to be changed. This way (as opposed to just attaching a bunch of files), you should still be able to use this guide with future versions of Chevereto.

This guide is broken down into three phases:
Phase 1: Add new settings for banner code, and add a sfw_whitelisted flag to the images database table.
Phase 2: Support editing new banner code in the dashboard, and change which banner is shown on the image page, based on whether the image is NSFW or whitelisted.
Phase 3: Support filtering images on the dashboard page, and modifying their whitelist value with the Action menu.
 
Last edited:
Phase 1: Add new settings for banner code, and add a sfw_whitelisted flag to the images database table.

This step makes modifications to your database. We will do this by adding a new "mini" unofficial Chevereto version number and calling your-site.com/install, just like upgrading with any other release. If you have a very large number of images, adding the whitelisted field to the databse may take a long time.

Step 1
Edit app/app.php.

When you see <new version id>, replace it with a version one tiny notch ahead of the current official Chevereto version. For example, if the current Chevereto release is 3.5.2, your <new version id> will be 3.5.2.1. This way, your new version will fit correctly between 3.5.2 and 3.5.3, when it comes out.

Change the line that contains 'G_APP_VERSION' to use your new version number:
PHP:
define('G_APP_VERSION', '<new version id>');

This will tell Chevereto that it needs to run updates when you load the your-site.com/install page.


Step 2
Edit app/installer.php. We will need to make 2 changes in this file -- one to add the new settings fields, and one to add the whitelisted field to the images table.

First, find the $settings_updates variable. This contains the list of new settings introduced with each new version. Go all the way to the bottom, which looks like this as of 3.5.2:
PHP:
        '3.5.1' => NULL,
        '3.5.2' => NULL

Add a comma to the end of the last value, then add new settings so it looks like this:
PHP:
        '3.5.1' => NULL,
        '3.5.2' => NULL,
        '<your version id>' => [
            'banner_whitelisted_image_image-viewer_foot' => NULL,
            'banner_whitelisted_image_image-viewer_top' => NULL,
            'banner_whitelisted_image_after_image-viewer' => NULL,
            'banner_whitelisted_image_after_header' => NULL,
            'banner_whitelisted_image_before_header' => NULL,
            'banner_whitelisted_image_footer' => NULL,
            'banner_whitelisted_content_tab-about_column' => NULL,
            'banner_whitelisted_content_before_comments' => NULL,
            'banner_nsfw_image_image-viewer_foot' => NULL,
            'banner_nsfw_image_image-viewer_top' => NULL,
            'banner_nsfw_image_after_image-viewer' => NULL,
            'banner_nsfw_image_after_header' => NULL,
            'banner_nsfw_image_before_header' => NULL,
            'banner_nsfw_image_footer' => NULL,
            'banner_nsfw_content_tab-about_column' => NULL,
            'banner_nsfw_content_before_comments' => NULL,
        ],
    ];

This adds new settings fields for each ad banner type that can be shown on an image page.

Now, a few lines down, find the $update_table variable. This specifies the SQL modifications to be made on existing database tables with each version.

Go down to the bottom of this variable again, which looks like this as of 3.5.2:
PHP:
                '3.4.0' => "
                    ALTER TABLE `%table_prefix%images` ADD image_category_id bigint(32) DEFAULT NULL;
                    ALTER TABLE `%table_prefix%albums` ADD album_description text;"
                    . file_get_contents(CHV_APP_PATH_INSTALL . 'sql/categories.sql')

Again, add a comma to the last entry, and add a new line like this:
PHP:
                '3.4.0' => "
                    ALTER TABLE `%table_prefix%images` ADD image_category_id bigint(32) DEFAULT NULL;
                    ALTER TABLE `%table_prefix%albums` ADD album_description text;"
                    . file_get_contents(CHV_APP_PATH_INSTALL . 'sql/categories.sql'),
                '<your version id>' => "ALTER TABLE `%table_prefix%images` ADD image_sfw_whitelisted tinyint(1) DEFAULT 0;"

Save and close installer.php.

Step 3
Edit app/lib/classes/class.image.php

Find the $table_chv_image variable near the top of the file. Change the end from this:
PHP:
                'category_id',
                'description'

to this (remember the comma!):
PHP:
                'category_id',
                'description',
                'sfw_whitelisted'

Find the insert function (about 3/4 of the way through the file, search for "public static function insert"). Inside that function change the $values variable from this:
PHP:
            $values = array(
                'album_id'            => $values['album_id'],
                'date'                => G\datetime(),
                'date_gmt'            => G\datetimegmt(),
                'nsfw'                => isset($values['nsfw']) ? $values['nsfw'] : 0,
                'user_id'            => $values['user_id'],
                'uploader_ip'        => G\get_client_ip(),
                'storage'            => $values['storage'],
                'storage_id'        => $values['storage_id'],
                'md5'                => md5_file($image_upload['uploaded']['file']),
                'original_filename' => $image_upload['source']['filename'],
                'original_exifdata' => (G\check_value($image_upload['source']['image_exif']) ? json_encode($image_upload['source']['image_exif']) : null),
                'category_id'        => $values['category_id'],
                'description'        => $values['description']
            );

to this:
PHP:
            $values = array(
                'album_id'            => $values['album_id'],
                'date'                => G\datetime(),
                'date_gmt'            => G\datetimegmt(),
                'nsfw'                => isset($values['nsfw']) ? $values['nsfw'] : 0,
                'user_id'            => $values['user_id'],
                'uploader_ip'        => G\get_client_ip(),
                'storage'            => $values['storage'],
                'storage_id'        => $values['storage_id'],
                'md5'                => md5_file($image_upload['uploaded']['file']),
                'original_filename' => $image_upload['source']['filename'],
                'original_exifdata' => (G\check_value($image_upload['source']['image_exif']) ? json_encode($image_upload['source']['image_exif']) : null),
                'category_id'        => $values['category_id'],
                'description'        => $values['description'],
                'sfw_whitelisted'   => 0
            );

Save and close class.image.php.

You are finished adding core support for the new banner settings and sfw_whitelisted image field. Now we need to be able to edit and use the new banners.

After making this change, you will need to load your-site.com/install, so chevereto will make the database changes necessary.
 
Last edited:
Phase 2: Support editing new banner code in the dashboard, and change which banner is shown on the image page, based on whether the image is NSFW or whitelisted.

Step 1
You need to be able to edit your banner code from the dashboard' Banners settings page. This change will add 3 sub-tabs to that page, allowing you to specify Default banners (shown when an image is not flagged NSFW and is also not whitelisted as SFW), NSFW banners (shown when an image is flagged NSFW), and Whitelisted safe banners (shown when an image has been manually whitelisted as SFW).

We're not adding new settings for all banners -- just the ones shown on the image page.

Open app/themes/<your theme>/views/dashboard.php. Search for the section of code that shows banner settings, about 3/4 of the way down the file. It looks like this:
PHP:
            <?php if(get_settings()['key'] == 'banners') { ?>
          
            <p><?php _se('Here you can set the codes for the predefined ad spaces.'); ?></p>
          
            <h3 class="margin-top-20"><?php _se('Homepage'); ?></h3>
            <div class="input-label">
                <label for="banner_home_before_cover"><?php _se('Before cover (homepage)'); ?></label>
                <div class="c12 phablet-c1"><textarea type="text" name="banner_home_before_cover" class="text-input r3"><?php echo CHV\getSetting('banner_home_before_cover', true); ?></textarea></div>
            </div>
            <div class="input-label">
                <label for="banner_home_after_cover"><?php _se('After cover (homepage)'); ?></label>
                <div class="c12 phablet-c1"><textarea type="text" name="banner_home_after_cover" class="text-input r3"><?php echo CHV\getSetting('banner_home_after_cover', true); ?></textarea></div>
            </div>
            <div class="input-label">
                <label for="banner_home_after_listing"><?php _se('After listing (homepage)'); ?></label>
                <div class="c12 phablet-c1"><textarea type="text" name="banner_home_after_listing" class="text-input r3"><?php echo CHV\getSetting('banner_home_after_listing', true); ?></textarea></div>
            </div>
          
            <span class="line-separator"></span>
          
            <h3 class="margin-top-20"><?php _se('Listings'); ?></h3>
            <div class="input-label">
                <label for="banner_listing_before_pagination"><?php _se('Before pagination'); ?></label>
                <div class="c12 phablet-c1"><textarea type="text" name="banner_listing_before_pagination" class="text-input r3"><?php echo CHV\getSetting('banner_listing_before_pagination', true); ?></textarea></div>
            </div>
            <div class="input-label">
                <label for="banner_listing_after_pagination"><?php _se('After pagination'); ?></label>
                <div class="c12 phablet-c1"><textarea type="text" name="banner_listing_after_pagination" class="text-input r3"><?php echo CHV\getSetting('banner_listing_after_pagination', true); ?></textarea></div>
            </div>
          
            <span class="line-separator"></span>
          
            <h3 class="margin-top-20"><?php _se('Content (image and album)'); ?></h3>
            <div class="input-label">
                <label for="banner_content_tab-about_column"><?php _se('Tab about column'); ?></label>
                <div class="c12 phablet-c1"><textarea type="text" name="banner_content_tab-about_column" class="text-input r3"><?php echo CHV\getSetting('banner_content_tab-about_column', true); ?></textarea></div>
            </div>
            <div class="input-label">
                <label for="banner_content_before_comments"><?php _se('Before comments'); ?></label>
                <div class="c12 phablet-c1"><textarea type="text" name="banner_content_before_comments" class="text-input r3"><?php echo CHV\getSetting('banner_content_before_comments', true); ?></textarea></div>
            </div>
          
            <span class="line-separator"></span>
          
            <h3 class="margin-top-20"><?php _se('Image page'); ?></h3>
            <div class="input-label">
                <label for="banner_image_image-viewer_top"><?php _se('Inside viewer top (image page)'); ?></label>
                <div class="c12 phablet-c1"><textarea type="text" name="banner_image_image-viewer_top" class="text-input r3"><?php echo CHV\getSetting('banner_image_image-viewer_top', true); ?></textarea></div>
                <div class="input-below"><?php _se('Expected banner size 728x90'); ?></div>
            </div>
            <div class="input-label">
                <label for="banner_image_image-viewer_foot"><?php _se('Inside viewer foot (image page)'); ?></label>
                <div class="c12 phablet-c1"><textarea type="text" name="banner_image_image-viewer_foot" class="text-input r3"><?php echo CHV\getSetting('banner_image_image-viewer_foot', true); ?></textarea></div>
                <div class="input-below"><?php _se('Expected banner size 728x90'); ?></div>
            </div>
            <div class="input-label">
                <label for="banner_image_after_image-viewer"><?php _se('After image viewer (image page)'); ?></label>
                <div class="c12 phablet-c1"><textarea type="text" name="banner_image_after_image-viewer" class="text-input r3"><?php echo CHV\getSetting('banner_image_after_image-viewer', true); ?></textarea></div>
            </div>
            <div class="input-label">
                <label for="banner_image_before_header"><?php _se('Before header (image page)'); ?></label>
                <div class="c12 phablet-c1"><textarea type="text" name="banner_image_before_header" class="text-input r3"><?php echo CHV\getSetting('banner_image_before_header', true); ?></textarea></div>
            </div>
            <div class="input-label">
                <label for="banner_image_after_header"><?php _se('After header (image page)'); ?></label>
                <div class="c12 phablet-c1"><textarea type="text" name="banner_image_after_header" class="text-input r3"><?php echo CHV\getSetting('banner_image_after_header', true); ?></textarea></div>
            </div>
            <div class="input-label">
                <label for="banner_image_footer"><?php _se('Footer (image page)'); ?></label>
                <div class="c12 phablet-c1"><textarea type="text" name="banner_image_footer" class="text-input r3"><?php echo CHV\getSetting('banner_image_footer', true); ?></textarea></div>
            </div>
          
            <span class="line-separator"></span>
          
            <h3 class="margin-top-20"><?php _se('Album page'); ?></h3>
            <div class="input-label">
                <label for="banner_album_before_header"><?php _se('Before header (album page)'); ?></label>
                <div class="c12 phablet-c1"><textarea type="text" name="banner_album_before_header" class="text-input r3"><?php echo CHV\getSetting('banner_album_before_header', true); ?></textarea></div>
            </div>
            <div class="input-label">
                <label for="banner_album_after_header"><?php _se('After header (album page)'); ?></label>
                <div class="c12 phablet-c1"><textarea type="text" name="banner_album_after_header" class="text-input r3"><?php echo CHV\getSetting('banner_album_after_header', true); ?></textarea></div>
            </div>
          
            <span class="line-separator"></span>
          
            <h3 class="margin-top-20"><?php _se('User profile page'); ?></h3>
            <div class="input-label">
                <label for="banner_user_after_top"><?php _se('After top (user profile)'); ?></label>
                <div class="c12 phablet-c1"><textarea type="text" name="banner_user_after_top" class="text-input r3"><?php echo CHV\getSetting('banner_user_after_top', true); ?></textarea></div>
            </div>
            <div class="input-label">
                <label for="banner_user_before_listing"><?php _se('Before listing (user profile)'); ?></label>
                <div class="c12 phablet-c1"><textarea type="text" name="banner_user_before_listing" class="text-input r3"><?php echo CHV\getSetting('banner_user_before_listing', true); ?></textarea></div>
            </div>
          
            <span class="line-separator"></span>
          
            <h3 class="margin-top-20"><?php _se('Explore page'); ?></h3>
            <div class="input-label">
                <label for="banner_explore_after_top"><?php _se('After top (explore page)'); ?></label>
                <div class="c12 phablet-c1"><textarea type="text" name="banner_explore_after_top" class="text-input r3"><?php echo CHV\getSetting('banner_explore_after_top', true); ?></textarea></div>
            </div>
          
            <?php } ?>

Replace the whole thing with this:
PHP:
            <?php if(get_settings()['key'] == 'banners') { ?>
          
            <p><?php _se('Here you can set the codes for the predefined ad spaces.'); ?></p>

            <div class="header">
                <?php
                global $tabs; // Define it as a global to bind it on the included tabs.php
                $tabs = [
                    [
                        "label"        => _s('Default banners'),
                        "id"        => "tab-banner",
                        "current"    => true,
                    ],
                    [
                        "label"        => _s('NSFW banners'),
                        "id"        => "tab-banner_nsfw",
                    ],
                    [
                        "label"        => _s('Whitelisted safe banners'),
                        "id"        => "tab-banner_whitelisted"
                    ]
                ];

                G\Render\include_theme_file("snippets/tabs");
                ?>
            </div>

            <div id="tabbed-content-group">
            <?php
                foreach(['banner', 'banner_nsfw', 'banner_whitelisted'] as $prefix) {
            ?>
                <div id="tab-<?php echo $prefix; ?>" class="tabbed-content<?php echo $prefix == 'banner' ? ' visible' : ''; ?>">
                    <?php if ($prefix == 'banner') { ?>

                    <h3 class="margin-top-20"><?php _se('Homepage'); ?></h3>
                    <div class="input-label">
                        <label for="<?php echo $prefix; ?>_home_before_cover"><?php _se('Before cover (homepage)'); ?></label>
                        <div class="c12 phablet-c1"><textarea type="text" name="<?php echo $prefix; ?>_home_before_cover" class="text-input r3"><?php echo CHV\getSetting($prefix . '_home_before_cover', true); ?></textarea></div>
                    </div>
                    <div class="input-label">
                        <label for="<?php echo $prefix; ?>_home_after_cover"><?php _se('After cover (homepage)'); ?></label>
                        <div class="c12 phablet-c1"><textarea type="text" name="<?php echo $prefix; ?>_home_after_cover" class="text-input r3"><?php echo CHV\getSetting($prefix . '_home_after_cover', true); ?></textarea></div>
                    </div>
                    <div class="input-label">
                        <label for="<?php echo $prefix; ?>_home_after_listing"><?php _se('After listing (homepage)'); ?></label>
                        <div class="c12 phablet-c1"><textarea type="text" name="<?php echo $prefix; ?>_home_after_listing" class="text-input r3"><?php echo CHV\getSetting($prefix . '_home_after_listing', true); ?></textarea></div>
                    </div>

                    <span class="line-separator"></span>

                    <h3 class="margin-top-20"><?php _se('Listings'); ?></h3>
                    <div class="input-label">
                        <label for="<?php echo $prefix; ?>_listing_before_pagination"><?php _se('Before pagination'); ?></label>
                        <div class="c12 phablet-c1"><textarea type="text" name="<?php echo $prefix; ?>_listing_before_pagination" class="text-input r3"><?php echo CHV\getSetting($prefix . '_listing_before_pagination', true); ?></textarea></div>
                    </div>
                    <div class="input-label">
                        <label for="<?php echo $prefix; ?>_listing_after_pagination"><?php _se('After pagination'); ?></label>
                        <div class="c12 phablet-c1"><textarea type="text" name="<?php echo $prefix; ?>_listing_after_pagination" class="text-input r3"><?php echo CHV\getSetting($prefix . '_listing_after_pagination', true); ?></textarea></div>
                    </div>

                    <span class="line-separator"></span>

                    <?php } ?>

                    <h3 class="margin-top-20"><?php _se('Content (image and album)'); ?></h3>
                    <div class="input-label">
                        <label for="<?php echo $prefix; ?>_content_tab-about_column"><?php _se('Tab about column'); ?></label>
                        <div class="c12 phablet-c1"><textarea type="text" name="<?php echo $prefix; ?>_content_tab-about_column" class="text-input r3"><?php echo CHV\getSetting($prefix . '_content_tab-about_column', true); ?></textarea></div>
                    </div>
                    <div class="input-label">
                        <label for="<?php echo $prefix; ?>_content_before_comments"><?php _se('Before comments'); ?></label>
                        <div class="c12 phablet-c1"><textarea type="text" name="<?php echo $prefix; ?>_content_before_comments" class="text-input r3"><?php echo CHV\getSetting($prefix . '_content_before_comments', true); ?></textarea></div>
                    </div>

                    <span class="line-separator"></span>

                    <h3 class="margin-top-20"><?php _se('Image page'); ?></h3>
                    <div class="input-label">
                        <label for="<?php echo $prefix; ?>_image_image-viewer_top"><?php _se('Inside viewer top (image page)'); ?></label>
                        <div class="c12 phablet-c1"><textarea type="text" name="<?php echo $prefix; ?>_image_image-viewer_top" class="text-input r3"><?php echo CHV\getSetting($prefix . '_image_image-viewer_top', true); ?></textarea></div>
                        <div class="input-below"><?php _se('Expected banner size 728x90'); ?></div>
                    </div>
                    <div class="input-label">
                        <label for="<?php echo $prefix; ?>_image_image-viewer_foot"><?php _se('Inside viewer foot (image page)'); ?></label>
                        <div class="c12 phablet-c1"><textarea type="text" name="<?php echo $prefix; ?>_image_image-viewer_foot" class="text-input r3"><?php echo CHV\getSetting($prefix . '_image_image-viewer_foot', true); ?></textarea></div>
                        <div class="input-below"><?php _se('Expected banner size 728x90'); ?></div>
                    </div>
                    <div class="input-label">
                        <label for="<?php echo $prefix; ?>_image_after_image-viewer"><?php _se('After image viewer (image page)'); ?></label>
                        <div class="c12 phablet-c1"><textarea type="text" name="<?php echo $prefix; ?>_image_after_image-viewer" class="text-input r3"><?php echo CHV\getSetting($prefix . '_image_after_image-viewer', true); ?></textarea></div>
                    </div>
                    <div class="input-label">
                        <label for="<?php echo $prefix; ?>_image_before_header"><?php _se('Before header (image page)'); ?></label>
                        <div class="c12 phablet-c1"><textarea type="text" name="<?php echo $prefix; ?>_image_before_header" class="text-input r3"><?php echo CHV\getSetting($prefix . '_image_before_header', true); ?></textarea></div>
                    </div>
                    <div class="input-label">
                        <label for="<?php echo $prefix; ?>_image_after_header"><?php _se('After header (image page)'); ?></label>
                        <div class="c12 phablet-c1"><textarea type="text" name="<?php echo $prefix; ?>_image_after_header" class="text-input r3"><?php echo CHV\getSetting($prefix . '_image_after_header', true); ?></textarea></div>
                    </div>
                    <div class="input-label">
                        <label for="<?php echo $prefix; ?>_image_footer"><?php _se('Footer (image page)'); ?></label>
                        <div class="c12 phablet-c1"><textarea type="text" name="<?php echo $prefix; ?>_image_footer" class="text-input r3"><?php echo CHV\getSetting($prefix . '_image_footer', true); ?></textarea></div>
                    </div>

                    <?php if ($prefix == 'banner') { ?>

                    <span class="line-separator"></span>

                    <h3 class="margin-top-20"><?php _se('Album page'); ?></h3>
                    <div class="input-label">
                        <label for="<?php echo $prefix; ?>_album_before_header"><?php _se('Before header (album page)'); ?></label>
                        <div class="c12 phablet-c1"><textarea type="text" name="<?php echo $prefix; ?>_album_before_header" class="text-input r3"><?php echo CHV\getSetting($prefix . '_album_before_header', true); ?></textarea></div>
                    </div>
                    <div class="input-label">
                        <label for="<?php echo $prefix; ?>_album_after_header"><?php _se('After header (album page)'); ?></label>
                        <div class="c12 phablet-c1"><textarea type="text" name="<?php echo $prefix; ?>_album_after_header" class="text-input r3"><?php echo CHV\getSetting($prefix . '_album_after_header', true); ?></textarea></div>
                    </div>

                    <span class="line-separator"></span>

                    <h3 class="margin-top-20"><?php _se('User profile page'); ?></h3>
                    <div class="input-label">
                        <label for="<?php echo $prefix; ?>_user_after_top"><?php _se('After top (user profile)'); ?></label>
                        <div class="c12 phablet-c1"><textarea type="text" name="<?php echo $prefix; ?>_user_after_top" class="text-input r3"><?php echo CHV\getSetting($prefix . '_user_after_top', true); ?></textarea></div>
                    </div>
                    <div class="input-label">
                        <label for="<?php echo $prefix; ?>_user_before_listing"><?php _se('Before listing (user profile)'); ?></label>
                        <div class="c12 phablet-c1"><textarea type="text" name="<?php echo $prefix; ?>_user_before_listing" class="text-input r3"><?php echo CHV\getSetting($prefix . '_user_before_listing', true); ?></textarea></div>
                    </div>

                    <span class="line-separator"></span>

                    <h3 class="margin-top-20"><?php _se('Explore page'); ?></h3>
                    <div class="input-label">
                        <label for="<?php echo $prefix; ?>_explore_after_top"><?php _se('After top (explore page)'); ?></label>
                        <div class="c12 phablet-c1"><textarea type="text" name="<?php echo $prefix; ?>_explore_after_top" class="text-input r3"><?php echo CHV\getSetting($prefix . '_explore_after_top', true); ?></textarea></div>
                    </div>

                    <?php } ?>
                </div>
            <?php } ?>
            </div>
          
            <?php } ?>

Save and close dashboard.php.
 
Last edited:
Step 2
Now we need to actually show the new ad banner code depending on the image's status.

Open app/lib/functions.render.php

At the end of the file, find the show_banner function, which looks like this:
PHP:
function show_banner($banner) {
    if(strpos($banner, 'banner_') !== 0) {
        $banner = 'banner_' . $banner;
    };
    $banner_code = CHV\getSetting($banner);
    if($banner_code) {
        echo '<div id="'.$banner.'" class="ad-banner">'.$banner_code.'</div>';
    }
}

and replace it with this:
PHP:
function show_banner($banner, $prefix='banner_') {
    if(strpos($banner, $prefix) !== 0) {
        $banner = $prefix . $banner;
    };
    $banner_code = CHV\getSetting($banner);
    if($banner_code) {
        echo '<div id="'.$banner.'" class="ad-banner">'.$banner_code.'</div>';
    }
}

Save and close functions.render.php.

Step 3
Open app/themes/<your theme>/views/image.php

Near the top of the file, beneath the line that defines $share_links_array, add this code:

PHP:
$banner_prefix = 'banner_';
if (get_image()['nsfw']) {
    $banner_prefix = 'banner_nsfw_';
} elseif (get_image()['sfw_whitelisted']) {
    $banner_prefix = 'banner_whitelisted_';
}

Now, search for every call to CHV\Render\show_banner, and add the $banner_prefix variable to its arguments.

For example, change this:
PHP:
CHV\Render\show_banner('image_image-viewer_top');

to this:
PHP:
CHV\Render\show_banner('image_image-viewer_top', $banner_prefix);

There are 8 calls to CHV\Render\show_banner that need to be updated like this. Make sure you leave the first argument the same in each one.

Save and close image.php

Your site will now show alternate banner code depending on whether an image is flagged as NSFW, SFW whitelisted, or neither.

Step 4:
In app/themes/<your theme>style.css, change this block (at the bottom of the file):
PHP:
#banner_image_image-viewer_top, #banner_image_image-viewer_foot {
    height: 90px;
    margin-left: auto;
    margin-right: auto;
}

to this:
PHP:
#banner_image_image-viewer_top, #banner_image_image-viewer_foot,
#banner_nsfw_image_image-viewer_top, #banner_nsfw_image_image-viewer_foot,
#banner_whitelisted_image_image-viewer_top, #banner_whitelisted_image_image-viewer_foot {
    height: 90px;
    margin-left: auto;
    margin-right: auto;
}
 
Last edited:
Phase 3: Support filtering images on the dashboard page, and modifying their whitelist value with the Action menu.

Now the only thing left is to enable actually setting the whitelisted flag on images from the dashboard (or the explore page, etc).

Step 1
First, we'll add filters to the dashboard's images page so you can easily find images that are marked SFW but aren't whitelisted.

Open app/themes/<your theme>/views/dashboard.php again.

Find the block of code that specifies tabs for the images panel. It looks like this:
PHP:
                        case 'images':
                            $tabs = [
                                0 => [
                                    'list'        => true,
                                    'tools'        => true,
                                    'label'        => _s('Most recent'),
                                    'id'        => 'list-most-recent',
                                    'params'    => 'list=images&sort=date_desc&page=1',
                                    'current'    => $_REQUEST['sort'] == 'date_desc' or !$_REQUEST['sort'] ? true : false,
                                ],
                                1 => [
                                    'list'        => true,
                                    'tools'        => true,
                                    'label'        => _s('Oldest'),
                                    'id'        => 'list-most-oldest',
                                    'params'    => 'list=images&sort=date_asc&page=1',
                                    'current'    => $_REQUEST['sort'] == 'date_asc',
                                ],
                                2 => [
                                    'list'        => true,
                                    'tools'        => true,
                                    'label'        => _s('Most viewed'),
                                    'id'        => 'list-most-viewed',
                                    'params'    => 'list=images&sort=views_desc&page=1',
                                    'current'    => $_REQUEST['sort'] == 'views_desc',
                                ]
                            ];
                        break;

Replace it with this:
PHP:
                        case 'images':
                            $tabs = [
                                0 => [
                                    'list'        => true,
                                    'tools'        => true,
                                    'label'        => _s('Most recent'),
                                    'id'        => 'list-most-recent',
                                    'params'    => 'list=images&sort=date_desc&page=1' .
                                                   (isset($_REQUEST['whitelisted']) ? '&whitelisted=' . $_REQUEST['whitelisted'] : '') .
                                                   (isset($_REQUEST['nsfw']) ? '&nsfw=' . $_REQUEST['nsfw'] : ''),
                                    'current'    => $_REQUEST['sort'] == 'date_desc' or !$_REQUEST['sort'] ? true : false,
                                ],
                                1 => [
                                    'list'        => true,
                                    'tools'        => true,
                                    'label'        => _s('Oldest'),
                                    'id'        => 'list-most-oldest',
                                    'params'    => 'list=images&sort=date_asc&page=1' .
                                                   (isset($_REQUEST['whitelisted']) ? '&whitelisted=' . $_REQUEST['whitelisted'] : '') .
                                                   (isset($_REQUEST['nsfw']) ? '&nsfw=' . $_REQUEST['nsfw'] : ''),
                                    'current'    => $_REQUEST['sort'] == 'date_asc',
                                ],
                                2 => [
                                    'list'        => true,
                                    'tools'        => true,
                                    'label'        => _s('Most viewed'),
                                    'id'        => 'list-most-viewed',
                                    'params'    => 'list=images&sort=views_desc&page=1' .
                                                   (isset($_REQUEST['whitelisted']) ? '&whitelisted=' . $_REQUEST['whitelisted'] : '') .
                                                   (isset($_REQUEST['nsfw']) ? '&nsfw=' . $_REQUEST['nsfw'] : ''),
                                    'current'    => $_REQUEST['sort'] == 'views_desc',
                                ]
                            ];
                        break;

This keeps your filter settings the same as you switch views between Most recent, Oldest, and Most viewed.

Just below that block where tabs are defined, find the code that sets parameters controlling the list of images to be retrieved from the database. It looks like this:
PHP:
                    try {
                        global $list;
                        $list = new CHV\Listing;
                        $list->setType($type); // images | users | albums
                        $list->setOffset($list_params['offset']);
                        $list->setLimit($list_params['limit']); // how many results?
                        $list->setItemsPerPage($list_params['items_per_page']); // must
                        $list->setSortType($list_params['sort'][0]); // date | size | views
                        $list->setSortOrder($list_params['sort'][1]); // asc | desc
                        $list->setRequester(CHV\Login::getUser());
                        $list->output_tpl = $type;
                        $list->exec();
                    } catch(Exception $e) {
                        G\exception_to_error($e);
                    }

Change it to this:
PHP:
                    try {
                        global $list;
                        $list = new CHV\Listing;
                        $list->setType($type); // images | users | albums
                        $list->setOffset($list_params['offset']);
                        $list->setLimit($list_params['limit']); // how many results?
                        $list->setItemsPerPage($list_params['items_per_page']); // must
                        $list->setSortType($list_params['sort'][0]); // date | size | views
                        $list->setSortOrder($list_params['sort'][1]); // asc | desc
                        $list->setRequester(CHV\Login::getUser());
                        $list->output_tpl = $type;

                        // Filter whitelisted images?
                        if (isset($_REQUEST['whitelisted'])) {
                            $list->setWhere(($list->where ? $list->where . ' AND' : 'WHERE') . ' image_sfw_whitelisted=:whitelisted');
                            $list->bind(':whitelisted', $_REQUEST['whitelisted'] ? 1 : 0);
                        }
                        if (isset($_REQUEST['nsfw'])) {
                            $list->setWhere(($list->where ? $list->where . ' AND' : 'WHERE') . ' image_nsfw=:nsfw');
                            $list->bind(':nsfw', $_REQUEST['nsfw'] ? 1 : 0);
                        }

                        $list->exec();
                    } catch(Exception $e) {
                        G\exception_to_error($e);
                    }

Finally, a few lines down, just after the line "<?php G\Render\include_theme_file("snippets/tabs"); ?>", add this code:
PHP:
            <!-- Dropdowns to filter by NSFW and/or whitelisted -->
            <div data-content="pop-selection" class="pop-btn header-link display-inline-block margin-left-10">
                <span class="pop-btn-text no-select margin-left-5"><?php _se('Show whitelisted'); ?>
                    <?php  if (isset($_GET['whitelisted'])) {
                        echo '[' . ($_GET['whitelisted'] ? _s('Yes') : _s('No')) . ']';
                    } ?>
                    <span class="arrow-down"></span>
                </span>
                <div class="pop-box anchor-right arrow-box arrow-box-top">
                    <div class="pop-box-inner pop-box-menu">
                        <ul>
                            <?php
                            $qs = $_GET;
                            unset($qs['whitelisted']);
                            unset($qs['page']);
                            ?>
                            <li><a href="<?php echo G\get_base_url('dashboard/'.get_dashboard() . '/?' . http_build_query($qs)); ?>"><?php _se('Both'); ?></a></li>
                            <?php $qs['whitelisted'] = 1; ?>
                            <li><a href="<?php echo G\get_base_url('dashboard/'.get_dashboard() . '/?' . http_build_query($qs)); ?>"><?php _se('Yes'); ?></a></li>
                            <?php $qs['whitelisted'] = 0; ?>
                            <li><a href="<?php echo G\get_base_url('dashboard/'.get_dashboard() . '/?' . http_build_query($qs)); ?>"><?php _se('No'); ?></a></li>
                        </ul>
                    </div>
                </div>
            </div>
            <div data-content="pop-selection" class="pop-btn header-link display-inline-block margin-left-10">
                <span class="pop-btn-text no-select margin-left-5"><?php _se('Show NSFW'); ?>
                    <?php  if (isset($_GET['nsfw'])) {
                        echo '[' . ($_GET['nsfw'] ? _s('Yes') : _s('No')) . ']';
                    } ?>
                    <span class="arrow-down"></span>
                </span>
                <div class="pop-box anchor-right arrow-box arrow-box-top">
                    <div class="pop-box-inner pop-box-menu">
                        <ul>
                            <?php
                            $qs = $_GET;
                            unset($qs['nsfw']);
                            unset($qs['page']);
                            ?>
                            <li><a href="<?php echo G\get_base_url('dashboard/'.get_dashboard() . '/?' . http_build_query($qs)); ?>"><?php _se('Both'); ?></a></li>
                            <?php $qs['nsfw'] = 1; ?>
                            <li><a href="<?php echo G\get_base_url('dashboard/'.get_dashboard() . '/?' . http_build_query($qs)); ?>"><?php _se('Yes'); ?></a></li>
                            <?php $qs['nsfw'] = 0; ?>
                            <li><a href="<?php echo G\get_base_url('dashboard/'.get_dashboard() . '/?' . http_build_query($qs)); ?>"><?php _se('No'); ?></a></li>
                        </ul>
                    </div>
                </div>
            </div>

Save and close dashboard.php. Now you can filter which images you see on the dashboard.
 
Last edited:
Step 2
The last thing left is to enable you to actually whitelist the images. We'll have several changes to make to a few different files...

In app/lib/functions.render.php, change this (inside "function get_peafowl_item_list"):
PHP:
    if($stock_tpl == 'IMAGE') {
        $replacements['IMAGE_FLAG'] = $item['nsfw'] ? 'unsafe' : 'safe';
    }

to this:

PHP:
    if($stock_tpl == 'IMAGE') {
        $replacements['IMAGE_FLAG'] = $item['nsfw'] ? 'unsafe' : 'safe';
        $replacements['IMAGE_WHITELISTED'] = $item['sfw_whitelisted'] ? 'whitelisted' : 'unwhitelisted';
    }

In app/lib/chevereto.js, change this block (about 1/3 of the way down the file):
PHP:
                case "flag-safe":
                case "flag-unsafe":
                  
                    var action = $(this).data("action");
                  
                    PF.fn.modal.call({
                        template: $("[data-modal=form-" + action + "]").html(),
                        button_submit: PF.fn._s("Confirm"),
                        ajax: {
                            url: PF.obj.config.json_api,
                            deferred: {
                                success: function(XHR) {
                                    $targets.each(function() {
                                        $(this).removeClass("safe unsafe").addClass(action == "flag-safe" ? "safe" : "unsafe").data("flag", action == "flag-safe" ? "safe" : "unsafe");
                                    });
                                    CHV.fn.list_editor.clearSelection();
                                }
                            }
                        },
                        confirm: function() {  
                            PF.obj.modal.form_data = {
                                action: action,
                                from: "list",
                                multiple: true,
                                flagging: {
                                    ids: ids,
                                    nsfw: action == "flag-safe" ? 0 : 1
                                }
                            };
                          
                            return true;
                        }
                    });
                break;

to this:
PHP:
                case "flag-safe":
                case "flag-unsafe":
                case "whitelist-safe":
                case "whitelist-unsafe":
                  
                    var action = $(this).data("action");
                  
                    PF.fn.modal.call({
                        template: $("[data-modal=form-" + action + "]").html(),
                        button_submit: PF.fn._s("Confirm"),
                        ajax: {
                            url: PF.obj.config.json_api,
                            deferred: {
                                success: function(XHR) {
                                    $targets.each(function() {
                                        if (action === "whitelist-safe" || action ==="whitelist-unsafe") {
                                            $(this).removeClass("whitelisted unwhitelisted")
                                                .addClass(action == "whitelist-safe" ? "whitelisted" : "unwhitelisted")
                                                .data("whitelisted", action == "whitelist-safe" ? "whitelisted" : "unwhitelisted");
                                        } else {
                                            $(this).removeClass("safe unsafe")
                                                .addClass(action == "flag-safe" ? "safe" : "unsafe")
                                                .data("flag", action == "flag-safe" ? "safe" : "unsafe");
                                        }
                                    });
                                    CHV.fn.list_editor.clearSelection();
                                }
                            }
                        },
                        confirm: function() {  
                            PF.obj.modal.form_data = {
                                action: action,
                                from: "list",
                                multiple: true,
                                flagging: {
                                    ids: ids
                                }
                            };
                            if (action === "whitelist-safe" || action === "whitelist-unsafe") {
                                PF.obj.modal.form_data.flagging['sfw_whitelisted'] = action == "whitelist-safe" ? 1 : 0;
                            } else {
                                PF.obj.modal.form_data.flagging['nsfw'] = action == "flag-safe" ? 0 : 1;
                            }

                            return true;
                        }
                    });
                break;

Near the bottom of the same file, inside the "selectionCount: function()" block, change this:
PHP:
            // Sensitive display
            if($content_listing.data('list') == 'images' && selection_count > 0) {
                var has_sfw = $(PF.obj.listing.selectors.list_item+".selected[data-flag=safe]", this).length > 0,
                    has_nsfw = $(PF.obj.listing.selectors.list_item+".selected[data-flag=unsafe]", this).length > 0;
                  
                $("[data-action=flag-safe]", $listing_options)[(has_nsfw ? "remove" : "add") + "Class"]("hidden");
                $("[data-action=flag-unsafe]", $listing_options)[(has_sfw ? "remove" : "add") + "Class"]("hidden");
            }

to this:
PHP:
            // Sensitive display
            if($content_listing.data('list') == 'images' && selection_count > 0) {
                var has_sfw = $(PF.obj.listing.selectors.list_item+".selected[data-flag=safe]", this).length > 0,
                    has_nsfw = $(PF.obj.listing.selectors.list_item+".selected[data-flag=unsafe]", this).length > 0;

                $("[data-action=flag-safe]", $listing_options)[(has_nsfw ? "remove" : "add") + "Class"]("hidden");
                $("[data-action=flag-unsafe]", $listing_options)[(has_sfw ? "remove" : "add") + "Class"]("hidden");

                var has_whitelist_safe = $(PF.obj.listing.selectors.list_item+".selected[data-whitelisted=whitelisted]", this).length > 0,
                    has_whitelist_unsafe = $(PF.obj.listing.selectors.list_item+".selected[data-whitelisted=unwhitelisted]", this).length > 0;

                $("[data-action=whitelist-safe]", $listing_options)[(has_whitelist_unsafe ? "remove" : "add") + "Class"]("hidden");
                $("[data-action=whitelist-unsafe]", $listing_options)[(has_whitelist_safe ? "remove" : "add") + "Class"]("hidden");
            }
        });

In app/routes/route.json.php, change this block:
PHP:
            case 'flag-safe':
            case 'flag-unsafe':

                if(!$logged_user) {
                    throw new Exception(_s('Login needed'), 403);
                }

                $flagging = $_REQUEST['flagging'];
                $owner_id = $logged_user['id'];

                // Admin
                if(!$logged_user['is_admin'] and $owner_id !== $logged_user['id']) {
                    throw new Exception('Invalid content owner request', 403);
                }

                $ids = array();
                foreach($flagging['ids'] as $id) {
                    $ids[] = CHV\decodeID($id);
                }

                $images = CHV\Image::getMultiple($ids);
                $images_ids = [];

                foreach($images as $image) {
                    if(!$logged_user['is_admin'] and $image['image_user_id'] != $logged_user['id']) {
                        continue;
                    }
                    $images_ids[] = $image['image_id'];
                }

                if(!$images_ids) {
                    throw new Exception('Invalid content owner request', 101);
                }

                // There is no CHV\Image::editMultiple, so we must cast manually the editing

                $db = CHV\DB::getInstance();
                $db->query('UPDATE `' . CHV\DB::getTable('images') . '` SET `image_nsfw`=:image_nsfw WHERE `image_id` IN ('.implode(',', $images_ids).')');
                $db->bind(':image_nsfw', $flagging['nsfw'] == 1 ? 1 : 0);
                $db->exec();

                $json_array['status_code'] = 200;
                $json_array['success'] = ['message' => 'Content flag changed', 'code' => 200];

            break;

to this:
PHP:
            case 'flag-safe':
            case 'flag-unsafe':
            case 'whitelist-safe':
            case 'whitelist-unsafe':

                if(!$logged_user) {
                    throw new Exception(_s('Login needed'), 403);
                }

                $flagging = $_REQUEST['flagging'];
                $owner_id = $logged_user['id'];

                // Admin
                if(!$logged_user['is_admin'] and $owner_id !== $logged_user['id']) {
                    throw new Exception('Invalid content owner request', 403);
                }

                // Admin ONLY for whitelisting
                $changing_whitelist = in_array($doing, ['whitelist-safe', 'whitelist-unsafe']);
                if ($changing_whitelist and !$logged_user['is_admin']) {
                    throw new Exception('Invalid content owner request', 403);
                }

                $ids = array();
                foreach($flagging['ids'] as $id) {
                    $ids[] = CHV\decodeID($id);
                }

                $images = CHV\Image::getMultiple($ids);
                $images_ids = [];

                foreach($images as $image) {
                    if(!$logged_user['is_admin'] and $image['image_user_id'] != $logged_user['id']) {
                        continue;
                    }
                    $images_ids[] = $image['image_id'];
                }

                if(!$images_ids) {
                    throw new Exception('Invalid content owner request', 101);
                }

                // There is no CHV\Image::editMultiple, so we must cast manually the editing

                $field_name = $changing_whitelist ? 'sfw_whitelisted' : 'nsfw';
                $db = CHV\DB::getInstance();
                $db->query('UPDATE `' . CHV\DB::getTable('images') . '` SET `image_' . $field_name . '`=:flag_param WHERE `image_id` IN (' . implode(',', $images_ids) . ')');
                $db->bind(':flag_param', $flagging[$field_name] == 1 ? 1 : 0);
                $db->exec();

                $json_array['status_code'] = 200;
                $json_array['success'] = ['message' => 'Content flag changed', 'code' => 200];

            break;

In app/themes/<your theme>/snippets/listing_tools_editor.php, change this:
PHP:
                    <?php
                        if($tab['type'] == 'images') {
                    ?>

to this:
PHP:
                    <?php
                        if($tab['type'] == 'images') {
                            if(is_admin()) {
                                ?>
                                <li><a data-action="whitelist-safe" class="hidden"><?php _se('Whitelist as safe'); ?></a></li>
                                <li><a data-action="whitelist-unsafe" class="hidden"><?php _se('Remove whitelist'); ?></a></li>
                                <?php
                            }
                    ?>

In app/themes/<your theme>/snippets/user_items_editor.php, add this to the end of the file:
PHP:
<div data-modal="form-whitelist-safe" class="hidden">
    <h1><?php _se('Confirm whitelist content as safe'); ?></h1>
    <p><?php _se("Do you really want to whitelist this content as safe?"); ?></p>
</div>
<div data-modal="form-whitelist-unsafe" class="hidden">
    <h1><?php _se('Confirm remove whitelist content as safe'); ?></h1>
    <p><?php _se("Do you really want to remove the safe whitelist for this content?"); ?></p>
</div>

In app/themes/<your theme>/tpl_list_item/image.php, change the first line to this:
PHP:
<div class="list-item fixed-size c%COLUMN_SIZE_IMAGE% gutter-margin-right-bottom privacy-%IMAGE_ALBUM_PRIVACY% %IMAGE_FLAG%" data-flag="%IMAGE_FLAG%" data-whitelisted="%IMAGE_WHITELISTED%" data-id="%IMAGE_ID_ENCODED%" data-type="image" data-description="%IMAGE_DESCRIPTION%" %DATA_OBJECT%>

(You are adding the data-whitelisted="%IMAGE_WHITELISTED%" bit.) Make that same change to the following files:
app/themes/<your theme>/tpl_list_item/image_plain.php
app/themes/<your theme>/tpl_list_item/album/image.php
app/themes/<your theme>/tpl_list_item/user/image.php

Made it this far? Congratulations, you're done! Go try it out and let me know if it works.
 
Last edited:
I think that the solution is pretty clever because the AdSense thing is very sensitive. I think that I will implement this in this way (or very similar).

Thanks.
 
I think that the solution is pretty clever because the AdSense thing is very sensitive. I think that I will implement this in this way (or very similar).

Thanks.

Hi Rodolfo,
Will you be implementing this feature in 3.6.* ?
 
Back
Top