search implementation completed; video ordering feature when browsing by category...
authorCalin Burloiu <calin.burloiu@gmail.com>
Wed, 7 Sep 2011 11:56:49 +0000 (14:56 +0300)
committerCalin Burloiu <calin.burloiu@gmail.com>
Wed, 7 Sep 2011 11:56:49 +0000 (14:56 +0300)
12 files changed:
application/config/autoload.php
application/config/config.php
application/controllers/catalog.php
application/language/english/error_lang.php [new file with mode: 0644]
application/language/english/ui_lang.php
application/models/videos_model.php
application/views/catalog/search_results_view.php
application/views/catalog/videos_summary_view.php
application/views/error_view.php [new file with mode: 0644]
application/views/header.php
css/catalog.css
css/default.css

index bac0142..c3d8699 100644 (file)
@@ -96,7 +96,7 @@ $autoload['config'] = array('p2p-tube');
 |
 */
 
-$autoload['language'] = array('ui');
+$autoload['language'] = array('ui', 'error');
 
 
 /*
index 10b45e0..755fc91 100644 (file)
@@ -126,7 +126,7 @@ $config['subclass_prefix'] = 'MY_';
 | DO NOT CHANGE THIS UNLESS YOU FULLY UNDERSTAND THE REPERCUSSIONS!!
 |
 */
