Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Z
Zope
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Kirill Smelkov
Zope
Commits
c2f76353
Commit
c2f76353
authored
May 10, 2000
by
Amos Latteier
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Added ZopeTutorial to CVS.
parent
47c1bd01
Changes
9
Hide whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
1596 additions
and
0 deletions
+1596
-0
lib/python/Products/ZopeTutorial/CHANGES.txt
lib/python/Products/ZopeTutorial/CHANGES.txt
+97
-0
lib/python/Products/ZopeTutorial/TutorialTopic.py
lib/python/Products/ZopeTutorial/TutorialTopic.py
+245
-0
lib/python/Products/ZopeTutorial/__init__.py
lib/python/Products/ZopeTutorial/__init__.py
+178
-0
lib/python/Products/ZopeTutorial/glossary.stx
lib/python/Products/ZopeTutorial/glossary.stx
+142
-0
lib/python/Products/ZopeTutorial/lessonView.dtml
lib/python/Products/ZopeTutorial/lessonView.dtml
+40
-0
lib/python/Products/ZopeTutorial/tutorial.stx
lib/python/Products/ZopeTutorial/tutorial.stx
+825
-0
lib/python/Products/ZopeTutorial/tutorialAdd.dtml
lib/python/Products/ZopeTutorial/tutorialAdd.dtml
+43
-0
lib/python/Products/ZopeTutorial/tutorialNav.dtml
lib/python/Products/ZopeTutorial/tutorialNav.dtml
+25
-0
lib/python/Products/ZopeTutorial/version.txt
lib/python/Products/ZopeTutorial/version.txt
+1
-0
No files found.
lib/python/Products/ZopeTutorial/CHANGES.txt
0 → 100644
View file @
c2f76353
Zope Tutorial 1.0a6
Bug Fixes
* Rearranged things a bit for 2.2 release.
* Improved missing cookie behavior.
* Improved descriptions of management screen navigation.
* Many typos and nits fixed in lessons and examples.
Known Limitations
* Requires Zope 2.1.6 or higher.
* Works better with HTTP cookies.
Zope Tutorial 1.0a5
Features
* Changed the way examples are installed. Now the examples are
installed by adding a Zope object. This resolves a lot of security
issues.
* Added some preliminary API docs, and an API documentation
infrastructure.
Bug Fixes
* Miscellaneous restructuring and fixing.
Known Limitations
* Requires Zope 2.1.6 or higher.
* Requires HTTP cookies.
Zope Tutorial 1.0a4
Features
* Added an introduction.
* Improved installation location and verification.
Bug Fixes
* More information about how to navigate between management
screens.
* Many typos fixed thanks to Karl Ulbrich.
Known Bugs
* Requires Zope 2.1.6 or higher.
Zope Tutorial 1.0a3
Features
* Glossary greatly improved and linked into lessons.
Bug Fixes
* Install now correctly sets up gadfly database.
* Some DTML typos fixed thanks to Didier Georgieff.
Known Bugs
* Requires Zope 2.1.6 or higher.
Zope Tutorial 1.0a2
Features
* Improved PRE formatting. Examples now look nice.
* Improved examples navigation, no longer requires Javascript.
* Added more information to the glossary.
* Added feedback link.
Bug Fixes
* Grammar and typo fixes.
Known Bugs
* Requires Zope 2.1.6 or higher.
Zope Tutorial 1.0a1
Initial preview release.
\ No newline at end of file
lib/python/Products/ZopeTutorial/TutorialTopic.py
0 → 100644
View file @
c2f76353
##############################################################################
#
# Zope Public License (ZPL) Version 1.0
# -------------------------------------
#
# Copyright (c) Digital Creations. All rights reserved.
#
# This license has been certified as Open Source(tm).
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# 1. Redistributions in source code must retain the above copyright
# notice, this list of conditions, and the following disclaimer.
#
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions, and the following disclaimer in
# the documentation and/or other materials provided with the
# distribution.
#
# 3. Digital Creations requests that attribution be given to Zope
# in any manner possible. Zope includes a "Powered by Zope"
# button that is installed by default. While it is not a license
# violation to remove this button, it is requested that the
# attribution remain. A significant investment has been put
# into Zope, and this effort will continue if the Zope community
# continues to grow. This is one way to assure that growth.
#
# 4. All advertising materials and documentation mentioning
# features derived from or use of this software must display
# the following acknowledgement:
#
# "This product includes software developed by Digital Creations
# for use in the Z Object Publishing Environment
# (http://www.zope.org/)."
#
# In the event that the product being advertised includes an
# intact Zope distribution (with copyright and license included)
# then this clause is waived.
#
# 5. Names associated with Zope or Digital Creations must not be used to
# endorse or promote products derived from this software without
# prior written permission from Digital Creations.
#
# 6. Modified redistributions of any form whatsoever must retain
# the following acknowledgment:
#
# "This product includes software developed by Digital Creations
# for use in the Z Object Publishing Environment
# (http://www.zope.org/)."
#
# Intact (re-)distributions of any official Zope release do not
# require an external acknowledgement.
#
# 7. Modifications are encouraged but must be packaged separately as
# patches to official Zope releases. Distributions that do not
# clearly separate the patches from the original work must be clearly
# labeled as unofficial distributions. Modifications which do not
# carry the name Zope may be packaged in any form, as long as they
# conform to all of the clauses above.
#
#
# Disclaimer
#
# THIS SOFTWARE IS PROVIDED BY DIGITAL CREATIONS ``AS IS'' AND ANY
# EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL DIGITAL CREATIONS OR ITS
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
# SUCH DAMAGE.
#
#
# This software consists of contributions made by Digital Creations and
# many individuals on behalf of Digital Creations. Specific
# attributions are listed in the accompanying credits file.
#
##############################################################################
import
OFS.Folder
from
HelpSys.HelpTopic
import
TextTopic
from
Globals
import
HTML
,
HTMLFile
,
MessageDialog
import
DateTime
import
DocumentTemplate
import
StructuredText
import
string
,
re
pre_pat
=
re
.
compile
(
r'<PRE>(.+?)</PRE>'
,
re
.
DOTALL
)
tutorialExamplesFile
=
'ZopeTutorialExamples.zexp'
class
TutorialTopic
(
TextTopic
):
"""
A Tutorial Help Topic
"""
def
__init__
(
self
,
id
,
title
,
text
):
self
.
id
=
id
self
.
title
=
title
text
=
str
(
StructuredText
.
HTML
(
text
))
self
.
obj
=
HTML
(
pre_pat
.
sub
(
clean_pre
,
text
))
index_html
=
HTMLFile
(
'lessonView'
,
globals
())
def
lessonURL
(
self
,
id
,
REQUEST
):
"""
URL of the examples for a lesson
"""
try
:
return
'%s/lesson%d'
%
(
REQUEST
[
'tutorialExamplesURL'
],
id
)
except
KeyError
:
return
""
def
tutorialShowLesson
(
self
,
id
,
REQUEST
):
"""
Navigate management frame to a given lesson's screen.
"""
url
=
self
.
lessonURL
(
id
,
REQUEST
)
if
not
url
:
return
"""
\
<p class="warning">
Zope cannot find the tutorial examples.
You should install the tutorial examples before
continuing. Choose "Zope Tutorial" from the product
add list in the Zope management screen to install
the examples.
</p>
<p class="warning">
If you have already installed the tutorial, you can either
follow along manually, or reinstall the tutorial examples.
Note: make sure that you have cookies turned on in your browser.
</p>
"""
return
"""
\
<SCRIPT LANGUAGE="javascript">
<!--
window.open("%s/manage_main", "manage_main");
//-->
</SCRIPT>
<p class="information">
<a href="%s/manage_main" target="manage_main"
onClick="javascript:window.open("%s/manage_main", "manage_main").focus()"
>Show lesson examples</a> in another window.
</p>"""
%
(
url
,
url
,
url
)
def
apiLink
(
self
,
klass
,
REQUEST
):
"""
Returns the URL to a API documentation for a given class.
"""
names
=
string
.
split
(
klass
,
'.'
)
url
=
"%s/Control_Panel/Products/%s/Help/%s.py#%s"
%
(
REQUEST
[
'SCRIPT_NAME'
],
names
[
0
],
names
[
1
],
names
[
2
])
return
'<a href="%s">API Documentation</a>'
%
url
tutorialNavigation
=
HTMLFile
(
'tutorialNav'
,
globals
())
addTutorialForm
=
HTMLFile
(
'tutorialAdd'
,
globals
())
def
addTutorial
(
self
,
id
,
REQUEST
=
None
,
RESPONSE
=
None
):
"""
Install tutorial examples.
"""
ob
=
OFS
.
Folder
.
Folder
()
ob
.
id
=
id
ob
.
title
=
'Zope Tutorial Examples'
id
=
self
.
_setObject
(
id
,
ob
)
folder
=
getattr
(
self
,
id
)
# work around old Zope bug in importing
try
:
folder
.
manage_importObject
(
tutorialExamplesFile
)
except
:
folder
.
_p_jar
=
self
.
Destination
().
_p_jar
folder
.
manage_importObject
(
tutorialExamplesFile
)
# acquire REQUEST if necessary
if
REQUEST
is
None
:
REQUEST
=
self
.
REQUEST
# Set local roles on examples
changeOwner
(
folder
,
REQUEST
[
'AUTHENTICATED_USER'
])
# Run lesson setup methods -- call Setup.setup methods in lesson folders
examples
=
folder
.
examples
for
lesson
in
examples
.
objectValues
():
if
hasattr
(
lesson
,
'Setup'
):
lesson
.
Setup
.
setup
(
lesson
.
Setup
,
REQUEST
)
if
RESPONSE
is
not
None
:
e
=
(
DateTime
.
DateTime
(
'GMT'
)
+
365
).
rfc822
()
RESPONSE
.
setCookie
(
'tutorialExamplesURL'
,
folder
.
absolute_url
()
+
'/examples'
,
path
=
'/'
,
expires
=
e
)
RESPONSE
.
redirect
(
folder
.
absolute_url
()
+
'/examples'
)
def
changeOwner
(
obj
,
owner
):
"""
Recursively changes the Owner of an object and all its subobjects.
"""
for
user
,
roles
in
obj
.
get_local_roles
():
if
'Owner'
in
roles
:
obj
.
manage_delLocalRoles
([
user
])
break
obj
.
manage_setLocalRoles
(
owner
.
getUserName
(),
[
'Owner'
])
for
subobj
in
obj
.
objectValues
():
changeOwner
(
subobj
,
owner
)
def
clean_pre
(
match
):
"""
Reformat a pre tag to get rid of extra indentation
and extra blank lines.
"""
lines
=
string
.
split
(
match
.
group
(
1
),
'
\
n
'
)
nlines
=
[]
min_indent
=
None
for
line
in
lines
:
indent
=
len
(
line
)
-
len
(
string
.
lstrip
(
line
))
if
min_indent
is
None
or
indent
<
min_indent
:
if
string
.
strip
(
line
):
min_indent
=
indent
for
line
in
lines
:
nlines
.
append
(
line
[
min_indent
:])
while
1
:
if
not
string
.
strip
(
nlines
[
0
]):
nlines
.
pop
(
0
)
else
:
break
while
1
:
if
not
string
.
strip
(
nlines
[
-
1
]):
nlines
.
pop
()
else
:
break
return
"<PRE>%s</PRE>"
%
string
.
join
(
nlines
,
'
\
n
'
)
lib/python/Products/ZopeTutorial/__init__.py
0 → 100644
View file @
c2f76353
##############################################################################
#
# Zope Public License (ZPL) Version 1.0
# -------------------------------------
#
# Copyright (c) Digital Creations. All rights reserved.
#
# This license has been certified as Open Source(tm).
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# 1. Redistributions in source code must retain the above copyright
# notice, this list of conditions, and the following disclaimer.
#
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions, and the following disclaimer in
# the documentation and/or other materials provided with the
# distribution.
#
# 3. Digital Creations requests that attribution be given to Zope
# in any manner possible. Zope includes a "Powered by Zope"
# button that is installed by default. While it is not a license
# violation to remove this button, it is requested that the
# attribution remain. A significant investment has been put
# into Zope, and this effort will continue if the Zope community
# continues to grow. This is one way to assure that growth.
#
# 4. All advertising materials and documentation mentioning
# features derived from or use of this software must display
# the following acknowledgement:
#
# "This product includes software developed by Digital Creations
# for use in the Z Object Publishing Environment
# (http://www.zope.org/)."
#
# In the event that the product being advertised includes an
# intact Zope distribution (with copyright and license included)
# then this clause is waived.
#
# 5. Names associated with Zope or Digital Creations must not be used to
# endorse or promote products derived from this software without
# prior written permission from Digital Creations.
#
# 6. Modified redistributions of any form whatsoever must retain
# the following acknowledgment:
#
# "This product includes software developed by Digital Creations
# for use in the Z Object Publishing Environment
# (http://www.zope.org/)."
#
# Intact (re-)distributions of any official Zope release do not
# require an external acknowledgement.
#
# 7. Modifications are encouraged but must be packaged separately as
# patches to official Zope releases. Distributions that do not
# clearly separate the patches from the original work must be clearly
# labeled as unofficial distributions. Modifications which do not
# carry the name Zope may be packaged in any form, as long as they
# conform to all of the clauses above.
#
#
# Disclaimer
#
# THIS SOFTWARE IS PROVIDED BY DIGITAL CREATIONS ``AS IS'' AND ANY
# EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL DIGITAL CREATIONS OR ITS
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
# SUCH DAMAGE.
#
#
# This software consists of contributions made by Digital Creations and
# many individuals on behalf of Digital Creations. Specific
# attributions are listed in the accompanying credits file.
#
##############################################################################
import
TutorialTopic
import
App.Common
import
os.path
import
string
import
os
import
stat
from
DateTime
import
DateTime
from
urllib
import
quote_plus
from
StructuredText.StructuredText
import
html_quote
import
re
from
HelpSys
import
APIHelpTopic
def
initialize
(
context
):
# abuse registerClass to get a tutorial constructor
# in the product add list
context
.
registerClass
(
None
,
meta_type
=
'Zope Tutorial'
,
permission
=
'Add Documents, Images, and Files'
,
constructors
=
(
TutorialTopic
.
addTutorialForm
,
TutorialTopic
.
addTutorial
),
)
# create tutorial help topics
lesson_path
=
os
.
path
.
join
(
App
.
Common
.
package_home
(
globals
()),
'tutorial.stx'
)
glossary_path
=
os
.
path
.
join
(
App
.
Common
.
package_home
(
globals
()),
'glossary.stx'
)
help
=
context
.
getProductHelp
()
# test to see if nothing has changed since last registration
if
help
.
lastRegistered
is
not
None
and
\
help
.
lastRegistered
>=
DateTime
(
os
.
stat
(
lesson_path
)[
stat
.
ST_MTIME
]):
return
help
.
lastRegistered
=
DateTime
()
# delete old help topics
for
id
in
help
.
objectIds
(
'Help Topic'
):
help
.
_delObject
(
id
)
# create glossary
text
=
open
(
glossary_path
).
read
()
text
=
term_pat
.
sub
(
defineTerm
,
text
)
glossary
=
TutorialTopic
.
TutorialTopic
(
'glossary'
,
'Zope Tutorial Glossary'
,
text
)
# create lessons
f
=
open
(
lesson_path
)
lines
=
[]
id
=
0
while
1
:
line
=
f
.
readline
()
if
(
string
.
strip
(
line
)
and
string
.
find
(
line
,
' '
)
!=
0
)
or
line
==
''
:
# new topic
if
lines
:
id
=
id
+
1
topic_id
=
'topic_%d'
%
id
text
=
string
.
join
(
lines
[
1
:],
''
)
text
=
term_pat
.
sub
(
glossaryTerm
,
text
)
topic
=
TutorialTopic
.
TutorialTopic
(
topic_id
,
string
.
strip
(
lines
[
0
]),
text
)
context
.
registerHelpTopic
(
topic_id
,
topic
)
lines
=
[
line
]
else
:
lines
.
append
(
line
)
if
line
==
''
:
break
f
.
close
()
# register glossary last
context
.
registerHelpTopic
(
'glossary'
,
glossary
)
# Glossary functions
# ----------------
term_pat
=
re
.
compile
(
r'\
[([^
\]])+?\
]
')
terms=[]
def glossaryTerm(match):
"""
A linked glossary term
"""
name=match.group(1)
if name in terms:
return """<a href="../glossary#%s">%s</a>""" % \
(quote_plus(name), html_quote(name))
return """[%s]""" % name
def defineTerm(match):
"""
Define a glossary term
"""
name=match.group(1)
terms.append(name)
return """<a name="%s"></a>\n\n<strong>%s</strong>""" % \
(quote_plus(name), html_quote(name))
lib/python/Products/ZopeTutorial/glossary.stx
0 → 100644
View file @
c2f76353
Glossary
For general information about Zope <a
href="http://www.zope.org/SiteIndex/searchForm" target="_blank">search</a> Zope.org.
For Zope documentation try the <a href="http://zdp.zope.org" target="_blank">Zope
Documentation Project</a> site and the <a
href="http://www.zope.org/Documentation" target="_blank">documentation area</a> on Zope.org.
[DTML] -- A tag-based reporting language, like ASP
or PHP. It allows you to perform operations on Zope objects and insert the
results in web pages.
For more information on DTML see the <a
href="http://www.zope.org/Documentation/Guides/DTML" target="_blank">DTML
Reference</a>.
[DTML Document] -- A Zope object for a web page.
You can use DTML tags in a DTML document.
For more information on DTML Documents see the <a
href="http://www.zope.org/Documentation/Guides/ZCMG-HTML/ZCMG.9.2.html"
target="_blank">Zope Content Manager's Guide</a>.
<dtml-var "apiLink('OFSP'+'.DTMLDocument.DTMLDocument', REQUEST)">
[DTML Method] -- A Zope object that holds a piece of content that can
be inserted into a web page. You can use DTML tags in a DTML Method.
For more information on DTML Methods see the <a
href="http://www.zope.org/Documentation/Guides/ZCMG-HTML/ZCMG.9.1.html"
target="_blank">Zope Content Manager's Guide</a>.
<dtml-var "apiLink('OFSP'+'.DTMLMethod.DTMLMethod', REQUEST)">
[Delete] -- Remove an object from the current Folder. Unlike Cut, this
does not place a copy of the object in the clipboard.
[Folder] -- A Zope object that contains other Zope objects. You can
move objects between Folders by cutting and pasting them. You can
add new objects to Folders with the product add list.
For more information on Folders see the <a
href="http://www.zope.org/Documentation/Guides/ZCMG-HTML/ZCMG.8.2.html"
target="_blank">Zope Content Manager's Guide</a>.
[Cut] -- Remove a Zope object from its current location and place it
in the clipboard.
[Copy] -- Copy a Zope object to the clipboard.
[Database Connection] -- A Zope object to connect to a relational
database.
For more information on Database Connections see the <a
href="http://www.zope.org/Documentation/Guides/ZSQL-HTML/ZSQL.1.3.html"
target="_blank">SQL Method User's Guide</a>.
[<dtml-call>] -- A DTML tag that allows your to perform an
action without inserting anything into a web page.
For more information on <dtml-call> tag see the <a
href="http://www.zope.org/Documentation/Guides/DTML-HTML/DTML.11.html"
target="_blank">DTML Reference</a>.
[<dtml-if>] -- A DTML tag that allows you to conditionally insert
objects into a DTML Document or DTML Method.
For more information on <dtml-if> tag see the <a
href="http://www.zope.org/Documentation/Guides/DTML-HTML/DTML.7.html"
target="_blank">DTML Reference</a>.
[<dtml-in>] -- A DTML tag that allows you to loop over a sequence
of objects.
For more information on <dtml-in> tag see the <a
href="http://www.zope.org/Documentation/Guides/DTML-HTML/DTML.8.html"
target="_blank">DTML Reference</a>.
[<dtml-var>] -- A DTML tag that allows you to insert objects
into a DTML Document or DTML Method.
For more information on <dtml-var> tag see the <a
href="http://www.zope.org/Documentation/Guides/DTML-HTML/DTML.6.html"
target="_blank">DTML Reference</a>.
[<dtml-sendmail>] -- A DTML tag that sends email.
For more information on <dtml-sendmail> tag see the <a
href="http://www.zope.org/Documentation/Guides/DTML-HTML/DTML.17.html"
target="_blank">DTML Reference</a>.
[Mail Host] -- A Zope object that allows you to send email.
Use the <dtml-sendmail> tag to send email.
[Paste] -- Insert the objects from the clipboard into the current Folder.
[Image] -- A Zope object for a picture.
For more information on Images see the <a
href="http://www.zope.org/Documentation/Guides/ZCMG-HTML/ZCMG.8.4.html"
>Zope Content Manager's Guide</a>.
<dtml-var "apiLink('OFSP'+'.Image.Image', REQUEST)">
[HTTP Cookies] -- Cookies allow you to record information in a visitor's
browser. This is commonly used to provide personalization for a web site.
[id] -- All Zope objects have an identifier known as the id. The id is a
short string that contains letters and numbers, spaces, underscores, dashes,
commas and tildes. Here are some example ids: 'index.html', 'My Page',
'item-12'.
[index_html] -- The name of the default Zope object in a Folder.
When you view a Folder the index_html object will be displayed
unless you specify otherwise. This is just like the 'index.html'
or 'default.htm' default web page with a normal web server.
[product add list] -- The list of addable objects. Select an object
from the product add list to create a new object in the current
Folder.
[Properties] -- Properties contain small pieces of content.
[standard_html_header] -- The standard Zope header object. By
convention this object displays an HTML header.
[standard_html_footer] -- The standard Zope footer object. By
convention this object displays an HTML footer.
[title] -- Most Zope objects can have titles. Titles are optional
one line descriptions.
[Undo] -- Allows you to recover from mistakes by undoing a
transaction.
[ZSQL Method] -- A Zope object to get data into or out of a relational
database.
For more information on ZSQL Methods see the <a
href="http://www.zope.org/Documentation/Guides/ZSQL-HTML/ZSQL.1.4.html"
target="_blank">SQL Method User's Guide</a>.
\ No newline at end of file
lib/python/Products/ZopeTutorial/lessonView.dtml
0 → 100644
View file @
c2f76353
<dtml-var standard_html_header>
<style type="text/css">
PRE {
background-color : #FFFFAA;
border : thin solid;
white-space : pre;
padding-bottom : 10pt;
padding-left : 10pt;
padding-right : 10pt;
padding-top : 10pt;
}
.feedback {
font-size: 9pt;
}
.warning {
font-size: 9pt;
font-weight: bold;
color: red;
}
.information {
font-size: 9pt;
font-weight: bold;
}
</style>
<dtml-var obj>
<dtml-unless "id=='glossary'">
<hr>
<dtml-var tutorialNavigation>
</dtml-unless>
<p class="feedback">Comments on this lesson?
<a href="mailto:docs@digicool.com?subject=<dtml-var title>">Email feedback</a>.
</p>
<dtml-var standard_html_footer>
\ No newline at end of file
lib/python/Products/ZopeTutorial/tutorial.stx
0 → 100644
View file @
c2f76353
Welcome to the Zope Tutorial
This tutorial show you the basics of using Zope. You will
learn how to create and manage Zope resources by building
a web site devoted to tracking Elvis sightings. Each lesson
includes working examples in Zope that allow you to learn
by hands-on experimentation.
The tutorial assumes that you are familiar with basic Internet
technologies such as <a href="http://www.w3.org/MarkUp/Guide/"
target="_blank">HTML</a>,
URLs, and web browers.
The "Back" and "Next" buttons on the bottom of each tutorial
page allow you to navigate the tutorial.
If you get lost you can use your browser's back button to
return to an earlier lesson.
If you want to quit the tutorial close your browser window.
You can return to the tutorial later by visiting the tutorial
object you created in the Zope management screen, and viewing
its "README" document.
<dtml-unless tutorialExamplesURL>
<dtml-call "REQUEST.set('hide_'+'next', 1)">
Install Tutorial Examples
Before you can begin the tutorial you must install the tutorial
examples.
1. Go to the Zope management screen and choose 'Zope Tutorial' from
the product add list.
2. Choose an id for the tutorial and click the 'Add' button.
3. Now you should be taken to the tutorial.
</dtml-unless>
Lesson 1. "Elvis Lives" Home Page
<dtml-var "tutorialShowLesson(1, REQUEST)">
Let's create a home page for "Elvis Lives", an organization that
tracks Elvis sightings.
First you need to create a [DTML Document]
for the home page. Zope uses 'DTML Documents' for web pages.
1. Select 'DTML Document' from the product add list.
2. Type 'home.html' for the document [id].
3. Type 'Elvis Lives' for the document [title].
4. Click 'Add and Edit'.
You should now see a screen
where you can edit your web page.
1. Change the contents of the document to::
<dtml-var standard_html_header>
<dtml-var header.gif>
<p>
Welcome to <i><dtml-var title></i>,
your source for information about
Elvis and Elvis sightings.
</p>
<dtml-var standard_html_footer>
2. Click the 'Change' button.
The contents of the document are a mixture of HTML and special
tags called [DTML].
The [<dtml-var>] tag inserts things into
web pages. In this page we insert a page header, a page footer,
an image, and a title with DTML.
Now let's see what our page
looks like.
1. Click the 'View' tab at the top of the screen.
Congratulations you've created
a web page with Zope.
Summary
Zope allows you to create and manage objects through
the web.
* Zope uses [DTML Document]s
for web pages.
* Create Zope objects by choosing them from the product add list.
* View Zope objects by clicking the 'View' tab.
* Insert content in web pages with the
[<dtml-var>] tag.
In the next lesson you'll expand
your web site.
Lesson 2. Going to Graceland
<dtml-var "tutorialShowLesson(2, REQUEST)">
Elvis loved his home, Graceland. Let's link an existing
page to our web site with information about Graceland.
1. Click on the 'graceland' [Folder].
2. Inside you should see a number of objects including an
'index_html' document.
If a [Folder] contains an object named [index_html], that object
provides the folder's default view. This is just like how an
'index.html' or a 'default.htm' file works with a conventional
web server.
Notice that the location of the 'graceland' folder appears at the
top of the management screen. This tells you the URL of the
folder.
You can navigate between Zope management screens using the location
links. Go back up to the enclosing folders by clicking their ids in
the URL.
3. Return to the enclosing folder by clicking the 'lesson2' link
in the folder's URL.
4. Click on the 'home.html' document to edit it.
5. Change the contents of the 'home.html' document to::
<dtml-var standard_html_header>
<dtml-var header.gif>
<p>
Welcome to <i><dtml-var title></i>,
your source for information about
Elvis and Elvis sightings.
</p>
<h2>About Elvis</h2>
<p>
Elvis was born in
<a href="graceland">Graceland</a>.
</p>
<dtml-var standard_html_footer>
6. Click the 'Change' button.
This adds a link the the 'graceland' folder. Now let's test
out the link.
7. Click the 'View' tab.
You've successfully created a multi-page site with a link between two
Zope objects.
Summary
Zope files and documents have a lot in common with
conventional web pages--they have names, are arranged in hierarchies,
and have URLs that correspond to their locations.
* [Folder]s contain other Zope objects.
* The default object for a Folder is named [index_html].
* The Zope management screens allow you to navigate between folders
and determine the URLs of objects.
In the next lesson you'll learn about moving
pages around in Zope.
Lesson 3. Elvis's Favorite Foods
<dtml-var "tutorialShowLesson(3, REQUEST)">
Elvis really liked to eat. Right now we have a couple pages about
foods Elvis liked. But as our web site grows we'll need a whole area
devoted to food. Let's create a [Folder] for food information.
1. Select 'Folder' from the product add list.
2. Type 'food' for the folder id.
3. Type 'Elvis and Food' for the folder title.
4. Uncheck the 'Create public interface' and 'Create user folder'
check boxes.
5. Click the 'Add' button.
Now we've got some place to put our food related resources. Let's
move some objects into this folder.
1. Select the 'sandwiches', 'pie', and 'meatloaf' documents by
clicking their check boxes.
2. Click the 'Cut' button.
3. Click the 'food' folder to enter it.
4. Click the 'Paste' button.
Now that we've moved some objects, we need to update the links to
them because an object's URL is related to its location.
1. Return the the 'home.html' document in the 'lesson3' folder,
by clicking on the 'lesson3' location link and then clicking
on the 'home.html' document.
2. Change the links to the sandwiches, pie and cake documents
to::
<p>
Elvis liked to eat
<a href="food/sandwiches">sandwiches</a>,
<a href="food/pie">pie</a>, and
<a href="food/meatloaf">meatloaf</a>.
</p>
3. Click the 'Change' button.
4. Click the 'View' tab at the top of the screen.
Notice that the food resources are now correctly linked from the home
page in their new location.
Congratulations you've performed your first
web site overhaul!
Summary
You can organize Zope object in folders. You can create
new folders and move objects between folders.
* Move Zope objects around with [Cut] and [Paste].
* Folders contain other Zope objects.
* Moving a Zope object between folders changes its URL.
In the next lesson you'll learn how to change
the look and feel of your site.
Lesson 4. Site Contact Information
<dtml-var "tutorialShowLesson(4, REQUEST)">
Part of running an Elvis web site is getting in touch with Elvis
fans the world over. One way to do this is to provide contact
information on your site so that visitors can email you. Let's
create an email link at the bottom of each page on your site that
lets people send you feedback.
1. Click the 'Properties' tab.
2. Type 'webmaster' in the Id field.
3. Type your email address in the Value field.
4. Click the 'Add' button.
Now you've created a property that defines your email address. Now
let's put a link to this address in the web page footer.
1. Click the 'Contents' tab to return to the list of items in the
folder, then click the 'standard_html_footer' [DTML Method] to edit it.
2. Change the contents of the method to::
<hr>
<a href="mailto:<dtml-var webmaster>?subject=<dtml-var
title>">Email Webmaster</a>
</body>
</html>
3. Click the 'Change' button.
We've change the standard Zope footer. Now let's verify that this
change is reflected in our web pages.
4. Navigate to the 'home.html' document in the 'lesson4' folder
by clicking on the 'lesson4' location link and then clicking
on the 'home.html' document.
5. Now click the 'View' tab to view the web site.
Notice that there is now an email link at the bottom of every web
page. The email link uses the property you defined and includes the
title of the web page as the email subject.
The email link appears on every page because every page
includes the 'standard_html_footer' object with the
[<dtml-var>] tag.
Summary
By consolidating content into components such as a common footer
you can provide a uniform look and feel for your web site.
* [Properties] contain small pieces of content.
* Use the [<dtml-var>] tag to insert properties into web
pages.
* [standard_html_footer] is used as the default Zope web page footer.
In the next lesson you'll see how to organize content
with a collection of Zope objects.
Lesson 5. Recent Elvis Sightings
<dtml-var "tutorialShowLesson(5, REQUEST)">
Elvis sightings happen all the time. You'd like to record them and
list them on your web site. Let's look at a simple system for
organizing sightings and displaying them on your web site.
1. Click the 'sightings' web page.
2. Click the 'View' tab to view it.
Notice how the page lists a number of Elvis sightings. Also notice
that these sightings weren't specifically listed in the contents of
the web page. Where did they come from?
1. Click the 'sightingsFolder' folder to enter it.
2. Click on the 'Shelbyville' method to examine it.
3. Go back the 'sightingsFolder' folder by clicking the
'sightingsFolder' location link.
4. Select the 'Shelbyville' method by clicking its check box.
5. Click the 'Delete' button.
You just deleted a Zope object that describes an Elvis sighting.
Let's see how this affects the 'sightings' web page.
1. Click the 'sightings' web page.
2. Click the 'View' tab to view it.
The Shelbyville sighting is not longer listed. So the list of
sightings is somehow built from the methods in the 'sightingsFolder'
folder.
What if you want the Shelbyville sighting back? Since you deleted
it, you can't just paste it back. You need to [Undo] your action.
1. Click the 'Undo' tab.
2. Select the first transaction (ending with "manage_delObjects")
by clicking its check box.
3. Click the 'Undo' button.
4. Click the 'sightingsFolder' folder to enter it.
Sure enough the 'Shelbyville' sighting has returned.
Summary
Multiple Zope objects can be combined to form complex web pages.
* [DTML Method]s contain chunks of content.
* Web pages can insert content from 'DTML Methods'.
* You can recover from mistakes with [Undo].
In the next lesson you'll learn about
looping over lists of Zope objects.
Lesson 6. Recent Elvis Sightings, cont.
<dtml-var "tutorialShowLesson(6, REQUEST)">
So we've seen that the 'sightings' page is built from objects in the
'sightingsFolder' folder. How does it work?
1. Click the 'sightings' document to edit it.
2. Change the contents of the document to::
<dtml-var standard_html_header>
<h2><dtml-var title></h2>
<table border="1">
<dtml-in expr="sightingsFolder.objectValues()">
<tr><td>
<dtml-var sequence-item>
</td></tr>
</dtml-in>
</table>
<dtml-var standard_html_footer>
3. Click the 'Change' button.
4. Click the 'View' tab.
Notice how each sighting now has a box drawn around it.
What's going on? The [<dtml-in>] tag iterates over a list of
objects. The '<dtml-var sequence-item>' tag inserts the
current object. The 'expr="sightingsFolder.objectValues()"' part of
the '<dtml-in>' tags tells it how to get a list of objects. It
calls the 'objectValues' method of the 'sightingsFolder' folder.
'objectValues' returns all the objects contained by a folder.
Summary
You can programmatically build web pages with groups of objects
by looping over them and inserting them.
* [<dtml-in>] iterates over a list of objects.
* '<dtml-var sequence-item>' inserts the current object
when looping over a list of objects.
* You can get the contents of a folder by calling its
'objectValues' method.
In the next lesson you'll learn about how to
manage and display pictures.
Lesson 7. Elvis Photo Archive
<dtml-var "tutorialShowLesson(7, REQUEST)">
The King had many faces. We can offer our site visitors a peek at
the many sides of Elvis with a photo archive.
1. Click the 'photos' document.
2. Click the 'View' tab to view it.
This page shows the photos in the archive one after another. It's not
very fancy.
Let's improve it to organize the photos by title and size.
The improved photo archive will list
the photos by title and includes a link each
picture.
1. Click the 'photos' document to edit it.
2. Change the document contents to::
<dtml-var standard_html_header>
<h2><dtml-var title></h2>
<dtml-in expr="photoArchive.objectValues()">
<p>
<a href="<dtml-var absolute_url>"><dtml-var title></a>
(<dtml-var getSize> bytes)
</p>
</dtml-in>
<dtml-var standard_html_footer>
3. Click the 'Change' button.
4. Click the 'View' tab.
Notice how the photo list now shows a link to each photo along with
its size.
Summary
[Image]s can display themselves and can also provide useful
information such as size, title, and URL.
* Get the contents of a Folder with the 'objectValues' method.
* Get the size of an [Image] with the 'getSize' method.
* Get the URL of an object with the 'absolute_url' method.
In the next lesson you'll learn how to create
pictures with DTML scripting.
Lesson 8. Elvis Photo Archive, cont.
<dtml-var "tutorialShowLesson(8, REQUEST)">
Now that we have a working photo archive, let's enhance it to handle
submissions from site visitors.
1. Click the 'photoForm' document.
2. Click the 'View' tab to view it.
3. Upload a picture (JPG, GIF, PNG or other graphics format supported
by your browser.)
4. Now return to the 'lesson8' folder in the Zope management screen
using your browser's back button.
5. Click the 'photoArchive' folder to enter it.
Notice that there is now a new [Image] object in the folder. This
image was created when you uploaded your photo.
Let's investigate how this works, and add the ability to give our
uploaded photo a title.
1. Click the 'photoForm' document.
2. Change the contents of the document to::
<dtml-var standard_html_header>
<h2><dtml-var title></h2>
<p>Upload a picture to the Elvis Photo Archive.</p>
<form action="photoAction" method="post"
enctype="multipart/form-data">
<p>Title: <input type="text" name="photo_title"></p>
<p>File: <input type="file" name="file"></p>
<input type="submit">
</form>
<dtml-var standard_html_footer>
3. Click the 'Change' button.
This document collects data needed to create an Image. It calls the
'photoAction' document with that data. The 'photoAction' document
actually creates the Image.
4. Click the 'photoAction' document to edit it.
5. Change the contents of the document to::
<dtml-var standard_html_header>
<h2><dtml-var title></h2>
<dtml-call
expr="photoArchive.manage_addImage(
id='', file=file, title=photo_title)">
<p>Thanks for your photo submission.</p>
<dtml-var standard_html_footer>
The 'photoAction' document uses the [<dtml-call>] tag to
perform an action without inserting anything into the web page. It
calls the 'manage_addImage' method on the 'photoArchive' folder.
'manage_addImage' is a folder method that creates a new
Image.
Summary
You can use forms to pass information between Zope objects.
You can programmatically create new Zope objects.
* Zope documents can be the action of HTML forms.
* The [<dtml-call>] tag performs an action without inserting
anything into a web page.
* Create new [Image]s in folder with the 'manage_addImage' method.
In the next lesson you'll learn about [HTTP Cookies] and
personalizing your web site.
Lesson 9. Elvis, Up Close and Personal
<dtml-var "tutorialShowLesson(9, REQUEST)">
People who come to your site want a personal relationship with
Elvis. You can provide this by tailoring your site to your viewer's
needs and preferences. Let's add the ability to bring new Elvis
sightings to the attention of site visitors. This way when a visitor
comes to your site they'll immediately know which sightings are new
since they last visited.
1. Click the 'sightings' document to edit it.
2. Click the 'View' tab to view it.
All the sightings should be
marked as New.
3. Reload the page.
Now none of the sightings should be marked as New. This is because
you've already seen them.
4. Click the 'sightingsFolder' folder to enter it.
5. Choose 'DTML Method' from the product add list.
6. Type 'Seattle' for the id. Since the title is optional and isn't
really needed in this case, leave the title field blank.
7. Click the 'Add and Edit' button.
8. Change the contents of the method to::
<p>6/1/2000 -- Seattle</p>
<p>Elvis spotted spare changing on Broadway.
He denied being the King.</p>
9. Click the 'Change' button.
Now you've created a new sighting. Let's see if it is marked as new
by the sightings page.
10. Click the 'sightings' document in the 'lesson9' folder.
11. Click the 'View' tab.
Sure enough our new sighting is marked New.
How does this work? It uses [HTTP Cookies]. When you visit the
'sightings' page a cookie is set that records the current time. Then
each time you return to the page sightings that are newer than the
cookie will be marked as new.
Let's look at the DTML for the 'sightings' page to see how this is
done.
1. Click the 'sightings' document to edit it.
The document sets a cookie with this code::
<dtml-call expr="RESPONSE.setCookie('lastVisited', ZopeTime())">
The document checks each sighting when displaying it to see if it is
newer than the 'lastVisited' cookie::
<dtml-if expr="bobobase_modification_time() >
ZopeTime(lastVisited)">
Summary
You can use cookies to personalize a web page. Zope documents
can dynamically control their presentation.
* You can set cookies with the 'REPONSE.setCookie' method.
* The [<dtml-if>] tag allows you to test conditions.
* The 'bobobase_modification_time' method returns the last
modification time of an object.
In the next lesson you'll learn how to create
a mail feedback form.
Lesson 10. So You've Seen Elvis
<dtml-var "tutorialShowLesson(10, REQUEST)">
What's the first thing you do when you spot Elvis? Report it at the
"Elvis Lives" web site! Let's enhance our site to allow visitors to
report their Elvis sightings.
1. Click 'mailhost' [Mail Host] object to edit it.
2. Type the name of your mail server in the 'SMTP Host' field.
Your mail server is typically named 'mail'. For example,
'mail.elvislives.com'. If you don't know the name of your mail
server, ask your system administrator, or check the configuration of
your mail client.
3. Click the 'Change' button.
Now Zope can send mail. Next let's edit the template for the Elvis
sighting report mail message.
1. Click the 'reportAction' document to edit it.
This document is called when a site visitor fills out an Elvis
sighting form. This document sends you mail telling you about the
sighting. Mail is sent with the [<dtml-sendmail>] tag which
works with the mail host you just configured.
Notice that the 'to' and the 'from' line both reference a
property named 'send_to'.
2. Click the 'Properties' tab to edit properties.
3. Type your email address into the 'send_to' property field.
4. Click the 'Save Changes' button.
5. Now go to the 'reportForm' document in the 'lesson10' folder.
6. Click the 'View' tab to view it.
7. Fill out the Elvis sighting form and submit it.
You should now receive a piece of email describing the Elvis
sighting.
Congratulations, you've built a mail form. If you receive
a Zope error, there is a good chance that you haven't set the 'SMTP
Host' setting on your mail host object correctly.
Summary
After you create a mail host, you can send mail from any
Zope document using the [<dtml-sendmail>] tag.
* [Mail Host]s allow you to send email.
* The [<dtml-sendmail>] tag sends an email message.
In the next lesson you'll learn how to use Zope
to put a database on the web.
Lesson 11. The Elvis Files
<dtml-var "tutorialShowLesson(11, REQUEST)">
Everyone knows that serious web sites use databases. We agree. Let's
convert our Elvis sightings system to use a relational database.
This will provide us with scalability and interoperability.
Let's see how Zope gets data
from a database.
1. Click the 'connection' [Database Connection] to edit it.
A [Database Connection] tells Zope how to access a relational
database.
2. Click the 'Browse' tab to examine the database tables.
3. Click the plus box next to the 'ELVIS_SIGHTINGS' table to
expand it.
Now you can see the column names and types for this table. Now let's
see how to get information out of this table.
1. Click the 'getSightings' [ZSQL Method] in the 'lesson11' folder
to edit it.
Notice that the SQL statement used to fetch information about Elvis
sightings is editable in the 'Query template' field.
2. Click the 'Test' tab.
3. Click the 'Submit Query' button.
This shows you what data the ZSQL Method returns.
A [ZSQL Method] is used to get information out of or into a relational
database.
Now let's see how to use
this ZSQL Method in a web page.
1. Click the 'sightings' DTML Document in the 'lesson11' folder
to edit it.
Notice that the sightings are generated by iterating over the
'sightings' ZSQL Method with the '<dtml-in>' tag.
2. Add a line between each sighting by changing the contents of
the 'sightings' DTML Document to::
<dtml-var standard_html_header>
<h2><dtml-var title></h2>
<dtml-in getSightings>
<p><dtml-var date> -- <dtml-var location></p>
<p>Reported by <dtml-var name></p>
<p><dtml-var description></p>
<hr>
</dtml-in>
<dtml-var standard_html_footer>
3. Click the 'Change' button.
4. Click the 'View' tab to view the document.
Summary
Zope can work with data from relational databases in the
same way it treats other Zope objects.
* [Database Connection]s tell Zope about relational databases.
* [ZSQL Method]s get information out of or into a relational
database.
Congratulations
Well done! You've finished the Zope Tutorial.
You can return to lesson examples and use these objects as the
basis of your own experiments. Just copy and paste them and start
changing them.
To find out more about Zope visit
<a href="http://www.zope.org">Zope.org</a>.
You may be especially interested in the Zope
<a href="http://www.zope.org/Documentation/How-To">How-Tos</a>,
<a href="http://www.zope.org/Resources/MailingLists">mailing lists</a>,
Guides and the <a href="http://zdp.zope.org">Zope Documentation Project</a>.
Please <a href="mailto:docs@digicool.com">let us know</a> what you liked and disliked
about the Zope Tutorial. Telling us what you think helps us
improve it.
\ No newline at end of file
lib/python/Products/ZopeTutorial/tutorialAdd.dtml
0 → 100644
View file @
c2f76353
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<HTML
lang=
"en"
>
<HEAD>
<TITLE>
Add Zope Tutorial
</TITLE>
</HEAD>
<BODY
BGCOLOR=
"#FFFFFF"
LINK=
"#000099"
VLINK=
"#555555"
>
<H2>
Add Zope Tutorial
</H2>
<P>
This tutorial show you the basics of using Zope. You will
learn how to create and manage Zope resources by builting
a web site devoted to tracking Elvis sightings. Each lesson
includes working examples in Zope that allow you to learn
by hands on experimentation.
</P><P>
The tutorial assumes that you are familiar with basic Internet
technologies such as
<a
href=
"http://www.w3.org/MarkUp/Guide/"
>
HTML
</a>
,
URLs, and web browers.
</P>
<FORM
ACTION=
"addTutorial"
METHOD=
"POST"
>
<TABLE
CELLSPACING=
"2"
>
<TR>
<TD
ALIGN=
"LEFT"
VALIGN=
"TOP"
>
<STRONG>
Id
</STRONG>
</TD>
<TD
ALIGN=
"LEFT"
VALIGN=
"TOP"
>
<INPUT
TYPE=
"TEXT"
NAME=
"id"
SIZE=
"40"
>
</TD>
</TR>
<TR>
<TD></TD>
<TD>
<BR><INPUT
TYPE=
"SUBMIT"
VALUE=
" Add "
>
</TD>
</TR>
</TABLE>
</FORM>
</BODY>
</HTML>
lib/python/Products/ZopeTutorial/tutorialNav.dtml
0 → 100644
View file @
c2f76353
<dtml-call "REQUEST.set('ids', aq_parent.objectIds())">
<dtml-call "ids.remove('glossary')">
<dtml-call "REQUEST.set('i', ids.index(id))">
<table width="100%" border="0" padding="0" spacing="0">
<tr valign="top"><td width="50%" align="right">
<dtml-if "i > 0">
<form action="../<dtml-var "ids[i-1]">">
<input type="submit" value=" < Back ">
</form>
</dtml-if>
</td>
<td width="50%">
<dtml-unless hide_next>
<dtml-if "i < _.len(ids) -1 ">
<form action="../<dtml-var "ids[i+1]">">
<input type="submit" value=" Next > ">
</form>
</dtml-if>
</dtml-unless>
</td></tr></table>
\ No newline at end of file
lib/python/Products/ZopeTutorial/version.txt
0 → 100644
View file @
c2f76353
Zope Tutorial 1.0a5
\ No newline at end of file
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment