Modify CKAN header's layout with custom plugin

情境描述:
我們希望能夠使用客製化的icon與排版於Header上
使得最終的畫面如下
不過根據CKAN Helpers.py的程式碼
只有 build_nav_icon 函式比較接近我們的需求
但他們是使用fontawesome的icon
且排版在超連結的左方,如下
而他們產生這類nav tab的函式
主要是由 _make_menu_item函式組裝對應的內容
這函式不僅Render排版而已
例如: 根據當前頁面是否與該tab要導到的頁面一致(也就是同頁面)
如果一致,就會讓該超連結<a>多了一個Style叫做active,就會反灰
這些原有的功能我們還是要保留 (e.g., _link_to,_link_active)
因此,就不是單單取代掉而已
我們需要客製化一個自己的 Template Helper function
客製化Template Helper Function可以參考官網的這篇
主要需要增加的程式碼如下
根目錄為 ~/ckan/lib/default/src/ckanext-ytdataservice/ckanext/ytdataservice
./public/chefHeader.css
.module-content:last-child {
    padding-bottom: 0px;
}


.mystyle {
    display: flex;
    float: right;
}

.mystyle-ul>li {
    float: left;
    list-style: none;
    text-align: center;
    padding-left: 1em;
    padding-right: 1em;
    margin-bottom: 1em;
    border-radius: 5px;
}

.mystyle-ul>li.hover {
    background-color: #003647;
}

.mystyle-ul>li.active {
    background-color: #003647;
}

.mystyle-ul>li>a>img {
    animation: colorize 2s cubic-bezier(0, 0, .78, .36) 1;
    background: transparent;
    display: block;
    width: 24px;
    height: 24px;
    margin: 0 auto;
}

.mystyle-ul>li>a {
    text-decoration: none;
}

./plugin.py
import ckan.plugins as plugins
import ckan.plugins.toolkit as toolkit
import ckan.lib.helpers as helpers
import copy
from webhelpers.html import literal, tags

KEY_IMG = 'img'

def build_nav_top_img(menu_item, title, **kw):
    _menu_items = toolkit.config['routes.named_routes']
    if menu_item not in _menu_items:
        raise Exception('menu item `%s` cannot be found' % menu_item)
    item = copy.copy(_menu_items[menu_item])
    item.update(kw)
    active = helpers._link_active(item)
    needed = item.pop('needed')
    for need in needed:
        if need not in kw:
            raise Exception('menu item `%s` need parameter `%s`'
                            % (menu_item, need))
    if KEY_IMG in kw:
        img = literal('<img src="' + kw.get(KEY_IMG) + '">')
    link = helpers._link_to(img + title, menu_item, suppress_active_class=True, **item)
    if active:
        return literal('<li class="active">') + link + literal('</li>')
    return literal('<li>') + link + literal('</li>')


class YtdataservicePlugin(plugins.SingletonPlugin):
    plugins.implements(plugins.IConfigurer)
    plugins.implements(plugins.IRoutes)
    plugins.implements(plugins.ITemplateHelpers)

    # IConfigurer
    def update_config(self, config_):
        toolkit.add_template_directory(config_, 'templates')
        toolkit.add_public_directory(config_, 'public')
        toolkit.add_resource('fanstatic', 'ytdataservice')

    # IRoutes
    def before_map(self, map):
        map.connect('cookData','/cookingData', controller='ckanext.ytdataservice.controllers:cookDataController', action='firstPage')
        map.connect('browseData','/cookingData/browseData', controller='ckanext.ytdataservice.controllers:cookDataController', action='browseData')
        map.connect('addData','/cookingData/addData', controller='ckanext.ytdataservice.controllers:cookDataController', action='addData')
        map.connect('cleanData','/cookingData/cleanData', controller='ckanext.ytdataservice.controllers:cookDataController', action='cleanData')
        map.connect('mergeData','/cookingData/mergeData', controller='ckanext.ytdataservice.controllers:cookDataController', action='mergeData')
        return map

    def after_map(self, map):
        return map

    # ITemplateHelpers
    def get_helpers(self):
        return {'ytdataservice_build_nav_top_img': build_nav_top_img}
最主要的就是實作 ITemplateHelpers 的 get_helpers function
將我們的function註冊到 helper function裡,好讓 template render時可以呼叫
Route的設定,可以參考 Add a new tab, controller, page on CKAN
接著將對應的image放在對應的目錄下後,就可以繼續進行header.html的撰寫
./templates/header.html
{% ckan_extends %} 
{% block header_site_navigation_tabs %}
  {% block mystyles %}
    <link rel="stylesheet" href="/chefHeader.css" />
  {% endblock %}
  <nav>
    <ul class="mystyle-ul">
      {{ h.ytdataservice_build_nav_top_img('cookData', _('Data Cooking'), img='/icons/cooker.png' ) }}
      {{ h.ytdataservice_build_nav_top_img('search', _('Datasets'), img='/icons/carrots.png' ) }}
      {{ h.ytdataservice_build_nav_top_img('organizations_index', _('Organizations'), img='/icons/farmer.png' ) }}
      {{ h.ytdataservice_build_nav_top_img('group_index', _('Groups'), img='/icons/vegetables.png' ) }}
      {{ h.ytdataservice_build_nav_top_img('about', _('About'), img='/icons/receipt.png' ) }}
    </ul>
  </nav>
{% endblock %}
這樣子,就可以透過Custom Template Helper function 完成我們的需求啦

留言