2022年5月18日 星期三

CKAN Route(2.9): add_ckan_admin_tab

https://github.com/ckan/ckan/blob/2.9/ckanext/example_iconfigurer/plugin.py

參考 CKAN Extensions Tutorial(v2.9):一個空的 CKAN extension,建立 ckanext-example_iconfigurer 擴充套件。

建立:ckanext-example_iconfigurer  

  1. 進入Python Virtual Environment:
    . /usr/lib/ckan/default/bin/activate
  2. 切換目錄
    cd /usr/lib/ckan/default/src 
  3. 執行 ckan generate extension,輸入:ckanext-example_iconfigurer

撰寫 plugin 類別:ExampleIConfigurerPlugin

修改 ckanext-example_iconfigurer/ckanext/example_iconfigurer/plugin.py。參考完整程式,主要內容如下:

略...
import ckanext.example_iconfigurer.blueprint as blueprint

class ExampleIConfigurerPlugin(plugins.SingletonPlugin):
    略...

    plugins.implements(plugins.IConfigurer)
    plugins.implements(plugins.IBlueprint)

    # IConfigurer

    def update_config(self, config):
        # Add extension templates directory

        toolkit.add_template_directory(config, u'templates')
        # Add a new ckan-admin tabs for our extension
        toolkit.add_ckan_admin_tab(
            config, u'example_iconfigurer.config_one',
            u'My First Custom Config Tab'
        )
        toolkit.add_ckan_admin_tab(
            config, u'example_iconfigurer.config_two',
            u'My Second Custom Config Tab'
        )

撰寫 blueprint.py

參考完整程式,主要內容如下:

# encoding: utf-8

import ckan.lib.base as base
import ckan.lib.helpers as helpers
from flask import Blueprint
render = base.render

example_iconfigurer = Blueprint(u'example_iconfigurer', __name__)


def config_one():
    u'''Render the config template with the first custom title.'''

    return render(
        u'admin/myext_config.html',
        extra_vars={u'title': u'Custom Config Page A'}
    )


def config_two():
    u'''Render the config template with the second custom title.'''
    return render(
        u'admin/myext_config.html',
        extra_vars={u'title': u'Custom Config Page B'}
    )


def build_extra_admin_nav():
    u'''Return results of helpers.build_extra_admin_nav for testing.'''
    return helpers.build_extra_admin_nav()


example_iconfigurer.add_url_rule(
    u'/ckan-admin/myext_config_one', view_func=config_one
)
example_iconfigurer.add_url_rule(
    u'/ckan-admin/myext_config_two', view_func=config_two
)
example_iconfigurer.add_url_rule(
    u'/build_extra_admin_nav', view_func=build_extra_admin_nav
)

修改 setup.py:設定 entry_points

修改 ckanext-example_iconfigurer/setup.py 的 entry_points:

