<!--toc--> |
|
## Введение |
|
У CppCMS будет полная встроенная поддержка плагинов в готовящейся к выходу версии 1.2, |
которая будет выпущена позже в этом году (2012). |
|
Между тем, в CppCMS 1.0 уже есть все основы для поддержки плагинов. Темплейты уже полностью подключаемы (загрузка разделемого объекта и темплейта готова к использованию). |
Также есть новый класс booster::shared_object, позволяющий легко загружать динамические библиотеки. |
|
Однако, в зависимости от архитектуры Вашего приложения, выбор точного решения может варьироваться. В следующих разделах будут рассмотрены различные варианты развития событий. |
Если здесь ничего не понятно, пожалуйста, спросите об этом в мейл-листе и вернитесь сюда для надлежащего исправления этой страницы. |
|
## Темплейты подключаемые с помощью разделяемого объекта. |
|
У каждого разделяемого объекта темплейта есть глобальный конструктор и деструктор. |
Как только разделяемый объект загружен, вызывается конструктор и регистрирует себя в singleton'е cppcms::views::pool, становясь видимым для всех. При выгрузке он разрегистрируется с помощью глобального деструктора и больше не может больше использоваться. |
Это простая и прозрачная семантика. |
|
Если плагин связан с кодом темплейта, будет загружаться его собственный темплейт: |
Если плагин связан с кодом темплейта, он загрузит собственный темплейт: |
|
libmy_plugin.so |
my_application.cpp: |
// мой код |
{ |
... |
render("my_plugin_skin","some_view",some_content); |
} |
my_view.cpp <- my_view.tmpl |
// этот код генерируется автоматически |
namespace { |
struct loader { |
loader() { cppcms::views::pool::instance().add(my_skin_generator()); } |
~loader() { cppcms::views::pool::instance().remove(my_skin_generator());} |
} loader_instance; |
} |
|
Поэтому, как только вы загрузите разделяемый объект, выполнится loader::loader() и |
зарегистрирует view, а когда будете выгружать разделяемый объект - loader::~loader() |
разрегистрирует его. |
|
Удостоверьтесь, что у каждого темплейта есть собственное имя skin'а. |
|
|
## Темплейты с собственным фреймворком плагинов |
|
Если Вы реализовали собственный фреймворк плагинов, есть два метода, позволяющие плагинам определить их собственные темплейты для создаваемого ими контента, интегрируемого в страницу, генерируемую главным приложением. |
|
### Использование потоков |
|
Основное приложение определяет макет всей страницы: |
|
|
|
<% skin basic_skin %> |
<% view basic_view uses content::basic_content %> |
<html> |
... |
<body> |
... |
|
<!--- aux-часть--> |
|
<% foreach p in plugings %> |
<% item %><%= p.rendered_aux_html_content | raw %><% end %> |
|
<% end %> |
|
|
... |
<!--- Main-часть--> |
|
<% foreach p in plugings %> |
<% item %> |
<div id="plugin_<%=p.id %>" > |
<%= p.rendered_main_html_content | raw %> |
|
</div> |
|
<% end %> |
|
<% end %> |
|
... |
|
|
</body> |
<% end view %> |
... |
|
<% end skin %> |
|
Каждый плагин определяет темплейты для своего собственного содержимого: |
|
<% skin plugin_a %> |
<% view main_html uses plugin_a_content::some_content_b %> |
<% template render() %> |
<!-- Указание render-функции --> |
какой-то конкретный html плагина |
делающий что-то, что ему нужно |
|
<% end template %> |
<% view aux_html uses plugin_a_content::some_content_a %> |
<% template render() %> |
<!-- Указание render-функции --> |
какой-то конкретный html плагина |
делающий что-то, что ему нужно |
|
<% end template %> |
|
<% end view %> |
|
<% end skin %> |
|
|
Предположим, что наш плагин должен предоставлять следующее API: |
|
class plugin { |
public: |
virtual std::string main_html(cppcms::applicatin *app) = 0; |
|
virtual std::string aux_html(cppcms::application *app) = 0; |
}; |
|
Содержимое каждого плагина рендерится примерно так: |
|
// main_application.cpp |
|
content::basic_content c; |
// подготавливаем c |
... |
// рендерим плагины |
for(int i=0;i<loaded_plugins.size();i++) { |
c.plugins[i].rendered_main_html_content = loaded_plugins[i]->main_html(this); |
c.plugins[i].rendered_aux_html_content = loaded_plugins[i]->aux_html(this); |
} |
|
render("basic_skin","basic_view",c); |
|
На стороне плагина, это выглядит примерно так: |
|
std::string main_html(cppcms::applicatin *app) |
{ |
plugin_a_content::some_content_b c; |
... |
std::ostringstream buf; |
app->render("plugin_a","main_html",c,buf); |
return buf.str(); |
|
} |
|
std::string aux_html(cppcms::applicatin *app) |
{ |
plugin_a_content::some_content_a c; |
... |
std::ostringstream buf; |
app->render("plugin_a","aux_html",c,buf); |
return buf.str(); |
|
} |
|
|
### С помощью команды 'render' |
|
Начиная с CppCMS 0.99.12 есть команда <% render ... %> См. [Рендеринг других view](wikipp/en/page/cppcms_1x_templates_comm#Rendering.other.views). |
|
Основное приложение определяет макет всей страницы: |
|
<% skin basic_skin %> |
<% view basic_view uses content::basic_content %> |
<html> |
... |
<body> |
... |
|
<!--- aux-часть--> |
|
<% foreach p in plugings %> |
<% item %><% render p->name(), "aux_html" with p->aux_html_content() %><% end %> |
|
<% end %> |
|
|
... |
<!--- Main-часть--> |
|
<% foreach p in plugings %> |
<% item %> |
<div id="plugin_<%=p->id() %>" > |
<% render p->name(), "main_html" with p->main_html_content() %> |
</div> |
|
<% end %> |
|
<% end %> |
|
... |
|
|
</body> |
<% end view %> |
... |
|
<% end skin %> |
|
, где p - указатель на объект плагина. |
|
Темплейт плагина будет реализовываться как и в предыдущем разделе. См. выше. |
|
Предположим, что наш плагин должен предоставлять следующее API: |
|
|
class plugin { |
public: |
virtual void prepare() = 0; |
|
virtual cppcms::base_content &main_html_content() = 0; |
|
virtual cppcms::base_content &aux_html_content() = 0; |
|
virtual std::string name() = 0; |
}; |
|
|
Содержимое каждого плагина рендерится примерно так: |
|
basic_content { |
... |
// указатели на плагины |
std::vector<plugin *> plugins; |
} |
|
main_application.cpp: |
|
content::basic_content c; |
// подготавливаем c |
... |
for(p in plugins) |
p->prepare(); |
render("basic_skin","basic_view",c); |
|
На стороне плагина, это выглядит примерно так: |
|
class my_plugin : public plugin { |
public: |
virtual void prepare() |
{ |
... установки main_ и aux_... |
|
} |
virtual std::string name() { return "plugin_a"; } |
|
virtual cppcms::basic_content &main_html_content() { return main_; } |
|
virtual cppcms::basic_content &aux_html_content() { return aux_; } |
|
|
private: |
|
plugin_a_content::some_content_b aux_; |
|
plugin_a_content::some_content_a main_; |
|
}; |
|
|
|
std::string main_html(cppcms::applicatin *app) |
{ |
plugin_a_content::some_content_b c; |
... |
std::ostringstream buf; |
app->render("plugin_a","main_html",c,buf); |
return buf.str(); |
|
} |
|
|
std::string aux_html(cppcms::applicatin *app) |
{ |
plugin_a_content::some_content_a c; |
... |
std::ostringstream buf; |
app->render("plugin_a","aux_html",c,buf); |
return buf.str(); |
|
} |
|
|
### Вариации |
|
Вариации на базе вышеуказанного, в зависимости от нужд Вашего приложения. |
|
Определение плагина: |
|
|
plugin { |
void render_main(cppcms::application &); |
|
void render_aux(cppcms::application &); |
|
} |
|
, где плагин выполняется как: |
|
|
render_main(cppcms::application &app) |
{ |
app.render("plugin_a","main_html",my_content); |
|
} |
|
|
и темплейт выглядит как: |
|
|
<% foreach p in plugins %> |
<% item %> |
<% c++ p->render_main(content.app()); %> |
<% end item %> |
<% end foreach %> |
|
|
## Повторное использование skin'а |
|
В этом разделе рассматривается возможность плагина по повторному использованию skin'а, осуществляющаяся ядром приложения. |
|
Предположим, что Ваш плагин хочет расширить basic_skin::basic_view (т.е. наследоваться от них) и иметь контроль над всей страницей. |
|
Это намного хитрее. |
|
Вы не можете наследоваться от них, т.к. cppcms_tmpl_cc не генерирует заголовочные файлы. |
Вместо этого, сделайте следующее. |
|
Вместо записи: <% skin basic_skin %>, используйте неименованный skin: <% skin %> |
|
Затем скомпилируйте основной skin: |
|
Main view cpp: |
|
cppcms_tmpl_cc -s basic_skin basic_view.tmpl some_other_main_views.tmpl -o basic_skin.cpp |
g++ basic_skin.cpp -o libbasic_skin.so |
|
cppcms_tmpl_cc -s plugin_a basic_view.tmpl plugin_a.tmpl -o plugin_a.cpp |
g++ plugin_code.cpp plugin_a.cpp -o libplugin_a.so |
|
|
|
Обратите внимание, что basic_view.tmpl присутствует дважды, первый - под skin'ом=namespace, а второй раз - под skin'ом=namespace plugin_a. |
|
Это дублирует c++-код и требует от плагина действительно использовать код skin'а приложения. |
Это не идеально, но терпимо, по крайней мере пока cppcms_tmpl_cc не станет поддерживать генерацию файлов .h и .cpp. |