Sort by items on browse page

Hi everyone,

I’m trying to add other element sets to sort by on browse page but something is not working properly. I tried modifying the code by itself first, then downloaded a plugin, but still stuck.

Would be glad if someone could help!!

this is my browse.php

<?php
$pageTitle = __('Browse Items');
echo head(array('title'=>$pageTitle,'bodyclass' => 'items browse'));
?>

<h1><?php echo $pageTitle;?> <?php echo __('(%s total)', $total_results); ?></h1>

<nav class="items-nav navigation secondary-nav">
    <?php echo public_nav_items(); ?>
</nav>

<?php echo item_search_filters(); ?>
<?php echo pagination_links(); ?>

<?php if ($total_results > 0): ?>

<?php
$sortLinks[__('Title')] = 'Dublin Core,Title';
$sortLinks[__('Obv Die #')] = 'Item Type Metadata,Obverse Die #';
$sortLinks[__('Rev Die #')] = 'Item Type Metadata,Reverse Die #';
$sortLinks[__('SCADA')] = 'Item Type Metadata,SCADA';
$sortLinks[__('Identifier')] = 'Dublin Core,Identifier';
?>
<div id="sort-links">
    <span class="sort-label"><?php echo __('Sort by: '); ?></span><?php echo browse_sort_links($sortLinks); ?>
</div>

<?php endif; ?>

<?php foreach (loop('items') as $item): ?>
<div class="item record">
    <h2><?php echo link_to_item(null, array('class'=>'permalink')); ?></h2>
    <div class="item-meta">
    <?php if (metadata('item', 'has files')): ?>
    <div class="item-img">
        <?php echo link_to_item(item_image()); ?>
    </div>
    <?php endif; ?>

    <!-- Identifier Field -->
    <?php if ($identifier = metadata('item', array('Dublin Core', 'Identifier'), array('snippet' => 250))): ?>
    <div class="item-identifier"><strong>Identifier:</strong>
        <?php echo $identifier; ?>
    </div>
    <?php endif; ?>

    <!-- Obverse Die Field -->
    <?php if ($obvDie = metadata('item', array('Item Type Metadata', 'Obverse Die #'), array('snippet' => 250))): ?>
    <div class="item-obv-die"><strong>Obv Die #:</strong>
        <?php echo $obvDie; ?>
    </div>
    <?php endif; ?>

    <!-- Reverse Die Field -->
    <?php if ($revDie = metadata('item', array('Item Type Metadata', 'Reverse Die #'), array('snippet' => 250))): ?>
    <div class="item-rev-die"><strong>Rev Die #:</strong>
        <?php echo $revDie; ?>
    </div>
    <?php endif; ?>

    <!-- SCADA Field -->
    <?php if ($scada = metadata('item', array('Item Type Metadata', 'SCADA'), array('snippet' => 250))): ?>
    <div class="item-scada"><strong>SCADA:</strong>
        <?php echo $scada; ?>
    </div>
    <?php endif; ?>
    
    <?php fire_plugin_hook('public_items_browse_each', array('view' => $this, 'item' =>$item)); ?>
    </div><!-- end class="item-meta" -->
</div><!-- end class="item hentry" -->
<?php endforeach; ?>

<?php echo pagination_links(); ?>

<div id="outputs">
    <span class="outputs-label"><?php echo __('Output Formats'); ?></span>
    <?php echo output_format_list(false); ?>
</div>

<?php fire_plugin_hook('public_items_browse', array('items'=>$items, 'view' => $this)); ?>
<?php echo foot(); ?>

this is my plugin.php

<?php
/**
 * Sort Browse Results Plugin
 * Allows sorting of results on the browse items page
 */

define('SORTBROWSERESULTS_PLUGIN_VERSION', '0.2');

add_plugin_hook('install', 'sortbrowse_install');
add_plugin_hook('uninstall', 'sortbrowse_uninstall');
add_plugin_hook('item_browse_sql', 'sortbrowse');