-$config['permitted_uri_chars'] = 'a-z 0-9~%.:_\-';
+$config['permitted_uri_chars'] = 'a-z 0-9~%.:_\-+';
 
 
 /*
index 4966152..95a6908 100644 (file)
@@ -86,7 +86,7 @@ class Catalog extends CI_Controller {
                $this->load->view('html_end');
        }
        
-       public function category($category_name, $offset = 0)
+       public function category($category_name, $ordering = 'hottest', $offset = 0)
        {
                // **
                // ** LOADING MODEL
@@ -98,12 +98,14 @@ class Catalog extends CI_Controller {
                $this->load->model('videos_model');
                $vs_data['videos'] = $this->videos_model->get_videos_summary(
                        $vs_data['category_id'], intval($offset),
-                       $this->config->item('videos_per_page'));
+                       $this->config->item('videos_per_page'), $ordering);
+               
+               $vs_data['ordering'] = $ordering;
                
                // Pagination
                $this->load->library('pagination');
-               $pg_config['base_url'] = site_url("catalog/category/$category_name/");
-               $pg_config['uri_segment'] = 4;
+               $pg_config['base_url'] = site_url("catalog/category/$category_name/$ordering/");
+               $pg_config['uri_segment'] = 5;
                $pg_config['total_rows'] = $this->videos_model->get_videos_count(
                        $vs_data['category_id']);
                $pg_config['per_page'] = $this->config->item('videos_per_page');
@@ -127,7 +129,10 @@ class Catalog extends CI_Controller {
                // ** LOADING VIEWS
                // **
                $this->load->view('html_begin', $this->html_head_params);
-               $this->load->view('header');
+               $this->load->view('header', array(
+                       'search_category_name'=>$vs_data['category_name'],
+                       'search_category_title'=>$vs_data['category_title']
+               ));
                
 //             $main_params['content'] = $this->load->view('catalog/category_view', $data, TRUE);
                $main_params['content'] = 
@@ -146,28 +151,43 @@ class Catalog extends CI_Controller {
                $str_post_search = $this->input->post('search', TRUE);
                if ($search_query === "" && $str_post_search !== FALSE) 
                        redirect('catalog/search/'. $this->input->post('search', TRUE));
+
+               $this->load->model('videos_model');
                
                // **
                // ** LOADING MODEL
                // **
+               // Search query is encoded for URL and must be decoded.
+               $enc_search_query = $search_query;
+               $search_query = $this->videos_model->decode_search_query($search_query);
+               $results_data['search_query'] = $search_query;
+
                // Category
                $results_data = $this->_get_category_data($category_name);
                if ($results_data === NULL)
                        $results_data = array('category_id'=>NULL);
                
-               $results_data['search_query'] = $search_query;
+               // Page header data
+               $header_data['search_query'] = $search_query;
+               if ($category_name !== NULL)
+               {
+                       $header_data['search_category_name'] = $results_data['category_name'];
+                       $header_data['search_category_title'] = $results_data['category_title'];
+               }
                
                // Check if search string is valid.
                if (strlen($search_query) < 4)
                {
-                       $results_data['videos'] = NULL;
+                       //$results_data['videos'] = NULL;
+                       $this->error($this->lang->line('error_search_query_too_short'),
+                               $header_data);
+                       return;
                }
                else
                {
                        // Retrieve search results.
-                       $this->load->model('videos_model');
                        $results_data['count'] = $this->videos_model->search_videos(
-                               $search_query);
+                               $search_query, 0, 0, $results_data['category_id']);
                        $results_data['videos'] = $this->videos_model->search_videos(
                                $search_query, intval($offset),
                                $this->config->item('search_results_per_page'),
@@ -177,7 +197,7 @@ class Catalog extends CI_Controller {
        
                        // Pagination
                        $this->load->library('pagination');
-                       $pg_config['base_url'] = site_url("catalog/search/$search_query/");
+                       $pg_config['base_url'] = site_url("catalog/search/$enc_search_query/");
                        $pg_config['uri_segment'] = 4;
                        $pg_config['total_rows'] = $results_data['count'];
                        $pg_config['per_page'] =
@@ -185,7 +205,8 @@ class Catalog extends CI_Controller {
                        $this->pagination->initialize($pg_config);
                        $results_data['pagination'] = $this->pagination->create_links();
                }
-                       
+               
+               // HTML head parameters
                $params = array(        'title' => $this->config->item('site_name'),
                                                        'css' => array(
                                                                'catalog.css'
@@ -199,7 +220,7 @@ class Catalog extends CI_Controller {
                // ** LOADING VIEWS
                // **
                $this->load->view('html_begin', $this->html_head_params);
-               $this->load->view('header');
+               $this->load->view('header', $header_data);
                
                // Search Results
                $main_params['content'] = 
@@ -212,6 +233,29 @@ class Catalog extends CI_Controller {
                $this->load->view('html_end');
        }
        
+       public function error($msg, $header_data)
+       {
+               $params = array(        'title' => 'Error - '. $this->config->item('site_name'),
+                       //'css' => array(),
+                       //'js' => array(),
+                       //'metas' => array('description'=>'','keywords'=>'')
+               );
+               $this->load->library('html_head_params', $params);
+               
+               // **
+               // ** LOADING VIEWS
+               // **
+               $this->load->view('html_begin', $this->html_head_params);
+               $this->load->view('header', $header_data);
+               
+               $main_params['content'] = $this->load->view('error_view', array('msg'=>$msg), TRUE);
+               $main_params['side'] = $this->load->view('side_default', NULL, TRUE);
+               $this->load->view('main', $main_params);
+               
+               $this->load->view('footer');
+               $this->load->view('html_end');
+       }
+       
        public function _get_category_data($category_name)
        {
                if ($category_name === NULL)
diff --git a/application/language/english/error_lang.php b/application/language/english/error_lang.php
new file mode 100644 (file)
index 0000000..ae3448f
--- /dev/null
@@ -0,0 +1,6 @@
+<?php
+
+$lang['error_search_query_too_short'] = 'The search query is too short! You must enter at least 4 characters.';
+
+/* End of file error_lang.php */
+/* Location: ./application/language/english/error_lang.php */
\ No newline at end of file
index 05f8ea1..d8f62b7 100644 (file)
@@ -11,6 +11,7 @@ $lang['ui_nav_menu_register'] = 'Register';
 
 // Search
 $lang['ui_search'] = 'Search';
+$lang['ui_search_in'] = 'Search in';
 $lang['ui_search_results_for'] = 'Search Results for';
 $lang['ui_results'] = 'result(s)';
 
@@ -27,6 +28,11 @@ $lang['ui_categ_tech-talks'] = 'TechTalks';
 $lang['ui_categ_events'] = 'Events';
 $lang['ui_categ_karaoke'] = 'Karaoke';
 
