(訳注:Zopeの一般的なプロダクトではなくて、CMF上にプロダクトを作る方法についてのチュートリアルです。ステップ・バイ・ステップが分かりやすいです。)
目次
このドキュメントは、2001年3月29日のニュースであった、CMFの1.0リリースに基づくもので、著者はこれをいつも更新しようと思っています。
このドキュメントに関する質問は、davew@digicool.comかjack@digicool.comへ送ってください。フィードバックを歓迎します。
Portalプロダクトとは、Zopeのアド・オンで、Portalの可能性を拡張するものです。PortalプロダクトはPortalのメンバーに以下のいずれか、あるいはすべての機能を持つオブジェクトを作らせることを可能にするものです。
プロダクトを適切なベースクラスから作ることにより、これらの機能のほとんどの利点を得ることができます。
このチュートリアルでは、Portalプロダクト(PortalPoll?)を作ります。ここでのサンプルは、メンバーがオンライン投票を作ることが可能なプロダクトです。それは頑健な検索能力を提供し、ユーザーはキーワードで質問や答えを検索することができます。このチュートリアルのゴールは、プロダクトがPortalプロダクトであるためには何が必要かを示しており、PortalPollプロダクトが実際に何をするのかについてはフォーカスを置いていません。
すべてのPortalコンテントは特定のメタデータを含みます。Portalでサポートされるプロパティの標準のセットは以下のようなものです。
以下のPortalプロパティもportal_catalogにより使われ、ユーザーが興味のあるオブジェクトを検索するために使われます。
PortalプロダクトはPortalコンテントであるとされるオブジェクトを生成します。メンバーはコンテントを作成し、そのコンテントのオーナーになります。また、コンテントはポータルの訪問者が見ることが出来るでしょう。Portalプロダクトはオブジェクトについてのワークフローに従うことができ、それはpendingやpublic、reviewedあるいはprivateなどのstateを持つことができます。
ポータルはサイト構築者が効果的にWebサイトをレイアウトし、検索やメンバーシップ、構造を容易にすることを可能にします。ポータル構造なしに加工品を積み上げることに基づくWebサイトはすぐに管理不能に陥ることでしょう。
ポータルオブジェクトは以下の利益をもたらします。
ポータルでないオブジェクトはCMFによって認識されません。あなたのプロダクトはポータルプロダクトとなることに意味がありますか?ポータルのユーザーがあなたのプロダクトのインスタンスを作成することを自分自身に問うてください。そして、もしyesならば、あなたのプロダクトをポータルプロダクトとすることに意味があります。
Zopeのインストール
CMFのインストール
ソフトウェアディレクトリに展開する(中略)
Zopeの再起動
ポータルの作成
ポータルプロダクトは多くの機能を受けることが出来ます。いくつかは特定のベースクラスから引き継がれ、いくつかは特定の属性やメソッドを提供します。
1つあなたがしなくてはならないことがあり、それを行うとCMFはあなたのプロダクトのオブジェクトをコンテントとして認識するのですが、それはそれをポータルに登録することです。理想的には、これはportal_types toolを使ってWebを通して行われます。また、あなたのコンテントに大して取ることのできるアクションを定義するためにportal_types toolを使うこともできます。
ステップはシンプルで、以下に従うだけです。
Zope 2.3.1とCMF 1.0をインストールします。
Control_Panel/ProductsでAdd Productボタンを押します。すると我々が追加したいプロダクトのIDとTitleを入力するところへ移動します。このフォームを以下のように埋めます。:
ID "PortalPollProduct"
Title "PortalPollProduct"
Generateボタンを押します。
/Control_Panel/Products/PortalPollProduct?へ行きます。ZClassを選んで追加します。以下を入力します。:
ID "PortalPollClass"
Title "PortalPollClass Title"
Meta_Type "PortalPollType"
Create Constructor objects?はチェックしたままにします。選択しなければならないベースクラスは、CMFCore:PortalContent?とCMFDefault:DefaultDublinCoreImpl?です。Include standard Zope persistent object base classes?はチェックしたままにします。Addボタンを押します。
上で作った/Control_Panel/Products/PortalPollProduct?/PortalPollClass?へ行き、Property Sheetsタブをクリックします。以下を入力します。:
ID "PortalPollPropertySheet"
Title "PortalPollPropertySheet_Title"
今作ったばかりのプロパティシートで、いくつかのプロパティを追加しなければなりません。プロパティは、名前、タイプ、デフォルト値からなります。Addをクリックするといくつかのプロパティをプロパティシートに追加できます。:
Name Type Value
---------- --------- -----------
Question string Sample Question
Choices lines
TotalVotes int 0
Choice0 int 0
Choice1 int 0
Choice2 int 0
Choice3 int 0
Choice4 int 0
Choice5 int 0
Choice6 int 0
Choice7 int 0
Choice8 int 0
Choice9 int 0
これで必要なプロパティは全部作りました。
PortalPollClass? (/Control_Panel/Products/PortalPollProduct?/PortalPollClass?) で、Define Permissionsタブをクリックします。パーミッションをCreate class instanceをAdd portal contentに変更して、Save Changesをクリックします。
始めに、PortalPollClass (/Control_Panel/Products/PortalPollProducts?/PortalPollClass?) の中にメソッドを作ります。そこへ行ってScriptを選んでAddボタンを押します。このスクリプトはSearchableTextと呼ばれるもので、コードは下にあります。
また、クラスの中にgetVoteStrというスクリプトを追加する必要があります。
以下からコピー・アンド・ペーストします。
クラスをインスタンス化するコンストラクタ・オブジェクトがクラスの外側に必要です。これはaddPortalPollClassPyスクリプトです。これはPortalPollProductプロダクトの中に必要ですが、クラスの中ではありません。
/Control_Panel/Products/PortalPollProduct?へ行って、addPortalPollClassPyスクリプトを追加します。
プロダクトのディレクトリ構造は以下のようになります。:
PortalPollProduct
+ PortalPollClass
| + EditDone
| + editForm
| + SearchableText (Searchable Text)
| + embeddedResultsView (Embedded Results View)
| + embeddedView (Embedded View)
| + embeddedVoteView (Embedded Vote View)
| + getVoteStr
| + processEmbeddedVotes (Process Embedded Votes)
| + view
| + viewResults (View Results)
+ PortalPollClass_add
+ PortalPollClass_addForm
+ PortalPollClass_add_permission
+ PortalPollClass_factory
+ Help
+ addPortalClassPy (Add Portal Types)
ルートにTestsフォルダを作ります。そのフォルダの中へ行って、CMF Siteを選んでAddをクリックします。
/Tests/TestPortal01?/portal_typesへ行って、Factory-based Type Informationを追加します。以下を入力します。:
ID "Poll"
Use default type information "(None)"
そしてAddをクリックします。新しいPollオブジェクトへ行って、そこの情報を変更します。:
Title "Poll_Title"
Description "This is an example of a poll product for CMF"
Meta_Type "PortalPollType"
Icon ""
Product Name "PortalPollProduct"
Factory method in product "addPortallClassPy"
Initial view name "editForm"
Filter content types (unchecked)
Allowed content types (none selected)
Allow Discussion (unchecked)
/Tests/TestPortal01?/portal_types/Pollで、Actionsタブをクリックします。ここで、いくつかのアクションを追加します。:
Name ID Action Permission Category
--------- ----- -------- --------------- --------
View View view View object
View Results ViewResults viewResults View object
Metadata Metadata metadata_edit_form Modify Portal Content object
これでCMFのためのプロダクトを作ることができました。このサンプルではとても簡単なプロダクトでしたが、一度どうやるのかを知れば、とても複雑なツールを追加できるでしょう。自身を持ってください。
何か問題が起きたら、以下をチェックしてみてください。
ZClassベースのPortalプロダクトを作るプロセスを見てきました。我々が示していないPortalの機能がたくさんあります。lib/python/Products/CMFCoreで定義されているクラスをみて、これらのクラスについて学んでください。良い情報がたくさんあります。
以下はコードとPortalPollClassのサンプルを形成するものです。必要ならばカット・アンド・ペーストできます。私は、ClassやProductが作成されたときに生成されるデフォルトのコードは含めていません。変更がなされた個所のみ示しています。
クラスの一部であるEditForm(DTMLメソッド)のコードです。このコードの目的はプロダクトをインスタンス化するために必要な情報をユーザから入力してもらうようにすることです。タイトルと質問と答えを聞きます。:
<dtml-var standard_html_header>
<form action="./" method="POST" enctype="multipart/form-data">
<h1 class="DesktopTitle"> Poll </h1>
<h2 class="DesktopTitle"> Add Content </h2>
<table width="100%" cellpadding="5">
<tr>
<td><b><i>Title:</i></b></td>
</tr>
<tr>
<td><input name="title" size="50" value=""></td>
</tr>
<tr>
<td><p>Please add a question for the poll.</p></td>
</tr>
<tr>
<td><b><i>Question:</i></b></td>
</tr>
<tr>
<td><input name="Question" size="50" value=""></td>
</tr>
<tr>
<td><b><i>Poll Answers: </i></b><br>(One per line.)<br>
<textarea name="Choices:lines" cols="30" rows="10"></textarea>
</td>
</tr>
</table>
<table>
<tr>
<td width="150"> </td>
<td><p align="right">
<input type="submit" value=" Finish " name="EditDone:method">
</p>
</td>
</tr>
</table>
</form>
<dtml-var standard_html_footer>
注意:このフォームに入力されると、EditDone(DTMLメソッド)が呼ばれます。そのコードは以下の通りです。:
<dtml-call "propertysheets.PortalPollPropertySheet.manage_changeProperties(REQUEST)">
<dtml-call "manage_changeProperties(REQUEST)">
<dtml-call indexObject>
<dtml-call "RESPONSE.redirect(URL2+'/folder_contents')">
最初の呼び出しは我々のインスタンスのプロパティを変更することに注意してください。2回目の呼び出しはオブジェクトのタイトルをセットします。3回目の呼び出しは我々のオブジェクトが参照されるようにカタログをアップデートするために必要です。最後に我々のインスタンスを追加した後、ユーザーにfolder_contentsを返します。
addPortalClassPy Pythonスクリプトのコードです。これはportal_typesツールを使ってプロダクトを追加するときに参照されます。:
Parameter List: id
return context.manage_addProduct['PortalPollProduct'
].PortalPollClass.createInObjectManager(id,context.REQUEST)
注意:これはプロダクトのインスタンスを作ります。
以下のコードは我々のクラスが提供するメソッドです。:
Method - SearchableText:
Parameter List:
return "%s %s %s %s" % (context.Title, context.Description
, context.Question, context.Choices)
Method - embeddedView:
<dtml-if "REQUEST.has_key('hasVoted')">
<dtml-if "REQUEST['hasVoted']=='true'">
<dtml-var embeddedResultsView>
<dtml-else>
<dtml-var embeddedVoteView>
</dtml-if>
<dtml-else>
<dtml-var embeddedVoteView>
</dtml-if>
Method - embeddedVoteView:
<table class="embeddedPoll" cellspacing="0" cellpadding="0" border="0" >
<tr>
<td width="100%">
<form action="&dtml-absolute_url;/processEmbeddedVotes" method="post">
<h3>
<div class="PollQuestion">
<dtml-var "Question">
</div>
</h3>
<dtml-in "Choices">
<div class="PollChoice">
<dtml-let respIndex="_['sequence-index']">
<input type="radio" name="responses" value="Choice&dtml-respIndex;">
<dtml-var "_['sequence-item']">
</dtml-let>
</div>
</dtml-in>
<center><br>
<input type="HIDDEN" name="origUrl" value="&dtml-URL0;">
<input type="SUBMIT" name="" value="Vote" align="center">
</center>
</form>
</font>
</td>
</tr>
</table>
Method - embeddedResultsView:
<table class="embeddedPoll" cellspacing="0" cellpadding="0" border="0">
<tr class="PollQuestion">
<td align="left" colspan="3">
<dtml-var Question>
</td>
</tr>
<tr><td colspan="3"> </td></tr>
<dtml-in "Choices">
<tr>
<td class="PollEmbeddedAnswer"><dtml-var "_['sequence-item']"></td>
<dtml-let votes="_['sequence-item']">
<dtml-let respStr="'Choice' + _.str(_['sequence-index'])">
<dtml-let numResps="_[respStr]">
<td class="PollEmbeddedAnswer">
<dtml-var "getVoteStr(numResps)">%
</td>
<td class="PollGraph">
<IMG SRC="&dtml-absolute_url;/redbar" WIDTH=<dtml-var "getVoteStr(numResps)">%
HEIGHT="10" BORDER="0">
</td>
</dtml-let>
</dtml-let>
</dtml-let>
</tr>
</dtml-in>
</table>
Method - getVoteStr:
Parameter List: votes
if context.TotalVotes:
return "%.2f" % (100*votes/context.TotalVotes)
else:
return "%s" % (votes)
Method - processEmbeddedVotes:
<dtml-if "REQUEST.has_key('responses')">
<dtml-if "_.hasattr(this(), REQUEST['responses'])">
<dtml-let vote="REQUEST['responses']">
<dtml-let curVotes="_[vote]+1">
<dtml-let totVotes="TotalVotes+1">
<dtml-call "propertysheets.PortalPollPropertySheet.
manage_changeProperties({REQUEST['responses']:curVotes})">
<dtml-call "propertysheets.PortalPollPropertySheet.
manage_changeProperties({'TotalVotes':totVotes})">
</dtml-let>
</dtml-let>
</dtml-let>
</dtml-if>
</dtml-if>
<dtml-var "RESPONSE.redirect(REQUEST['origUrl']+'?hasVoted=true')">
Method - view:
<dtml-var standard_html_header>
<div class="Desktop">
<div class="PortalPoll">
<dtml-var content_byline>
<dtml-var embeddedView>
</div>
</div>
<dtml-var standard_html_footer>
Method - viewResults:
<dtml-var standard_html_header>
<div class="Desktop">
<div class="PortalPoll">
<dtml-var content_byline>
<dtml-var embeddedResultsView>
</div>
</div>
<dtml-var standard_html_footer>
我々のサンプルで定義されているActionsには以下のものがあります。:
|| Name || Action || Permission || Category ||
|| View || view || View || object ||
|| View Results || viewResults || View || object ||
|| Metadata || metadata_edit_form || Modify portal content || object ||
我々のサンプルで定義されているプロパティは以下のものです。:
|| Name || Type || Explanation ||
|| Question || String || The Question for the POLL. ||
|| Choices || lines || A list of the Answers, one per line. ||
|| TotalVotes || int || Total count of all votes. ||
|| Choice0 || int || Count of times this choice voted for. ||
|| Choice1 || int || Count of times this choice voted for. ||
|| Choice2 || int || Count of times this choice voted for. ||
|| Choice3 || int || Count of times this choice voted for. ||
|| Choice4 || int || Count of times this choice voted for. ||
|| Choice5 || int || Count of times this choice voted for. ||
|| Choice6 || int || Count of times this choice voted for. ||
|| Choice7 || int || Count of times this choice voted for. ||
|| Choice8 || int || Count of times this choice voted for. ||
|| Choice9 || int || Count of times this choice voted for. ||
| Last edited Sat, 09 Sep 2006 19:29:44 +0900 | Edit this page |