function sortbrowse_install()
{
    set_option('sortbrowseresults_plugin_version', SORTBROWSERESULTS_PLUGIN_VERSION);
}

function sortbrowse_uninstall()
{
    delete_option('sortbrowseresults_plugin_version');
}

function sortbrowse($args)
{
    // Extract 'select' and 'params' from the passed arguments
    $select = $args['select'];
    $params = $args['params'];

    // Use 'sort_field' and 'sort_dir' parameters from browse_sort_links()
    if (isset($_GET['sort_field'])) {
        $sort_field = $_GET['sort_field'];
        $sort_dir = isset($_GET['sort_dir']) ? $_GET['sort_dir'] : 'a'; // Default to ascending

        // Log the sort_field and sort_dir for debugging
        error_log("SortBrowseResults Plugin: sort_field = " . $sort_field);
        error_log("SortBrowseResults Plugin: sort_dir = " . $sort_dir);

        // Split the 'sort_field' parameter into element set and element name
        $sortTerm = explode(",", $sort_field, 2);
        if (sizeof($sortTerm) === 2) {
            $elementSetName = trim($sortTerm[0]);
            $elementName = trim($sortTerm[1]);
            $includesElementSet = true;

            error_log("SortBrowseResults Plugin: Element Set = " . $elementSetName);
            error_log("SortBrowseResults Plugin: Element Name = " . $elementName);
        } else {
            $elementName = trim($sortTerm[0]);
            $includesElementSet = false;

            error_log("SortBrowseResults Plugin: Element Name (No Set) = " . $elementName);
        }

        if ($includesElementSet) {
            sort_by_element($select, $elementSetName, $elementName, $sort_dir);
        } else {
            // If no element set is provided, try to sort by Omeka's item fields
            sort_by_omeka($select, $elementName, $sort_dir);
        }
    }
}

function sort_by_element($select, $elementSetName, $elementName, $sort_dir)
{
    $db = get_db();

    // Get the element ID
    $elementTable = $db->getTable('Element');
    $elementSelect = $elementTable->getSelect();
    $elementSelect->setIntegrityCheck(false)
        ->join(array('element_sets' => $db->ElementSet), 'elements.element_set_id = element_sets.id', array())
        ->where('element_sets.name = ?', $elementSetName)
        ->where('elements.name = ?', $elementName);

    $element = $elementTable->fetchObject($elementSelect);

    if ($element) {
        $elementId = $element->id;

        error_log("SortBrowseResults Plugin: Found Element ID = " . $elementId);

        // Join the element_texts table to sort by the element
        $select->joinLeft(
            array('element_texts_sort' => $db->ElementText),
            'element_texts_sort.record_id = items.id AND element_texts_sort.record_type = "Item" AND element_texts_sort.element_id = ' . $elementId,
            array()
        );

        // Determine sort direction
        $dir = (strtolower($sort_dir) == 'd') ? 'DESC' : 'ASC';

        error_log("SortBrowseResults Plugin: Sorting direction = " . $dir);

        // Add the order by clause
        $select->order('element_texts_sort.text ' . $dir);
    } else {
        error_log("SortBrowseResults Plugin: Element not found. Falling back to Omeka's item fields.");
        // If the element is not found, fall back to sorting by Omeka's item fields
        sort_by_omeka($select, $elementName, $sort_dir);
    }
}

function sort_by_omeka($select, $sortField, $sort_dir)
{
    // Check if sortField is a valid column name
    $validCols = array('id', 'added', 'modified', 'public', 'featured');

    if (in_array($sortField, $validCols)) {
        $dir = (strtolower($sort_dir) == 'd') ? 'DESC' : 'ASC';
        error_log("SortBrowseResults Plugin: Sorting Omeka field = " . $sortField . " Direction = " . $dir);
        $select->order('items.' . $sortField . ' ' . $dir);
    } else {
        error_log("SortBrowseResults Plugin: Invalid sort field = " . $sortField);
    }
}
?>

this is my functions.php