entry_points='''
        [ckan.plugins] 
        example_iconfigurer=ckanext.example_iconfigurer.plugin:ExampleIConfigurerPlugin

修改 ckan.ini:啟用 plugin

修改 /etc/ckan/default/ckan.ini。CKAN 的 extension plugins 必須加到 CKAN 設定檔的 ckan.plugins 中,CKAN 才會呼叫。

ckan.plugins = stats text_view 略... example_iconfigurer

安裝

  1. 進入Python Virtual Environment: 
    . /usr/lib/ckan/default/bin/activate
  2. 切換目錄:
    cd /usr/lib/ckan/default/src/example_iconfigurer
  3. 安裝 Plugin: 
    python setup.py develop
  4. 重啟 CKAN 服務:
    sudo supervisorctl restart ckan-uwsgi:*

測試

瀏覽 http://CKAN web site]/ckan-admin/ 可看到兩個新增的管理頁籤。




2022年5月17日 星期二

CKAN Route(2.9): Flask IBlueprint

參考 CKAN Extensions Tutorial(v2.9):一個空的 CKAN extension,建立 ckanext-example_flask_iblueprint 擴充套件。

建立:ckanext-example_flask_iblueprint  

  1. 進入Python Virtual Environment:
    . /usr/lib/ckan/default/bin/activate
  2. 切換目錄
    cd /usr/lib/ckan/default/src 
  3. 執行 ckan generate extension,輸入:ckanext-example_flask_iblueprint

撰寫 plugin 類別:ExampleFlaskIBlueprintPlugin

修改 ckanext-example_flask_iblueprint/ckanext/example_flask_iblueprint/plugin.py。參考完整程式,主要內容如下:

class ExampleFlaskIBlueprintPlugin(p.SingletonPlugin):
    u'''
    An example IBlueprint plugin to demonstrate Flask routing from an
    extension.
    '''
    p.implements(p.IBlueprint)
    p.implements(p.IConfigurer)

    # IConfigurer

    def update_config(self, config):
        # Add extension templates directory
        p.toolkit.add_template_directory(config, u'templates')

    def get_blueprint(self):
        u'''Return a Flask Blueprint object to be registered by the app.'''

        # Create Blueprint for plugin
        blueprint = Blueprint(self.name, self.__module__)
        blueprint.template_folder = u'templates'
        # Add plugin url rules to Blueprint object
        rules = [
            (u'/hello_plugin', u'hello_plugin', hello_plugin),
            (u'/', u'home', override_flask_home),
            (u'/helper_not_here', u'helper_not_here', helper_not_here),
            (u'/helper', u'helper_here', helper_here),
            (u'/flask_request', u'flask_request', flask_request),
        ]
        for rule in rules:
            blueprint.add_url_rule(*rule)

        return blueprint

修改 setup.py:設定 entry_points

修改 ckanext-example_flask_iblueprint/setup.py 的 entry_points:

entry_points='''
        [ckan.plugins] 
        example_flask_iblueprint=ckanext.example_flask_iblueprint.plugin:ExampleFlaskIBlueprintPlugin

修改 ckan.ini:啟用 plugin

修改 /etc/ckan/default/ckan.ini。CKAN 的 extension plugins 必須加到 CKAN 設定檔的 ckan.plugins 中,CKAN 才會呼叫。

ckan.plugins = stats text_view 略... example_flask_iblueprint

安裝

  1. 進入Python Virtual Environment: 
    . /usr/lib/ckan/default/bin/activate
  2. 切換目錄:
    cd /usr/lib/ckan/default/src/example_flask_iblueprint
  3. 安裝 Plugin: 
    python setup.py develop
  4. 重啟 CKAN 服務:
    sudo supervisorctl restart ckan-uwsgi:*

測試

瀏覽 http://[CKAN web site] 下的 /, /hello_plugin, /helper, /helper_not_here, /flask_request 可看到對應的程式內容。

2022年5月12日 星期四

CKAN JavaScript Tutorial 2(v2.9): Webassets

http://docs.ckan.org/en/2.9/theming/webassets.html

CSS 檔等靜態檔案有兩種方式加入:簡單的 extra_public_paths 作法或是 Webassets。但 JavaScript module 就只能透過 Webassets。

Webasset 的好處是在 production 版中會自動最小化檔案、快取並打包/bundling,以減少下載時間,載入指定相依檔⋯⋯。

CKAN 只有 *.js、*.css 會以 Webasset 資源處理,其它的靜態檔(圖檔、PDF)要透過 extra_public_paths 方式處理。

建立: ckanext-example_theme

延續 CKAN Theming Tutorial 1(v2.9): 靜態檔案(圖片與CSS) 往下做。

建立 assets 目錄

把 example_theme.css 改放到 assets 目錄下:

ckanext-example_theme/
  ckanext/
    example_theme/
      public/
        promoted-image.jpg
      assets/
        example_theme.css
        webassets.yml

另外再加一個 webassets.yml ,內容如下:

example_theme:
  output: example_theme/example_theme.css
  contents:
    - example_theme.css

修改 plugin.py

編輯 ckanext-example_theme/ckanext/example_theme/plugin.py ,在 update_config() 中呼叫 add_resource() 向 CKAN 註冊 assets 目錄,如下:

