(機械翻訳)承認を追加する¶
:app: Pyramid`は:term: authentication`と:term: authorization`のための機能を提供します。両方の機能を使用して、アプリケーションにセキュリティを提供します。私たちのアプリケーションは、現在、サーバーにアクセスして誰でもページを表示し、編集し、wikiに追加することができます。 * group *のメンバーである人だけが ` group:editors``にwikiページの追加と編集を許可するように変更しますが、引き続きサーバーにアクセスできる人は誰でもページを見ることができます。
また、すべてのページにログインページとログアウトリンクを追加します。ログインページは、デフォルトの"403 Forbidden "ページではなく、アクセス権が必要なビューへのアクセスをユーザが拒否されたときに表示されます。
以下の手順でアクセス制御を実装します。
- パスワードハッシュの依存関係を追加する。
- ユーザーとグループを追加します(新しいモジュールである `` security.py``)。
- :term: ACL`( models.py`)を追加してください。
- :term: 認証ポリシー`と:term: `認証ポリシー`( ` __init __。py``)を追加してください。
- `` edit_page``と `` add_page``ビュー( `` views.py``)にterm: `permission`宣言を追加してください。
次に、ログイン機能とログアウト機能を追加します。
- `` login``と `` logout``ビュー( `` views.py``)を追加してください。
- ログインテンプレート( `` login.pt``)を追加してください。
- 既存のビューが `` logged_in``フラグをレンダラーに返すようにします( `` views.py``)。
- ログインしてページを表示または編集するときに表示される "ログアウト"リンクを追加します( `` view.pt``、 `` edit.pt``)。
アクセス制御¶
依存関係を追加する¶
:ref: wiki_defining_views`のように、新しい依存関係が必要です。私たちは `bcrypt <https://pypi.org/project/bcrypt/> `_package 'を、この依存関係を setup() 関数の requires``パラメータに割り当てることによって、チュートリアルパッケージの `setup.py``ファイルに追加します。
`` setup.py``を開き、以下のように編集してください:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 | import os
from setuptools import setup, find_packages
here = os.path.abspath(os.path.dirname(__file__))
with open(os.path.join(here, 'README.txt')) as f:
README = f.read()
with open(os.path.join(here, 'CHANGES.txt')) as f:
CHANGES = f.read()
requires = [
'plaster_pastedeploy',
'pyramid >= 1.9a',
'pyramid_chameleon',
'pyramid_debugtoolbar',
'pyramid_retry',
'pyramid_tm',
'pyramid_zodbconn',
'transaction',
'ZODB3',
'waitress',
'docutils',
'bcrypt',
]
tests_require = [
'WebTest >= 1.3.1', # py3 compat
'pytest',
'pytest-cov',
]
setup(
name='tutorial',
version='0.0',
description='myproj',
long_description=README + '\n\n' + CHANGES,
classifiers=[
'Programming Language :: Python',
'Framework :: Pyramid',
'Topic :: Internet :: WWW/HTTP',
'Topic :: Internet :: WWW/HTTP :: WSGI :: Application',
],
author='',
author_email='',
url='',
keywords='web pyramid pylons',
packages=find_packages(),
include_package_data=True,
zip_safe=False,
extras_require={
'testing': tests_require,
},
install_requires=requires,
entry_points={
'paste.app_factory': [
'main = tutorial:main',
],
},
)
|
強調表示された行だけを追加する必要があります。
:ref: wiki-running-pip-install`のように `pip install -e .``を実行することを忘れないでください。
注釈
PyPIの `` bcrypt``パッケージを使ってパスワードを安全にハッシュしています。 bcryptがシステム上の問題である場合、パスワード用の他の一方向ハッシュアルゴリズムがあります。これは、パスワードの保存に対して一般的な一方向ハッシュに対して承認されたアルゴリズムであることを確認するだけです。
ユーザーとグループを追加する¶
次の内容の新しい `` tutorial / security.py``モジュールを作成してください:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | import bcrypt
def hash_password(pw):
hashed_pw = bcrypt.hashpw(pw.encode('utf-8'), bcrypt.gensalt())
# return unicode instead of bytes because databases handle it better
return hashed_pw.decode('utf-8')
def check_password(expected_hash, pw):
if expected_hash is not None:
return bcrypt.checkpw(pw.encode('utf-8'), expected_hash.encode('utf-8'))
return False
USERS = {'editor': hash_password('editor'),
'viewer': hash_password('viewer')}
GROUPS = {'editor':['group:editors']}
def groupfinder(userid, request):
if userid in USERS:
return GROUPS.get(userid, [])
|
`` groupfinder``関数はユーザIDとリクエストを受け取り、次のいずれかの値を返します:
- `` userid``がシステムに存在する場合、グループ識別子のシーケンスを返します(ユーザーがグループのメンバーでない場合は空のシーケンス)。
- userid *がシステムに存在しない場合、 `` None``が返されます。
たとえば、 `` groupfinder( 'viewer'、request) ``は、[[group:editor '] ``、 `` groupfinder(' viewer '、request) ``を返します。 groupfinder( 'admin'、request) は None``を返します。 `term: principal`またはユーザのプリンシパルを提供する:term: 認証ポリシー "コールバック"として `` groupfinder() ``を使用します。
後でユーザーを認証するのに役立つ2つのヘルパーメソッドがあります。最初は `` hash_password``です。これは生のパスワードを受け取り、bcryptを使ってそれを非可逆的な表現に変換します。プロセスは"ハッシング"と呼ばれます。 2番目のメソッド `` check_password``では、送信されたパスワードのハッシュ値と、ユーザのレコードに保存されているパスワードのハッシュ値を比較することができます。 2つのハッシュ値が一致すると、送信されたパスワードが有効であり、ユーザーを認証できます。
私たちはパスワードをハッシュするので、アプリケーションで復号化してそれを使って認証することは不可能です。パスワードを平文で愚かに保存した場合、データベースにアクセスできる人は誰でもパスワードを取得して認証することができます。
本番システムでは、ユーザーとグループのデータはほとんどの場合、保存されデータベースから取得されますが、ここでは「ダミー」データを使用してユーザーとグループのソースを表します。
ACLを追加する¶
`` tutorial / models.py``を開き、上に次のimport文を追加してください:
4 5 6 7 8 | from pyramid.security import (
Allow,
Everyone,
)
|
`` Wiki``クラスに次の行を追加してください:
9 10 11 12 13 | class Wiki(PersistentMapping):
__name__ = None
__parent__ = None
__acl__ = [ (Allow, Everyone, 'view'),
(Allow, 'group:editors', 'edit') ]
|
データ: 〜pyramid.security.Allow、パーミッションが許可されていることを意味するアクション、:data:〜pyramid.security.Everyone、すべてのリクエストに関連するspecial:term: principal 。どちらも、ACLを構成する:term: `ACE`エントリで使用されます。
ACLは `` __acl__``という名前でクラスの属性にする必要があるリストです。 term: ACL`は2つのterm: ACE`エントリで定義されます。最初のエントリはすべてのユーザに `` view``パーミッションを許可します。 2番目のエントリは、 `` group:editors``プリンシパルの `` edit``パーミッションを許可します。
ACLを含む `` Wiki``クラスは:term: root`リソースのための:term: resource`コンストラクタです。これは `` Wiki``インスタンスです。 ACLは `` context``属性として要求の:term: `context`の各ビューに提供されます。
このACLをクラスのスコープに割り当てるのは偶然のことです。 ACLはオブジェクト*インスタンス*にもアタッチすることができます。これはapp: Pyramid`アプリケーションで"行レベルのセキュリティ"を実現する方法です。ただし、セキュリティ要件は単純なので、実際にはシステム全体で* 1つの* ACLしか必要としないため、この機能は示されていません。 :term: `ACL`が表すものの詳細については:ref: assigning_acls`を参照してください。
認証および認可ポリシーの追加¶
`` tutorial / __ init __。py``を開き、強調表示されたimport文を追加してください:
1 2 3 4 5 6 7 8 | from pyramid.config import Configurator
from pyramid_zodbconn import get_connection
from pyramid.authentication import AuthTktAuthenticationPolicy
from pyramid.authorization import ACLAuthorizationPolicy
from .models import appmaker
from .security import groupfinder
|
これらのポリシーを設定に追加します。
18 19 20 21 22 23 24 25 | settings['tm.manager_hook'] = 'pyramid_tm.explicit_manager'
authn_policy = AuthTktAuthenticationPolicy(
'sosecret', callback=groupfinder, hashalg='sha512')
authz_policy = ACLAuthorizationPolicy()
with Configurator(settings=settings) as config:
config.set_authentication_policy(authn_policy)
config.set_authorization_policy(authz_policy)
config.include('pyramid_chameleon')
|
強調表示された行だけを追加する必要があります。
リクエストに含まれるauthチケットに基づいた `` AuthTktAuthenticationPolicy``を有効にしています。 ACLを使用してビューの* allow または deny *結果を判断するACLAuthorizationPolicyも有効にしています。
:class: pyramid.authentication.AuthTktAuthenticationPolicy`コンストラクタは secret``と callback``の二つの引数を受け付けます。 ` secret``は、このポリシーが表す"認証チケット"のマシンで使用される暗号鍵を表す文字列です。必須です。 `` callback``は以前作成した `` groupfinder() ``関数です。
権限宣言を追加する¶
`` add_page() ``と `` edit_page() ``の `` @ view_config``デコレータに `` tutorial / views.py``を開き、 `` permission = 'edit'``パラメータを追加してください:
@view_config(name='add_page', context='.models.Wiki',
renderer='templates/edit.pt',
permission='edit')
@view_config(name='edit_page', context='.models.Page',
renderer='templates/edit.pt',
permission='edit')
強調表示されている行とその前のコンマのみを編集して追加する必要があります。
その結果、リクエスト時に `` edit``パーミッションを持っているユーザだけが、その2つのビューを呼び出すことができます。
`` view_wiki() ``と `` view_page() ``の `` @ view_config``デコレータに次のように `` permission = 'view'``パラメータを追加してください:
@view_config(context='.models.Wiki',
permission='view')
@view_config(context='.models.Page', renderer='templates/view.pt',
permission='view')
強調表示されている行とその前のコンマのみを編集して追加する必要があります。
これにより、誰もがこの2つのビューを呼び出すことができます。
私たちは、アクセスを制御するために必要な変更を完了しました。これに続く変更により、ログインとログアウト機能が追加されます。
ログイン、ログアウト¶
ログインビューとログアウトビューを追加する¶
ログインフォームをレンダリングし、ログインフォームからの投稿を処理してクレデンシャルをチェックする `` login``ビューを追加します。
また、アプリケーションに呼び出し可能な `` logout``ビューを追加し、そこへのリンクを提供します。このビューでは、ログインしたユーザーの資格情報がクリアされ、フロントページにリダイレクトされます。
`` tutorial / views.py`の先頭に以下のimport文を追加してください:
from pyramid.view import (
view_config,
forbidden_view_config,
)
from pyramid.security import (
remember,
forget,
)
from .security import USERS, check_password
強調表示された行をすべて追加または編集する必要があります。
:meth: 〜pyramid.view.forbidden_view_config`はデフォルトの403 Forbiddenページをカスタマイズするために使用されます。 :meth: `〜pyramid.security.remember`と:meth:〜pyramid.security.forget`は、認証チケットクッキーの作成と期限切れに役立ちます。
次に、ファイルの最後に `` login``と `` logout``ビューを追加します:
80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 | @view_config(context='.models.Wiki', name='login',
renderer='templates/login.pt')
@forbidden_view_config(renderer='templates/login.pt')
def login(request):
login_url = request.resource_url(request.context, 'login')
referrer = request.url
if referrer == login_url:
referrer = '/' # never use the login form itself as came_from
came_from = request.params.get('came_from', referrer)
message = ''
login = ''
password = ''
if 'form.submitted' in request.params:
login = request.params['login']
password = request.params['password']
if check_password(USERS.get(login), password):
headers = remember(request, login)
return HTTPFound(location=came_from,
headers=headers)
message = 'Failed login'
return dict(
message=message,
url=request.application_url + '/login',
came_from=came_from,
login=login,
password=password,
)
@view_config(context='.models.Wiki', name='logout')
def logout(request):
headers = forget(request)
return HTTPFound(location=request.resource_url(request.context),
headers=headers)
|
`` login() ``には2つのデコレータがあります:
- `` @ view_config``デコレータはそれを `` login``ルートに関連付け、 `` / login``を訪れた時にそれを見えるようにします。
- @ forbidden_view_config``デコレータは:term: forbidden view`に変換します。 `` login() ``は、ユーザが権限を持たないビュー呼び出し可能呼び出しを実行しようとしたときに呼び出されます。たとえば、ユーザーがログインせずにWikiページを追加または編集しようとすると、続行を許可される前にログインフォームが表示されます。
これらの2つの用語:用語: `view configuration`デコレータの順序は重要ではありません。
`` logout() ``は `` @ view_config``デコレータで装飾されています。これは `` logout``ルートに関連しています。 `` / logout``を訪れたときに呼び出されます。
`` login.pt``テンプレートを追加してください¶
次の内容で `` tutorial / templates / login.pt``を作成してください:
<!DOCTYPE html>
<html lang="${request.locale_name}">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="pyramid web application">
<meta name="author" content="Pylons Project">
<link rel="shortcut icon" href="${request.static_url('tutorial:static/pyramid-16x16.png')}">
<title>Login - Pyramid tutorial wiki (based on
TurboGears 20-Minute Wiki)</title>
<!-- Bootstrap core CSS -->
<link href="//oss.maxcdn.com/libs/twitter-bootstrap/3.0.3/css/bootstrap.min.css" rel="stylesheet">
<!-- Custom styles for this scaffold -->
<link href="${request.static_url('tutorial:static/theme.css')}" rel="stylesheet">
<!-- HTML5 shim and Respond.js IE8 support of HTML5 elements and media queries -->
<!--[if lt IE 9]>
<script src="//oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js" integrity="sha384-0s5Pv64cNZJieYFkXYOTId2HMA2Lfb6q2nAcx2n0RTLUnCAoTTsS0nKEO27XyKcY" crossorigin="anonymous"></script>
<script src="//oss.maxcdn.com/libs/respond.js/1.3.0/respond.min.js" integrity="sha384-f1r2UzjsxZ9T4V1f2zBO/evUqSEOpeaUUZcMTz1Up63bl4ruYnFYeM+BxI4NhyI0" crossorigin="anonymous"></script>
<![endif]-->
</head>
<body>
<div class="starter-template">
<div class="container">
<div class="row">
<div class="col-md-2">
<img class="logo img-responsive" src="${request.static_url('tutorial:static/pyramid.png')}" alt="pyramid web framework">
</div>
<div class="col-md-10">
<div class="content">
<p>
<strong>
Login
</strong><br>
<span tal:replace="message"></span>
</p>
<form action="${url}" method="post">
<input type="hidden" name="came_from" value="${came_from}">
<div class="form-group">
<label for="login">Username</label>
<input type="text" name="login" value="${login}">
</div>
<div class="form-group">
<label for="password">Password</label>
<input type="password" name="password" value="${password}">
</div>
<div class="form-group">
<button type="submit" name="form.submitted" value="Log In" class="btn btn-default">Log In</button>
</div>
</form>
</div>
</div>
</div>
<div class="row">
<div class="copyright">
Copyright © Pylons Project
</div>
</div>
</div>
</div>
<!-- Bootstrap core JavaScript
================================================== -->
<!-- Placed at the end of the document so the pages load faster -->
<script src="//oss.maxcdn.com/libs/jquery/1.10.2/jquery.min.js" integrity="sha384-aBL3Lzi6c9LNDGvpHkZrrm3ZVsIwohDD7CDozL0pk8FwCrfmV7H9w8j3L7ikEv6h" crossorigin="anonymous"></script>
<script src="//oss.maxcdn.com/libs/twitter-bootstrap/3.0.3/js/bootstrap.min.js" integrity="sha384-s1ITto93iSMDxlp/79qhWHi+LsIi9Gx6yL+cOKDuymvihkfol83TYbLbOw+W/wv4" crossorigin="anonymous"></script>
</body>
</html>
上記のテンプレートは、 `` views.py``で追加したログインビューで参照されます。
`` logged_in``フラグをレンダラーに返します。¶
再び `` tutorial / views.py``を開きます。次のように `` logged_in``パラメータを `` view_page() ``、 `` add_page() ``、 `` edit_page() ``の戻り値に追加してください:
return dict(page=context, content=content, edit_url=edit_url,
logged_in=request.authenticated_userid)
return dict(page=page, save_url=save_url,
logged_in=request.authenticated_userid)
return dict(page=context,
save_url=request.resource_url(context, 'edit_page'),
logged_in=request.authenticated_userid)
強調表示された行だけを追加または編集する必要があります。
:meth: pyramid.request.Request.authenticated_userid`は、ユーザが認証されていなければ None`、ユーザが認証されている場合はユーザIDになります。
ログイン時に"ログアウト"リンクを追加¶
`` tutorial / templates / edit.pt``と `` tutorial / templates / view.pt``を開き、強調表示された行に示すように次のコードを追加してください。
<div class="col-md-10">
<div class="content">
<p tal:condition="logged_in" class="pull-right">
<a href="${request.application_url}/logout">Logout</a>
</p>
属性 `` tal:condition = "logged_in " ``は、 `` logged_in``が任意のユーザIDであるときに要素を含めるようにします。リンクがログアウトビューを呼び出します。ユーザが認証されていないなど、 `` logged_in``が `` None``の場合、上記の要素はインクルードされません。
変更のレビュー¶
私たちの「チュートリアル/ __ init __。py」は完了したら次のようになります:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | from pyramid.config import Configurator
from pyramid_zodbconn import get_connection
from pyramid.authentication import AuthTktAuthenticationPolicy
from pyramid.authorization import ACLAuthorizationPolicy
from .models import appmaker
from .security import groupfinder
def root_factory(request):
conn = get_connection(request)
return appmaker(conn.root())
def main(global_config, **settings):
""" This function returns a Pyramid WSGI application.
"""
settings['tm.manager_hook'] = 'pyramid_tm.explicit_manager'
authn_policy = AuthTktAuthenticationPolicy(
'sosecret', callback=groupfinder, hashalg='sha512')
authz_policy = ACLAuthorizationPolicy()
with Configurator(settings=settings) as config:
config.set_authentication_policy(authn_policy)
config.set_authorization_policy(authz_policy)
config.include('pyramid_chameleon')
config.include('pyramid_tm')
config.include('pyramid_retry')
config.include('pyramid_zodbconn')
config.set_root_factory(root_factory)
config.add_static_view('static', 'static', cache_max_age=3600)
config.scan()
return config.make_wsgi_app()
|
強調表示された行だけを追加または編集する必要があります。
私たちの "チュートリアル/ models.py"は完了したら次のようになります:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | from persistent import Persistent
from persistent.mapping import PersistentMapping
from pyramid.security import (
Allow,
Everyone,
)
class Wiki(PersistentMapping):
__name__ = None
__parent__ = None
__acl__ = [ (Allow, Everyone, 'view'),
(Allow, 'group:editors', 'edit') ]
class Page(Persistent):
def __init__(self, data):
self.data = data
def appmaker(zodb_root):
if 'app_root' not in zodb_root:
app_root = Wiki()
frontpage = Page('This is the front page')
app_root['FrontPage'] = frontpage
frontpage.__name__ = 'FrontPage'
frontpage.__parent__ = app_root
zodb_root['app_root'] = app_root
return zodb_root['app_root']
|
強調表示された行だけを追加または編集する必要があります。
私たちの「チュートリアル/ views.py」は完了したら次のようになります:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 | from docutils.core import publish_parts
import re
from pyramid.httpexceptions import HTTPFound
from pyramid.view import (
view_config,
forbidden_view_config,
)
from pyramid.security import (
remember,
forget,
)
from .security import USERS, check_password
from .models import Page
# regular expression used to find WikiWords
wikiwords = re.compile(r"\b([A-Z]\w+[A-Z]+\w+)")
@view_config(context='.models.Wiki',
permission='view')
def view_wiki(context, request):
return HTTPFound(location=request.resource_url(context, 'FrontPage'))
@view_config(context='.models.Page', renderer='templates/view.pt',
permission='view')
def view_page(context, request):
wiki = context.__parent__
def check(match):
word = match.group(1)
if word in wiki:
page = wiki[word]
view_url = request.resource_url(page)
return '<a href="%s">%s</a>' % (view_url, word)
else:
add_url = request.application_url + '/add_page/' + word
return '<a href="%s">%s</a>' % (add_url, word)
content = publish_parts(context.data, writer_name='html')['html_body']
content = wikiwords.sub(check, content)
edit_url = request.resource_url(context, 'edit_page')
return dict(page=context, content=content, edit_url=edit_url,
logged_in=request.authenticated_userid)
@view_config(name='add_page', context='.models.Wiki',
renderer='templates/edit.pt',
permission='edit')
def add_page(context, request):
pagename = request.subpath[0]
if 'form.submitted' in request.params:
body = request.params['body']
page = Page(body)
page.__name__ = pagename
page.__parent__ = context
context[pagename] = page
return HTTPFound(location=request.resource_url(page))
save_url = request.resource_url(context, 'add_page', pagename)
page = Page('')
page.__name__ = pagename
page.__parent__ = context
return dict(page=page, save_url=save_url,
logged_in=request.authenticated_userid)
@view_config(name='edit_page', context='.models.Page',
renderer='templates/edit.pt',
permission='edit')
def edit_page(context, request):
if 'form.submitted' in request.params:
context.data = request.params['body']
return HTTPFound(location=request.resource_url(context))
return dict(page=context,
save_url=request.resource_url(context, 'edit_page'),
logged_in=request.authenticated_userid)
@view_config(context='.models.Wiki', name='login',
renderer='templates/login.pt')
@forbidden_view_config(renderer='templates/login.pt')
def login(request):
login_url = request.resource_url(request.context, 'login')
referrer = request.url
if referrer == login_url:
referrer = '/' # never use the login form itself as came_from
came_from = request.params.get('came_from', referrer)
message = ''
login = ''
password = ''
if 'form.submitted' in request.params:
login = request.params['login']
password = request.params['password']
if check_password(USERS.get(login), password):
headers = remember(request, login)
return HTTPFound(location=came_from,
headers=headers)
message = 'Failed login'
return dict(
message=message,
url=request.application_url + '/login',
came_from=came_from,
login=login,
password=password,
)
@view_config(context='.models.Wiki', name='logout')
def logout(request):
headers = forget(request)
return HTTPFound(location=request.resource_url(request.context),
headers=headers)
|
強調表示された行だけを追加または編集する必要があります。
終了したら、私たちの `` tutorial / templates / edit.pt``テンプレートは次のようになります:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 | <!DOCTYPE html>
<html lang="${request.locale_name}">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="pyramid web application">
<meta name="author" content="Pylons Project">
<link rel="shortcut icon" href="${request.static_url('tutorial:static/pyramid-16x16.png')}">
<title>${page.__name__} - Pyramid tutorial wiki (based on
TurboGears 20-Minute Wiki)</title>
<!-- Bootstrap core CSS -->
<link href="//oss.maxcdn.com/libs/twitter-bootstrap/3.0.3/css/bootstrap.min.css" rel="stylesheet">
<!-- Custom styles for this scaffold -->
<link href="${request.static_url('tutorial:static/theme.css')}" rel="stylesheet">
<!-- HTML5 shim and Respond.js IE8 support of HTML5 elements and media queries -->
<!--[if lt IE 9]>
<script src="//oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js" integrity="sha384-0s5Pv64cNZJieYFkXYOTId2HMA2Lfb6q2nAcx2n0RTLUnCAoTTsS0nKEO27XyKcY" crossorigin="anonymous"></script>
<script src="//oss.maxcdn.com/libs/respond.js/1.3.0/respond.min.js" integrity="sha384-f1r2UzjsxZ9T4V1f2zBO/evUqSEOpeaUUZcMTz1Up63bl4ruYnFYeM+BxI4NhyI0" crossorigin="anonymous"></script>
<![endif]-->
</head>
<body>
<div class="starter-template">
<div class="container">
<div class="row">
<div class="col-md-2">
<img class="logo img-responsive" src="${request.static_url('tutorial:static/pyramid.png')}" alt="pyramid web framework">
</div>
<div class="col-md-10">
<div class="content">
<p tal:condition="logged_in" class="pull-right">
<a href="${request.application_url}/logout">Logout</a>
</p>
<p>
Editing <strong><span tal:replace="page.__name__">
Page Name Goes Here</span></strong>
</p>
<p>You can return to the
<a href="${request.application_url}">FrontPage</a>.
</p>
<form action="${save_url}" method="post">
<div class="form-group">
<textarea class="form-control" name="body" tal:content="page.data" rows="10" cols="60"></textarea>
</div>
<div class="form-group">
<button type="submit" name="form.submitted" value="Save" class="btn btn-default">Save</button>
</div>
</form>
</div>
</div>
</div>
<div class="row">
<div class="copyright">
Copyright © Pylons Project
</div>
</div>
</div>
</div>
<!-- Bootstrap core JavaScript
================================================== -->
<!-- Placed at the end of the document so the pages load faster -->
<script src="//oss.maxcdn.com/libs/jquery/1.10.2/jquery.min.js" integrity="sha384-aBL3Lzi6c9LNDGvpHkZrrm3ZVsIwohDD7CDozL0pk8FwCrfmV7H9w8j3L7ikEv6h" crossorigin="anonymous"></script>
<script src="//oss.maxcdn.com/libs/twitter-bootstrap/3.0.3/js/bootstrap.min.js" integrity="sha384-s1ITto93iSMDxlp/79qhWHi+LsIi9Gx6yL+cOKDuymvihkfol83TYbLbOw+W/wv4" crossorigin="anonymous"></script>
</body>
</html>
|
強調表示された行だけを追加または編集する必要があります。
終了したら、私たちの `` tutorial / templates / view.pt``テンプレートは次のようになります:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 | <!DOCTYPE html>
<html lang="${request.locale_name}">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="pyramid web application">
<meta name="author" content="Pylons Project">
<link rel="shortcut icon" href="${request.static_url('tutorial:static/pyramid-16x16.png')}">
<title>${page.__name__} - Pyramid tutorial wiki (based on
TurboGears 20-Minute Wiki)</title>
<!-- Bootstrap core CSS -->
<link href="//oss.maxcdn.com/libs/twitter-bootstrap/3.0.3/css/bootstrap.min.css" rel="stylesheet">
<!-- Custom styles for this scaffold -->
<link href="${request.static_url('tutorial:static/theme.css')}" rel="stylesheet">
<!-- HTML5 shim and Respond.js IE8 support of HTML5 elements and media queries -->
<!--[if lt IE 9]>
<script src="//oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js" integrity="sha384-0s5Pv64cNZJieYFkXYOTId2HMA2Lfb6q2nAcx2n0RTLUnCAoTTsS0nKEO27XyKcY" crossorigin="anonymous"></script>
<script src="//oss.maxcdn.com/libs/respond.js/1.3.0/respond.min.js" integrity="sha384-f1r2UzjsxZ9T4V1f2zBO/evUqSEOpeaUUZcMTz1Up63bl4ruYnFYeM+BxI4NhyI0" crossorigin="anonymous"></script>
<![endif]-->
</head>
<body>
<div class="starter-template">
<div class="container">
<div class="row">
<div class="col-md-2">
<img class="logo img-responsive" src="${request.static_url('tutorial:static/pyramid.png')}" alt="pyramid web framework">
</div>
<div class="col-md-10">
<div class="content">
<p tal:condition="logged_in" class="pull-right">
<a href="${request.application_url}/logout">Logout</a>
</p>
<div tal:replace="structure content">
Page text goes here.
</div>
<p>
<a tal:attributes="href edit_url" href="">
Edit this page
</a>
</p>
<p>
Viewing <strong><span tal:replace="page.__name__">
Page Name Goes Here</span></strong>
</p>
<p>You can return to the
<a href="${request.application_url}">FrontPage</a>.
</p>
</div>
</div>
</div>
<div class="row">
<div class="copyright">
Copyright © Pylons Project
</div>
</div>
</div>
</div>
<!-- Bootstrap core JavaScript
================================================== -->
<!-- Placed at the end of the document so the pages load faster -->
<script src="//oss.maxcdn.com/libs/jquery/1.10.2/jquery.min.js" integrity="sha384-aBL3Lzi6c9LNDGvpHkZrrm3ZVsIwohDD7CDozL0pk8FwCrfmV7H9w8j3L7ikEv6h" crossorigin="anonymous"></script>
<script src="//oss.maxcdn.com/libs/twitter-bootstrap/3.0.3/js/bootstrap.min.js" integrity="sha384-s1ITto93iSMDxlp/79qhWHi+LsIi9Gx6yL+cOKDuymvihkfol83TYbLbOw+W/wv4" crossorigin="anonymous"></script>
</body>
</html>
|
強調表示された行だけを追加または編集する必要があります。
ブラウザでのアプリケーションの表示¶
最後に、ブラウザでアプリケーションを調べることができます(参照:ref: wiki-start-the-application)。ブラウザを起動し、次の各URLにアクセスして、結果が期待どおりであることを確認します。
- http:// localhost:6543 / `` view_wiki``ビューを呼び出します。これは常に `` FrontPage``ページリソースの `` view_page``ビューにリダイレクトされます。任意のユーザーが実行可能です。
- http:// localhost:6543 / FrontPageは、 `` FrontPage``ページリソースの `` view_page``ビューを呼び出します。これは、 `` Page``リソースのための:term: default view`( `name`"のないビュー)です。任意のユーザーが実行可能です。
- http:// localhost:6543 / FrontPage / edit_pageは、FrontPageオブジェクトの編集ビューを呼び出します。 `` editor``ユーザだけが実行可能です。別のユーザー(または匿名ユーザー)がそれを呼び出すと、ログインフォームが表示されます。ユーザー名 `` editor``で資格情報を入力すると、パスワード `` editor``が編集ページのフォームを表示します。
- http:// localhost:6543 / add_page / SomePageNameはページの追加ビューを呼び出します。 `` editor``ユーザだけが実行可能です。別のユーザー(または匿名ユーザー)がそれを呼び出すと、ログインフォームが表示されます。ユーザー名 `` editor``で資格情報を入力すると、パスワード `` editor``が編集ページのフォームを表示します。
- ログイン後(編集やページの追加や `` editor``の資格情報を使ったログインフォームの送信の結果として)、右上にログアウトリンクが表示されます。これをクリックすると、ログアウトされ、フロントページにリダイレクトされます。