<?php
/**
 * Modify a hex color by the given number of steps (out of 255).
 *
 * Adapted from a solution by Torkil Johnsen.
 *
 * @param string $color
 * @param int $steps
 * @link http://stackoverflow.com/questions/3512311/how-to-generate-lighter-darker-color-with-php
 */
function thanksroy_brighten($color, $steps) {
    $steps = max(-255, min(255, $steps));
    $hex = str_replace('#', '', $color);
    $r = hexdec(substr($hex,0,2));
    $g = hexdec(substr($hex,2,2));
    $b = hexdec(substr($hex,4,2));

    $r = max(0,min(255,$r + $steps));
    $g = max(0,min(255,$g + $steps));  
    $b = max(0,min(255,$b + $steps));

    $r_hex = str_pad(dechex($r), 2, '0', STR_PAD_LEFT);
    $g_hex = str_pad(dechex($g), 2, '0', STR_PAD_LEFT);
    $b_hex = str_pad(dechex($b), 2, '0', STR_PAD_LEFT);

     return '#'.$r_hex.$g_hex.$b_hex;
}


/**
 * Return the HTML for summarizing a random featured exhibit
 *
 * @return string
 */
function thanksroy_display_random_featured_records($type = null, $count = 2, $hasImage = null)
{
    $records = get_records(ucfirst($type), array('featured' => 1,
                                     'sort_field' => 'random',
                                     'hasImage' => $hasImage), $count);

    $recordPaths = array(
        'collection' => 'collections/',
        'exhibit' => 'exhibit-builder/exhibits/',
        'item' => 'items/'
    );
    $html = '';
    if ($records) {
        foreach ($records as $record) {
            $html .= get_view()->partial($recordPaths[$type] . 'single.php', array($type => $record));
            // Note: 'release_object' is deprecated in newer versions of Omeka; you may remove it if unnecessary.
            release_object($record);
        }
        if ($type == 'exhibits') {
            $html = apply_filters('exhibit_builder_display_random_featured_exhibit', $html);
        }
    }
    return $html;
}

// Custom function to modify the item browse SQL for sorting by custom metadata fields
if (!function_exists('custom_sort_item_browse_sql')) {
    function custom_sort_item_browse_sql($select, $params)
    {
        $db = get_db();

        if (isset($_GET['sort_field'])) {
            $sortField = $_GET['sort_field'];
            $sortDir = (isset($_GET['sort_dir']) && strtolower($_GET['sort_dir']) == 'd') ? 'DESC' : 'ASC';

            $sortTerm = explode(",", $sortField, 2);
            if (sizeof($sortTerm) === 2) {
                $elementSetName = trim($sortTerm[0]);
                $elementName = trim($sortTerm[1]);

                // Get the element ID
                $elementTable = $db->getTable('Element');
                $elementSelect = $elementTable->getSelect();
                $elementSelect->setIntegrityCheck(false)
                    ->join(
                        array('element_sets' => $db->ElementSet),
                        'elements.element_set_id = element_sets.id',
                        array()
                    )
                    ->where('element_sets.name = ?', $elementSetName)
                    ->where('elements.name = ?', $elementName);

                $element = $elementTable->fetchObject($elementSelect);

                if ($element) {
                    $elementId = $element->id;

                    // Join the element_texts table to sort by the element
                    $select->joinLeft(
                        array('element_texts_sort' => $db->ElementText),
                        'element_texts_sort.record_id = items.id AND element_texts_sort.record_type = "Item" AND element_texts_sort.element_id = ' . $elementId,
                        array()
                    );

                    // Add the order by clause
                    $select->order('element_texts_sort.text ' . $sortDir);
                }
            } else {
                // If no element set is provided, try to sort by Omeka's item fields
                $validCols = array('id', 'added', 'modified', 'public', 'featured');

                if (in_array($sortField, $validCols)) {
                    $select->order('items.' . $sortField . ' ' . $sortDir);
                }
            }
        }
    }

    // Add the custom function to the item_browse_sql hook
    add_filter('item_browse_sql', 'custom_sort_item_browse_sql');
}