+// Ordering
+$lang['ui_show_hottest'] = "Show Hottest";
+$lang['ui_show_newest'] = "Show Newest";
+$lang['ui_sort_alphabetically'] = "Sort Alphabetically";
+
 // Pagination
 $lang['ui_page_first'] = 'First';
 $lang['ui_page_previous'] = 'Previous';
index 69e7328..73bdde7 100644 (file)
@@ -22,10 +22,18 @@ class Videos_model extends CI_Model {
         * Retrieves information about a set of videos which are going to be
         * displayed in the catalog.
         *
-        * TODO: filter, limit, ordering parameters
         * @param               int $category_id        DB category ID
         * @param               int $offset
         * @param               int $count
+        * @param               string $ordering        control videos ording by these
+        * possibilities:
+        * <ul>
+        *   <li><strong>'hottest':</strong> newest most appreciated first. An
+        *   appreciated video is one which has a bigger
+        *   score = views + likes - dislikes.</li>
+        *   <li><strong>'newest':</strong> newest first.</li>
+        *   <li><strong>'alphabetically':</strong> sort alphabetically.</li>
+        * </ul>
         * @return              array   a list of videos, each one being an assoc array with:
         * <ul>
         *   <li>id, name, title, duration, thumbs_count, default_thumb, views => from DB</li>
@@ -35,17 +43,34 @@ class Videos_model extends CI_Model {
         *   <li>thumbs => thumbnail images' URLs</li>
         * </ul>
         */
-       public function get_videos_summary($category_id, $offset, $count)
+       public function get_videos_summary($category_id, $offset, $count, $ordering = 'hottest')
        {
                $this->load->helper('text');
                
+               // Ordering
+               switch ($ordering)
+               {
+               case 'hottest':
+                       $order_statement = "ORDER BY date DESC, score DESC, RAND()";
+                       break;
+               case 'newest':
+                       $order_statement = "ORDER BY date DESC";
+                       break;
+               case 'alphabetically':
+                       $order_statement = "ORDER BY title";
+                       break;
+                       
+               default:
+                       $order_statement = "";
+               }
+               
                $query = $this->db->query(
-                       'SELECT id, name, title, duration, user_id, views, thumbs_count,
-                               default_thumb
+                       "SELECT id, name, title, duration, user_id, views, thumbs_count,
+                               default_thumb, (views + likes - dislikes) AS score
                        FROM `videos`
                        WHERE category_id = ?
-                       ORDER BY name
-                       LIMIT ?, ?', // TODO summary order 
+                       $order_statement
+                       LIMIT ?, ?", 
                        array(intval($category_id), $offset, $count)); 
                
                if ($query->num_rows() > 0)
@@ -213,15 +238,36 @@ class Videos_model extends CI_Model {
         */
        public function search_videos($search_query, $offset = 0, $count = 0, 
                                                                        $category_id = NULL)
-       {
+       {               
+               if (! $this->is_advanced_search_query($search_query)) // if natural language mode
+               {
+                       $search_cond = "MATCH (title, description, tags)
+                                       AGAINST ('$search_query')";
+                       $relevance = "$search_cond AS relevance";
+               }       // if boolean mode
+               else
+               {
+                       $against = "AGAINST ('$search_query' IN BOOLEAN MODE)";
+                       $search_cond = "MATCH (title, description, tags)
+                                       $against";
+                       $relevance = "( (0.5 * (MATCH(title) $against))
+                                       + (0.3 * (MATCH(tags) $against))
+                                       + (0.2 * (MATCH(description) $against)) ) AS relevance";
+               }
+               
                if ($count === 0)
                {
                        $selected_columns = "COUNT(*) count";
+                       $order = "";
                        $limit = "";
                }
                else
                { 
-                       $selected_columns = "id, name, title, duration, user_id, views, thumbs_count, default_thumb, date, description";
+                       $selected_columns = "id, name, title, duration, user_id, views,
+                                       thumbs_count, default_thumb, date, description,
+                                       (views + likes - dislikes) AS score, 
+                                       $relevance";
+                       $order = "ORDER BY relevance DESC, score DESC";
                        $limit = "LIMIT $offset, $count";
                }
                
@@ -232,12 +278,12 @@ class Videos_model extends CI_Model {
 
                $search_query = trim($search_query);
                
-               $query = $this->db->query(
-                       "SELECT $selected_columns
+               $str_query = "SELECT $selected_columns
                        FROM `videos`
-                       WHERE  $category_cond MATCH (title, description, tags) AGAINST (?)
-                       $limit",
-                       array($search_query)); 
+                       WHERE  $category_cond $search_cond
+                       $order
+                       $limit";
+               $query = $this->db->query($str_query);
                
                if ($query->num_rows() > 0)
                {
@@ -271,6 +317,34 @@ class Videos_model extends CI_Model {
                
                return $videos;
        }
+       
+       public function decode_search_query($search_query)
+       {
+               $search_query = urldecode($search_query);
+               
+               $search_query = str_replace('_AST_', '*', $search_query);
+               $search_query = str_replace('_AND_', '+', $search_query);
+               $search_query = str_replace('_GT_', '>', $search_query);
+               $search_query = str_replace('_LT_', '<', $search_query);
+               $search_query = str_replace('_PO_', '(', $search_query);
+               $search_query = str_replace('_PC_', ')', $search_query);
+               $search_query = str_replace('_LOW_', '~', $search_query);
+               $search_query = str_replace('_QUO_', '"', $search_query);
+               
+               return $search_query;
+       }
+       
+       /**
+        * Return TRUE if it contains any special caracter from an advanced search
+        * query.
+        * @param string $search_query
+        * @return boolean
+        */
+       public function is_advanced_search_query($search_query)
+       {
+               return (preg_match('/\*|\+|\-|>|\<|\(|\)|~|"/', $search_query) == 0
+                       ? FALSE : TRUE);
+       }
 }
 
 /* End of file videos_model.php */
index 38f036d..f6146d4 100644 (file)
@@ -1,6 +1,6 @@
 <div class="search_results">
        <h1><?php echo $this->lang->line('ui_search_results_for')
-               . " &laquo;$search_query&raquo;" ?></h1>
+               . " <em>&laquo;" . htmlentities($search_query) . "&raquo;</em>" ?></h1>
                
        <p><?php echo "$count ". $this->lang->line('ui_results'); ?></p>
 
index 25e496b..66e5f29 100644 (file)
@@ -4,6 +4,21 @@
                        <?php echo $category_title ?>
                </a>
        </h1>
+       
+       <?php
+       if ( isset($ordering))
+       {
+               $ordering_opts = array(
+                       'hottest'=> $this->lang->line('ui_show_hottest'),
+                       'newest'=> $this->lang->line('ui_show_newest'),
+                       'alphabetically'=> $this->lang->line('ui_sort_alphabetically')
+               );
+               
+               echo '<p>';
+               echo form_dropdown('ordering', $ordering_opts, $ordering, 'id="ordering"');
+               echo '</p>';
+       }
+       ?>
 
        <?php echo $pagination ?>
 
        
        <div style="clear: both"></div>
 
-</div>
\ No newline at end of file
+</div>
+
+<script type="text/javascript">
+       $(function() {
+               $('#ordering').change(function(e) {
+                       var uri = "<?php echo site_url("catalog/category/$category_name") ?>";
+                       
+                       // Default ordering
+                       if ($(this).val() != "hottest")
+                               uri += "/" + $(this).val();
+                               
+                       window.location = uri;
+               });
+       });
+
+</script>
\ No newline at end of file
diff --git a/application/views/error_view.php b/application/views/error_view.php
new file mode 100644 (file)
index 0000000..e83c7d3
--- /dev/null
@@ -0,0 +1,6 @@
+<div class="ui-state-error ui-corner-all" style="padding: 0 .7em;">
+       <p style="text-align: center">
+               <span class="ui-icon ui-icon-alert" style="float: left; margin-right: .3em;"></span>
+               <span><?php echo $msg ?></span>
+       </p>
+</div>
\ No newline at end of file
index 14047e1..987b6c4 100644 (file)
@@ -1,6 +1,11 @@
-<?php if (! isset($selected_menu)):
-$selected_menu = '';
-endif ?>
+<?php 
+       if (! isset($selected_menu))
+               $selected_menu = '';
+       if (! isset($search_query))
+               $search_query = '';
+       if (! isset($search_category_name))
+               $search_category_name = NULL;
+?>
 
 <ul
        id="nav-menu">
@@ -52,7 +57,15 @@ endif ?>
        
        
        <?php echo form_open('catalog/search', array('id'=>'quick-search')); ?>
-               <input type="text" id="search" name="search" value="" />
+               <label for="search"><?php 
+                       if ($search_category_name === NULL)
+                               echo $this->lang->line('ui_search') . ':';
+                       else
+                               echo $this->lang->line('ui_search_in') . ' <em>'
+                               . $search_category_title . '</em>:';
+               ?>
+               </label>
+               <input type="text" id="search" name="search" value="<?php echo $search_query ?>" />
                <input type="submit" id="button-quick-search" value="<?php echo $this->lang->line('ui_search') ?>" />
                <a href="#" id="button-js-quick-search" style="display:none">
                        <?php echo $this->lang->line('ui_search') ?>
@@ -68,22 +81,28 @@ endif ?>
                // Fake JS submit via CI URI segments
                var fakeSubmit = function() {
                        var searchQuery = $('#search').val();
-                       searchQuery = searchQuery.replace(/\*/g, '*2A');  // *
-                       searchQuery = searchQuery.replace(/\+/g, '*2B');        // +
+
+                       if (searchQuery.length < 4)
+                       {
+                               alert('<?php echo $this->lang->line('error_search_query_too_short') ?>');
+                               return;
+                       }
+                       
+                       searchQuery = searchQuery.replace(/\*/g, '_AST_');  // *
+                       searchQuery = searchQuery.replace(/\+/g, '_AND_');      // +
+                       //searchQuery = searchQuery.replace(/\-/g, '_');        // -
                        searchQuery = searchQuery.replace(/\s/g, '+');  // <white spaces>
-                       searchQuery = searchQuery.replace(/>/g, '*3E'); // >
-                       searchQuery = searchQuery.replace(/\</g, '*3C');        // <
-                       searchQuery = searchQuery.replace(/\(/g, '*28');        // (
-                       searchQuery = searchQuery.replace(/\)/g, '*29');        // )
-                       searchQuery = searchQuery.replace(/~/g, '*7E'); // ~ 
-                       searchQuery = searchQuery.replace(/"/g, '*22'); // " 
+                       searchQuery = searchQuery.replace(/>/g, '_GT_');        // >
+                       searchQuery = searchQuery.replace(/\</g, '_LT_');       // <
+                       searchQuery = searchQuery.replace(/\(/g, '_PO_');       // (
+                       searchQuery = searchQuery.replace(/\)/g, '_PC_');       // )
+                       searchQuery = searchQuery.replace(/~/g, '_LOW_');       // ~ 
+                       searchQuery = searchQuery.replace(/"/g, '_QUO_');       // " 
                        searchQuery = encodeURI(searchQuery);
-                       searchQuery = searchQuery.replace(/\*/g, '%');  // *
-
-                       alert(searchQuery);
                        
                        window.location = "<?php echo site_url('catalog/search') ?>/" 
-                               + searchQuery;
+                               + searchQuery + '/0'
+                               + "<?php echo ($search_category_name === NULL ? '' : '/'. $search_category_name) ?>";
                };
                
                $('#button-js-quick-search')
index da17393..7f941ea 100644 (file)
        margin-bottom: 1em;
 }
 
+.search-result
+{
+       position: relative;
+}
+
+.search-result-info
+{
+       position: absolute;
+       left : 154px;
+       margin-left: 8px;
+}
+
 .category-title
 {
        /* border-bottom: 1px solid rgb(108,162,222); */
index 7a2f789..2c0b2fe 100644 (file)
@@ -215,4 +215,5 @@ h1
 {
        clear: both;
        word-spacing: 0.5em;
+       margin-bottom: 0.5em;
 }