# encoding: utf-8

import ckan.plugins as plugins
import ckan.plugins.toolkit as toolkit

def most_popular_groups():
    略⋯⋯

class ExampleThemePlugin(plugins.SingletonPlugin):
    略⋯⋯

    def update_config(self, config):
        略⋯⋯

        # Register this plugin's assets directory with CKAN.
        # Here, 'assets' is the path to the webassets directory
        # (relative to this plugin.py file), and 'example_theme' is the name
        # that we'll use to refer to this assets directory from CKAN
        # templates.
        toolkit.add_resource('assets', 'example_theme')
  • toolkit.add_resource 的兩個參數:
    • assets: webassets 所在目錄(相對於plugin.py的路徑)
    • example_theme: 在 CKAN template 中存取此 assets 目錄的名稱

在 templates 中使用 {% asset %}

編輯 ckanext-example_theme/ckanext/example_theme/templates/base.html,在其中以 Jinja2 的{% asset %} 取代<link>  匯入檔案,如下:

{% ckan_extends %}

{% block styles %}
  {{ super() }}

  {# Import example_theme.css using Webassets.  'example_theme/' is
    the name that the example_theme/fanstatic directory was registered
    with when the toolkit.add_resource() function was called.
    'example_theme' is the name to the Webassets bundle file,
    registered inside webassets.yml file. #}
  {% asset 'example_theme/example_theme' %}

{% endblock %}

所有 template 都可用 {% asset %},Webassets 會加入需要的 <style> 、 <script>  來 include CSS 及JavaScript檔案,但最好把他們集中在 Jinja2 的  styles、scripts block.

Assets 不會在 {% asset %} 出現那行被 include。

用一個設定檔設定 Webassets 如何處理每個 asset 檔(是否打包/bundle,include的順序,include 檔要放在網頁的最前面還是最後面,檔案間的相依性...),參考:Assets

安裝

  1. 進入Python Virtual Environment: 
    . /usr/lib/ckan/default/bin/activate
  2. 切換目錄:
    cd /usr/lib/ckan/default/src/ckanext-example_theme
  3. 安裝 Plugin: 
    python setup.py develop
  4. 重啟 CKAN 服務:
    (production版) sudo supervisorctl restart ckan-uwsgi:*
    (develop版) ckan -c /etc/ckan/default/ckan.ini run

測試

檢視 http://[CKAN Website IP] 的網頁原始碼,在HTML的 <head>...</head> 可發現:

<link href="/webassets/webassets-external/2dce0ddda2882168f0c12c639a912e24_example_theme.css" rel="stylesheet"/>

2022年5月8日 星期日

CAKN 偵錯: flask_debugtoolbar

  1. 啟動 Python 虛擬環境
    . /usr/lib/ckan/default/bin/activate
  2. 安裝 flask_debugtoolbar
    pip install flask_debugtoolbar
  3. 開啟偵錯模式
    vi /etc/ckan/default/ckan.ini
    debug = true
  4. 重啟 CKAN
    sudo supervisorctl restart ckan-uwsgi:*
  • 若有錯檢視 uwsgi.ERR
    cat /etc/ckan/default/uwsgi.ERR


2022年5月6日 星期五

CKAN Templates Tutorial 9(v2.9): 存取自訂的 config 設定

http://docs.ckan.org/en/2.9/theming/templates.html#accessing-custom-config-settings-from-templates

template 裡的 app_globals 不是所有 CKAN config 設定都能存取,自訂的 config 設定無法取得,但透過 helper function 就可以。

參考:Using custom config settings in extensions

延續 上個 CKAN templates CSS tutorial

新增自訂 config 設定

如下 show_most_popular_groups 設定,決定首頁是否顯示最受歡迎群組。

修改 extension 根目錄下的 plugin.py,新增 helper function 存取自訂的 show_most_popular_groups 設定:

# encoding: utf-8


import ckan.plugins as plugins
import ckan.plugins.toolkit as toolkit
from ckan.common import config


def show_most_popular_groups():
    '''Return the value of the most_popular_groups config setting.

    To enable showing the most popular groups, add this line to the
    [app:main] section of your CKAN config file::

      ckan.example_theme.show_most_popular_groups = True

    Returns ``False`` by default, if the setting is not in the config file.

    :rtype: bool

    '''
    value = config.get('ckan.example_theme.show_most_popular_groups', False)
    value = toolkit.asbool(value)
    return value

略⋯⋯

class ExampleThemePlugin(plugins.SingletonPlugin):
    略⋯⋯

    def get_helpers(self):
        '''Register the most_popular_groups() function above as a template
        helper function.

        '''
        # Template helper function names should begin with the name of the
        # extension they belong to, to avoid clashing with functions from
        # other extensions.
        return {'example_theme_most_popular_groups': most_popular_groups,
                'example_theme_show_most_popular_groups':
                show_most_popular_groups,
                }

extension 加入的 config 設定名稱要以 extension 名稱開頭,以避免與其他 extension 或 CKAN 內建設定衝突(參考 Avoid name clashes)。

在 layout1.html 中呼叫自訂 helper function

修改 templates/home/layout1.html 如下:

{% ckan_extends %}

略⋯⋯

{# Show the most popular groups if the show_most_popular_groups config setting
   is True, otherwise call the super block. #}
{% block featured_organization %}
<p>example_theme_show_most_popular_groups: [{{h.example_theme_show_most_popular_groups()}}]</p>
  {% if h.example_theme_show_most_popular_groups() %}
    {% snippet 'snippets/example_theme_most_popular_groups.html' %}
  {% else %}
    {{ super() }}
  {% endif %}
{% endblock %}

修改 ckan.ini 設定

/etc/ckan/default/ckan.ini

[app:main]
## example_theme
ckan.example_theme.show_most_popular_groups = False

安裝

  1. 進入Python Virtual Environment: 
    . /usr/lib/ckan/default/bin/activate
  2. 切換目錄:
    cd /usr/lib/ckan/default/src/ckanext-example_theme
  3. 安裝 Plugin: 
    python setup.py develop
  4. 重啟 CKAN 服務:
    (production版) sudo supervisorctl restart ckan-uwsgi:*
    (develop版) ckan -c /etc/ckan/default/ckan.ini run


CKAN Templates Tutorial 8(v2.9): CSS class

http://docs.ckan.org/en/2.9/theming/templates.html#html-tags-and-css-classes

CKAN 使用的 CSS class 來自:

  1. Bootstrap 3.3.7
    這個版本 Bootstrap 的 HTML, CSS, JavaScript 都可在 CKAN 中使用
  2. CKAN 的 development primer page
    CKAN 網站下的 /testing/primer

調整 snippet 裡的 CSS class

修改 ckanext-example_theme/ckanext/example_theme/templates/snippets/example_theme_most_popular_groups.html 如下:

{#

Renders a list of the site's most popular groups.

groups - the list of groups to render

#}
<div class="box">
  <header class="module-heading">
    <h3>Most popular groups</h3>
  </header>
  <section class="module-content">
    <ul class="unstyled">
      {% for group in groups %}
      <li>
        <a href="{{ h.url_for('group_read', action='read', id=group.name) }}">
          <h3>{{ group.display_name }}</h3>
        </a>
        {% if group.description %}
          <p>
            {{ h.markdown_extract(group.description, extract_length=80) }}
          </p>
        {% else %}
          <p>{{ _('This group has no description') }}</p>
        {% endif %}
        {% if group.package_count %}
          <strong>{{ ungettext('{num} Dataset', '{num} Datasets', group.package_count).format(num=group.package_count) }}</strong>
        {% else %}
          <span>{{ _('0 Datasets') }}</span>
        {% endif %}
      </li>
    {% endfor %}
 </ul>
  <section>
</div>

以上加入:

  • 把程式碼放進
    • <div class="box">
    • <header class="module-heading">
    • <section class="module-content">
  • 用 Bootstrap 的 class="unstyled" 移除 <ul> 前的黑點

調整 layout1.html 的 CSS class

修改 ckanext-example_theme/ckanext/example_theme/templates/home/layout1.html 如下:

{% ckan_extends %}

{% block featured_group %}
  <div class="box">
    <header class="module-heading">
      <h3>Recent activity</h3>
    </header>
    <div class="module-content">
    <ul>
        {% for stream in h.recently_changed_packages_activity_stream(limit=4) %}
            <li>stream id: {{ stream.id }}</li>
        {% endfor %}
    </ul>
    </div>
  </div>

{% endblock %}

{% block featured_organization %}
  {% snippet 'snippets/example_theme_most_popular_groups.html',
             groups=h.example_theme_most_popular_groups()  %}
{% endblock %}


重新檢視網頁:






CKAN Templates Tutorial 7(v2.9): 自訂 snippet

http://docs.ckan.org/en/2.9/theming/templates.html#adding-your-own-template-snippets

延續 上個 CKAN templates snippet tutorial,plugin 只要在它的 templates/snippets 目錄下加檔案即可自訂 snippet。

以下撰寫一個 snippet 來顯示最受歡迎群組。


在 templates/snippets 目錄下新增 snippet

建立目錄 ckanext-example_theme/ckanext/example_theme/templates/snippets,並在其下加入一個檔案:example_theme_most_popular_groups.html ,內容如下:

{#

Renders a list of the site's most popular groups.

groups - the list of groups to render

#}
<h3>熱門群組(自訂snippet)</h3>
<ul>
  {% for group in groups %}
    <li>
      <a href="{{ h.url_for('group_read', action='read', id=group.name) }}">
        <h3>{{ group.display_name }}</h3>
      </a>
      {% if group.description %}
        <p>
          {{ h.markdown_extract(group.description, extract_length=80) }}
        </p>
      {% else %}
        <p>{{ _('This group has no description') }}</p>
      {% endif %}
      {% if group.package_count %}
        <strong>{{ ungettext('{num} Dataset', '{num} Datasets', group.package_count).format(num=group.package_count) }}</strong>
      {% else %}
        <span>{{ _('0 Datasets') }}</span>
      {% endif %}
    </li>
  {% endfor %}
</ul>

  • snippet 檔最開頭需以 docstring 描述需傳入的參數。在這個例子裡是:groups。
  • 呼叫 url_for() 為每個 group 名稱產生超連結至群組頁面
  • 若群組有 description 呼叫 markdown_extract() 產生其內容,若無則呼叫 _() 把 'This group has no description' 訊息翻譯使用者瀏覽器對應的語言。
  • 呼叫 ungettext() 顯示群組的資料集數(翻譯成對應的複數表示方式)。
  • 群組屬性/group attributes 如: {{ group.name }}, {{ group.display_name }}, {{ group.description }}, {{ group.package_count }},及其他 CKAN 物件(packages/datasets, organizations, users…) 屬性可透過 CKAN API 讀取,如 group_show()。


修改 templates/home/layout1.html

如下:

{% ckan_extends %}

{% block featured_group %}
  {{ h.recently_changed_packages_activity_stream(limit=4) }}
{% endblock %}

{% block featured_organization %}
  {% snippet 'snippets/example_theme_most_popular_groups.html',
             groups=h.example_theme_most_popular_groups()  %}
{% endblock %}

重啟 CKAN,再次瀏覽網頁可看到:

覆寫預設 snippet

若 plugin 加入的 snippet 名稱與 CKAN 預設 snippet 同名,則 plugin 的 snippet 會覆寫預設 snippet,無論是否有使用它。

若兩個 plugins 有同名 snippet 便會互相覆蓋。為避免這個問題,snippet 檔名最好以它的 extension 名稱開頭,如:snippets/example_theme_*.html。

snippet 「不能」存取 template 全域環境變數 c ( Variables and functions available to templates). 但可以存取:

  • h
  • app_globals
  • request
  • 其他父 template 透過 {% snippet %} 呼叫時傳入的參數