and this was the readme from the plugin, after which something is not working and my opinion is it was something to do with sortbrowse function’s parameters

<!-- 
    Sort Browse Results needs to be invoked from within the theme.
    Copy and paste the following into /themes/myTheme/items/browse.php
    (Between the pagination and the items)

    $sortBy->get_link(@param)
    @param is a string representing the value to sort on. E.g. 'title'
    The plugin will first check the element_texts table (Dublin Core) then check the items table 
    except where @param = 'type' in which case the plugin will only use the items table. (Omeka item_type)
    
    This behaviour can be overridden by pre-pending dc. or omeka.
    E.g. omeka.modified will sort on the modified value in the items table.
    or   dc.type will sort based on the Dublin Core 'type' instead of the Omeka item_type
-->

<?php $sortBy = new SortBy; // requires Sort Browse Results Plugin ?>
<ul class="sort-options">
    <li class="title <?php echo $sortBy->get_cssclass("title"); ?>"><a href="<?php echo $sortBy->get_link('dc.title'); ?>" title="Sort by title">Title</a></li>
    <li class="type <?php echo $sortBy->get_cssclass("type"); ?>"><a href="<?php echo $sortBy->get_link('type'); ?>" title="Sort by type">Type</a></li>
    <li class="date <?php echo $sortBy->get_cssclass("omeka.modified"); ?>"><a href="<?php echo $sortBy->get_link('omeka.modified'); ?>" title="Sort by modified">Date</a></li>
</ul>

Try it without the plugin.

That’s a very old plugin that doesn’t really have any applicability today. So I’d uninstall it and remove the code you added from its readme, if you did at all.

The first snippet you have, of your theme, looks fine. Or do you want to add more choices than the 5 I see listed there?

I’m actually a little curious: where did you find this plugin?

Thanks for your reply. I’ll uninstall it.

The problem with this is that it only sorts the identifier and the title. The other ones don’t work.

Hmmm… they look OK from here.

Can you link to your site? Maybe seeing it in action will reveal something.

this page has more fields entered

https://tigranes.armnumres.org/collections/show/23

this page has more fields entered

actually now that I removed that plugin, nothing gets sorted

Do you have some other plugin(s) that are related to sorting?

You have a few things going on there: first I don’t think the sorters of any kind will work on the collections/show page… it looks like you’ve altered that page to look more like a browse page, but it really isn’t one so you can show the links there but they won’t function.

On your actual browse page at Browse Items · The Tigranes Die Study Initiative you have something else happening: only the Identifier sort is working at all and the others are all being ignored, including Title.

No other plugins related to sorting.

But even before altering, the sort fields wouldn’t work

so the collections/show page wouldn’t be able to have the sorting option?

For collections/show, you can probably make it work with sorting links but you’d have to change the code of the page to look for them and use them.

For the rest, it really looks like something else is here affecting the order. Are you sure you don’t have another plugin (or a modification to the code, or anything like that) related to order?

ohh sorry, actually I have the Item Order plugin

I just deactivated it, but I don’t think. it helped

Hmmm, OK.

Have you changed the names of any of the metadata, or the element sets, or anything like that? This is acting more or less like how the system acts when you try to sort by an unknown element… but the elements look correct.

I guess just as a double-check, could you list all the plugins you’re using? Or, temporarily deactivate them all and see if that changes anything.

ArchiveRepertory
COinS
Collections Plus
Collection Tree
CSV Import
Derivative Images
Dropbox
Exhibit Builder
Hide Elements
Item Relations
Simple Pages

It looks like Collections Plus does something related to sorting; try disabling that one as a test? Or again, just all of them to rule in or out plugins as a whole as the culprit.

Whoops, I’m sorry, the relevant issue (maybe) is right in your first post. This in your theme’s functions.php:

// Custom function to modify the item browse SQL for sorting by custom metadata fields
if (!function_exists('custom_sort_item_browse_sql')) {
    function custom_sort_item_browse_sql($select, $params)
    {
        $db = get_db();

        if (isset($_GET['sort_field'])) {
            $sortField = $_GET['sort_field'];
            $sortDir = (isset($_GET['sort_dir']) && strtolower($_GET['sort_dir']) == 'd') ? 'DESC' : 'ASC';

            $sortTerm = explode(",", $sortField, 2);
            if (sizeof($sortTerm) === 2) {
                $elementSetName = trim($sortTerm[0]);
                $elementName = trim($sortTerm[1]);

                // Get the element ID
                $elementTable = $db->getTable('Element');
                $elementSelect = $elementTable->getSelect();
                $elementSelect->setIntegrityCheck(false)
                    ->join(
                        array('element_sets' => $db->ElementSet),
                        'elements.element_set_id = element_sets.id',
                        array()
                    )
                    ->where('element_sets.name = ?', $elementSetName)
                    ->where('elements.name = ?', $elementName);

                $element = $elementTable->fetchObject($elementSelect);

                if ($element) {
                    $elementId = $element->id;

                    // Join the element_texts table to sort by the element
                    $select->joinLeft(
                        array('element_texts_sort' => $db->ElementText),
                        'element_texts_sort.record_id = items.id AND element_texts_sort.record_type = "Item" AND element_texts_sort.element_id = ' . $elementId,
                        array()
                    );

                    // Add the order by clause
                    $select->order('element_texts_sort.text ' . $sortDir);
                }
            } else {
                // If no element set is provided, try to sort by Omeka's item fields
                $validCols = array('id', 'added', 'modified', 'public', 'featured');

                if (in_array($sortField, $validCols)) {
                    $select->order('items.' . $sortField . ' ' . $sortDir);
                }
            }
        }
    }

    // Add the custom function to the item_browse_sql hook
    add_filter('item_browse_sql', 'custom_sort_item_browse_sql');
}

That’s definitely affecting sorting. If you don’t need that, I’d take it out. I’m not actually sure what it’s intending to do… it looks like it’s basically the core’s sorting support but rewritten. Or a partial rewrite of what that plugin you removed was doing? I’m not sure if you wrote this yourself or got it from somewhere.

In general, I’d just say that those sorting links you’re having trouble with should work fine on a “vanilla” Omeka install.

I just deleted that function, uninstalled the plugin.

Does it matter how this looks?

$sortLinks = array(
    __('Title') => 'Dublin Core,Title',
    __('Obv Die #') => 'Item Type Metadata,Obverse Die #',
    __('Rev Die #') => 'Item Type Metadata,Reverse Die #',
    __('SCADA') => 'Item Type Metadata,SCADA',
    __('Identifier') => 'Dublin Core,Identifier',
);
$sortLinks[__('Title')] = 'Dublin Core,Title';
$sortLinks[__('Obv Die #')] = 'Item Type Metadata,Obverse Die #';
$sortLinks[__('Rev Die #')] = 'Item Type Metadata,Reverse Die #';
$sortLinks[__('SCADA')] = 'Item Type Metadata,SCADA';
$sortLinks[__('Identifier')] = 'Dublin Core,Identifier';

And it looks like the Title is only being sorted in ascending but not descending

I’m at a bit of a loss here as the behavior remains that only the Identifier sort actually functions.

I can only really suggest that you try deactivating every plugin, make sure you’ve removed all the custom sorting code you added or modified anywhere (the links themselves are fine but anywhere else), and see if that resolves your problem.

Also of course if you’ve customized other places, for example editing the core, or messing with the queries at the Apache level, that could all be relevant also.

Thank you, I will try to play around a little more. By the way it doesnt do it on the admin side either

I was able to fix the sorting by updating the site. Part of the problem remains. I’m running into an issue where the sorting is lexicographical rather than numeric. For example, numbers are sorted like this: 1, 10, 2 instead of the expected 1, 2, 10.
Is there an easy way to fix this?

https://tigranes.armnumres.org/items/browse?collection=23&sort_field=Item+Type+Metadata%2CReverse+Die+%23