Commit 7bbfdd64 by Owo Sugiana

Kali pertama

0 parents
Showing 55 changed files with 2342 additions and 0 deletions
*.egg
*.egg-info
*.pyc
*$py.class
*~
.coverage
coverage.xml
build/
dist/
.tox/
nosetests.xml
env*/
tmp/
Data.fs*
*.sublime-project
*.sublime-workspace
.*.sw?
.sw?
.DS_Store
coverage
test
0.1
---
- Initial version.
include *.txt *.ini *.cfg *.rst
recursive-include web_starter *.ico *.png *.css *.gif *.jpg *.pt *.txt *.mak *.mako *.js *.html *.xml *.jinja2
Web Starter
===========
Getting Started
---------------
Change directory into your newly created project::
cd web-starter
Create a Python virtual environment::
python3 -m venv ../env
Upgrade packaging tools::
../env/bin/pip install --upgrade pip setuptools
Install required package::
../env/bin/python setup.py develop-use-pip
Set sqlalchemy.url on development.ini and create tables::
../env/bin/initialize_web_starter_db development.ini
Run your project::
../env/bin/pserve --reload development.ini
Web Starter
===========
Getting Started
---------------
- Change directory into your newly created project.
cd web-starter
- Create a Python virtual environment.
python3 -m venv ../env
- Upgrade packaging tools.
../env/bin/pip install --upgrade pip setuptools
- Install required package.
../env/bin/python setup.py develop-use-pip
- Set sqlalchemy.url on development.ini and create tables.
../env/bin/initialize_web_starter_db development.ini
- Run your project.
../env/bin/pserve --reload development.ini
# A generic, single database configuration.
[alembic]
# path to migration scripts
script_location = alembic
# template used to generate migration files
# file_template = %%(rev)s_%%(slug)s
# timezone to use when rendering the date
# within the migration file as well as the filename.
# string value is passed to dateutil.tz.gettz()
# leave blank for localtime
# timezone =
# max length of characters to apply to the
# "slug" field
#truncate_slug_length = 40
# set to 'true' to run the environment during
# the 'revision' command, regardless of autogenerate
# revision_environment = false
# set to 'true' to allow .pyc and .pyo files without
# a source .py file to be detected as revisions in the
# versions/ directory
# sourceless = false
# version location specification; this defaults
# to alembic/versions. When using multiple version
# directories, initial revisions must be specified with --version-path
# version_locations = %(here)s/bar %(here)s/bat alembic/versions
# the output encoding used when revision files
# are written from script.py.mako
# output_encoding = utf-8
sqlalchemy.url = {db_url}
# Logging configuration
[loggers]
keys = root,sqlalchemy,alembic
[handlers]
keys = console
[formatters]
keys = generic
[logger_root]
level = WARN
handlers = console
qualname =
[logger_sqlalchemy]
level = WARN
handlers =
qualname = sqlalchemy.engine
[logger_alembic]
level = INFO
handlers =
qualname = alembic
[handler_console]
class = StreamHandler
args = (sys.stderr,)
level = NOTSET
formatter = generic
[formatter_generic]
format = %(levelname)-5.5s [%(name)s] %(message)s
datefmt = %H:%M:%S
Generic single-database configuration.
\ No newline at end of file
from __future__ import with_statement
from alembic import context
from sqlalchemy import engine_from_config, pool
from logging.config import fileConfig
# this is the Alembic Config object, which provides
# access to the values within the .ini file in use.
config = context.config
# Interpret the config file for Python logging.
# This line sets up loggers basically.
fileConfig(config.config_file_name)
# add your model's MetaData object here
# for 'autogenerate' support
# from myapp import mymodel
# target_metadata = mymodel.Base.metadata
target_metadata = None
# other values from the config, defined by the needs of env.py,
# can be acquired:
# my_important_option = config.get_main_option("my_important_option")
# ... etc.
def run_migrations_offline():
"""Run migrations in 'offline' mode.
This configures the context with just a URL
and not an Engine, though an Engine is acceptable
here as well. By skipping the Engine creation
we don't even need a DBAPI to be available.
Calls to context.execute() here emit the given string to the
script output.
"""
url = config.get_main_option("sqlalchemy.url")
context.configure(
url=url, target_metadata=target_metadata, literal_binds=True)
with context.begin_transaction():
context.run_migrations()
def run_migrations_online():
"""Run migrations in 'online' mode.
In this scenario we need to create an Engine
and associate a connection with the context.
"""
connectable = engine_from_config(
config.get_section(config.config_ini_section),
prefix='sqlalchemy.',
poolclass=pool.NullPool)
with connectable.connect() as connection:
context.configure(
connection=connection,
target_metadata=target_metadata
)
with context.begin_transaction():
context.run_migrations()
if context.is_offline_mode():
run_migrations_offline()
else:
run_migrations_online()
"""${message}
Revision ID: ${up_revision}
Revises: ${down_revision | comma,n}
Create Date: ${create_date}
"""
from alembic import op
import sqlalchemy as sa
${imports if imports else ""}
# revision identifiers, used by Alembic.
revision = ${repr(up_revision)}
down_revision = ${repr(down_revision)}
branch_labels = ${repr(branch_labels)}
depends_on = ${repr(depends_on)}
def upgrade():
${upgrades if upgrades else "pass"}
def downgrade():
${downgrades if downgrades else "pass"}
"""alter users date fields with time zone
Revision ID: 074b33635316
Revises:
Create Date: 2018-10-11 12:00:48.568483
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = '074b33635316'
down_revision = None
branch_labels = None
depends_on = None
def upgrade():
op.alter_column('users', 'last_login_date',
type_=sa.DateTime(timezone=True),
existing_type=sa.DateTime(timezone=False))
op.alter_column('users', 'registered_date',
type_=sa.DateTime(timezone=True),
existing_type=sa.DateTime(timezone=False))
op.alter_column('users', 'security_code_date',
type_=sa.DateTime(timezone=True),
existing_type=sa.DateTime(timezone=False))
def downgrade():
pass
delete from users;
select setval('users_id_seq', 1, false);
drop table external_identities;
drop table users_resources_permissions;
drop table groups_resources_permissions;
drop table groups_permissions;
drop table resources;
drop table users_groups;
drop table users_permissions;
drop table groups;
drop table users;
drop table alembic_version;
###
# app configuration
# https://docs.pylonsproject.org/projects/pyramid/en/latest/narr/environment.html
###
[app:main]
use = egg:web_starter
pyramid.reload_templates = true
pyramid.debug_authorization = false
pyramid.debug_notfound = false
pyramid.debug_routematch = false
pyramid.default_locale_name = id
pyramid.includes =
pyramid_debugtoolbar
sqlalchemy.url = postgresql://user:pass@localhost/dbname
timezone = Asia/Jakarta
localization = id_ID.UTF-8
# By default, the toolbar only appears for clients from IP addresses
# '127.0.0.1' and '::1'.
# debugtoolbar.hosts = 127.0.0.1 ::1
###
# wsgi server configuration
###
[server:main]
use = egg:waitress#main
listen = localhost:6543
###
# logging configuration
# https://docs.pylonsproject.org/projects/pyramid/en/latest/narr/logging.html
###
[loggers]
keys = root, web_starter
[handlers]
keys = console
[formatters]
keys = generic
[logger_root]
level = INFO
handlers = console
[logger_web_starter]
level = DEBUG
handlers =
qualname = web_starter
[handler_console]
class = StreamHandler
args = (sys.stderr,)
level = NOTSET
formatter = generic
[formatter_generic]
format = %(asctime)s %(levelname)-5.5s [%(name)s:%(lineno)s][%(threadName)s] %(message)s
###
# app configuration
# https://docs.pylonsproject.org/projects/pyramid/en/latest/narr/environment.html
###
[app:main]
use = egg:web_starter
pyramid.reload_templates = false
pyramid.debug_authorization = false
pyramid.debug_notfound = false
pyramid.debug_routematch = false
pyramid.default_locale_name = id
sqlalchemy.url = postgresql://user:pass@localhost/dbname
timezone = Asia/Jakarta
localization = id_ID.UTF-8
###
# wsgi server configuration
###
[server:main]
use = egg:waitress#main
listen = *:6543
###
# logging configuration
# https://docs.pylonsproject.org/projects/pyramid/en/latest/narr/logging.html
###
[loggers]
keys = root, web_starter
[handlers]
keys = console
[formatters]
keys = generic
[logger_root]
level = WARN
handlers = console
[logger_web_starter]
level = WARN
handlers =
qualname = web_starter
[handler_console]
class = StreamHandler
args = (sys.stderr,)
level = NOTSET
formatter = generic
[formatter_generic]
format = %(asctime)s %(levelname)-5.5s [%(name)s:%(lineno)s][%(threadName)s] %(message)s
[pytest]
testpaths = web_starter
python_files = *.py
name,path
home,/
login,/login
logout,/logout
password,/password
import os
import sys
import subprocess
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',
'pyramid_chameleon',
'pyramid_debugtoolbar',
'waitress',
'zope.sqlalchemy',
'psycopg2-binary',
'pytz',
'ziggurat-foundations',
'alembic',
'colander',
'deform',
'pyramid_beaker',
]
tests_require = [
'WebTest >= 1.3.1', # py3 compat
'pytest',
'pytest-cov',
]
if sys.argv[1:] and sys.argv[1] == 'develop-use-pip':
bin_ = os.path.split(sys.executable)[0]
pip = os.path.join(bin_, 'pip')
for package in requires:
cmd = [pip, 'install', package]
subprocess.call(cmd)
cmd = [sys.executable, sys.argv[0], 'develop']
subprocess.call(cmd)
sys.exit()
setup(
name='web_starter',
version='0.0',
description='Web Starter',
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 = web_starter:main',
],
'console_scripts': [
'initialize_web_starter_db = web_starter.scripts.initialize_db:main',
]
},
)
import csv
import deform
from pkg_resources import resource_filename
from pyramid.i18n import get_localizer
from pyramid.threadlocal import get_current_request
from pyramid.config import Configurator
from pyramid_beaker import session_factory_from_settings
from pyramid.authentication import AuthTktAuthenticationPolicy
from pyramid.authorization import ACLAuthorizationPolicy
from sqlalchemy import engine_from_config
from .models import (
DBSession,
Base,
)
from .security import (
group_finder,
get_user,
)
from .tools.this_framework import get_locale_name
from .views import RemoveSlashNotFoundViewFactory
def main(global_config, **settings):
""" This function returns a Pyramid WSGI application.
"""
engine = engine_from_config(settings, 'sqlalchemy.')
DBSession.configure(bind=engine)
Base.metadata.bind = engine
session_factory = session_factory_from_settings(settings)
config = Configurator(
settings=settings,
root_factory='web_starter.models.ziggurat.RootFactory',
session_factory=session_factory,
locale_negotiator=get_locale_name)
config.include('pyramid_beaker')
config.include('pyramid_chameleon')
authn_policy = AuthTktAuthenticationPolicy(
'sosecret', callback=group_finder, hashalg='sha512')
config.set_authentication_policy(authn_policy)
authz_policy = ACLAuthorizationPolicy()
config.set_authorization_policy(authz_policy)
config.add_request_method(get_user, 'user', reify=True)
config.add_notfound_view(RemoveSlashNotFoundViewFactory())
config.add_static_view('static', 'static', cache_max_age=3600)
config.add_static_view('deform_static', 'deform:static')
config.add_translation_dirs('locale')
def translator(term):
return get_localizer(get_current_request()).translate(term)
deform_template_dir = resource_filename('deform', 'templates/')
zpt_renderer = deform.ZPTRendererFactory(
[deform_template_dir],
translator=translator,)
deform.Form.set_default_renderer(zpt_renderer)
with open('routes.csv') as f:
c = csv.DictReader(f)
for row in c:
config.add_route(row['name'], row['path'])
config.scan()
return config.make_wsgi_app()
#
# SOME DESCRIPTIVE TITLE
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2018.
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE 1.0\n"
"POT-Creation-Date: 2018-10-15 20:15+0700\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Lingua 4.13\n"
#. Default: Copyright &copy; Pylons Project
msgid "copyright"
msgstr ""
# Translations template for colander.
# Copyright (C) 2016 ORGANIZATION
# This file is distributed under the same license as the colander project.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2016.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: colander 1.3.1\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2016-09-18 22:57+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 2.3.4\n"
#: colander/__init__.py:294
msgid "Invalid value"
msgstr ""
#: colander/__init__.py:340
msgid "String does not match expected pattern"
msgstr ""
#: colander/__init__.py:359
msgid "Invalid email address"
msgstr ""
#: colander/__init__.py:387
msgid "${val} is less than minimum value ${min}"
msgstr ""
#: colander/__init__.py:388
msgid "${val} is greater than maximum value ${max}"
msgstr ""
#: colander/__init__.py:435
msgid "Shorter than minimum length ${min}"
msgstr ""
#: colander/__init__.py:436
msgid "Longer than maximum length ${max}"
msgstr ""
#: colander/__init__.py:464
msgid "\"${val}\" is not one of ${choices}"
msgstr ""
#: colander/__init__.py:479
msgid "\"${val}\" must not be one of ${choices}"
msgstr ""
#: colander/__init__.py:501
msgid "One or more of the choices you made was not acceptable"
msgstr ""
#: colander/__init__.py:523 colander/__init__.py:528
msgid "\"${val}\" is not a valid credit card number"
msgstr ""
#: colander/__init__.py:549
msgid "Must be a URL"
msgstr ""
#: colander/__init__.py:553
msgid "Invalid UUID string"
msgstr ""
#: colander/__init__.py:650
msgid "\"${val}\" is not a mapping type: ${err}"
msgstr ""
#: colander/__init__.py:694
msgid "Unrecognized keys in mapping: \"${val}\""
msgstr ""
#: colander/__init__.py:789 colander/__init__.py:1020
msgid "\"${val}\" is not iterable"
msgstr ""
#: colander/__init__.py:797
msgid "\"${val}\" has an incorrect number of elements (expected ${exp}, was ${was})"
msgstr ""
#: colander/__init__.py:936 colander/__init__.py:967
msgid "${cstruct} is not iterable"
msgstr ""
#: colander/__init__.py:1254
msgid "${val} cannot be serialized: ${err}"
msgstr ""
#: colander/__init__.py:1275
msgid "${val} is not a string: ${err}"
msgstr ""
#: colander/__init__.py:1295 colander/__init__.py:1306
msgid "\"${val}\" is not a number"
msgstr ""
#: colander/__init__.py:1454
msgid "${val} is not a string"
msgstr ""
#: colander/__init__.py:1465
msgid "\"${val}\" is neither in (${false_choices}) nor in (${true_choices})"
msgstr ""
#: colander/__init__.py:1525 colander/__init__.py:1542 colander/__init__.py:1552
msgid "relative name \"${val}\" irresolveable without package"
msgstr ""
#: colander/__init__.py:1582
msgid "\"${val}\" has no __name__"
msgstr ""
#: colander/__init__.py:1591
msgid "\"${val}\" is not a string"
msgstr ""
#: colander/__init__.py:1600
msgid "The dotted name \"${name}\" cannot be imported"
msgstr ""
#: colander/__init__.py:1648 colander/__init__.py:1722
msgid "Invalid date"
msgstr ""
#: colander/__init__.py:1662
msgid "\"${val}\" is not a datetime object"
msgstr ""
#: colander/__init__.py:1733
msgid "\"${val}\" is not a date object"
msgstr ""
#: colander/__init__.py:1794
msgid "Invalid time"
msgstr ""
#: colander/__init__.py:1804
msgid "\"${val}\" is not a time object"
msgstr ""
#: colander/tests/test_colander.py:334 colander/tests/test_colander.py:341
msgid "fail ${val}"
msgstr ""
#: colander/tests/test_colander.py:537
msgid "${val}: ${choices}"
msgstr ""
#
# SOME DESCRIPTIVE TITLE
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2016.
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE 1.0\n"
"POT-Creation-Date: 2016-11-19 13:28+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: \n"
"Generated-By: Lingua 4.11\n"
#: ./deform/schema.py:109
#, python-format
msgid "${value} is not a dictionary"
msgstr ""
#: ./deform/schema.py:116
#, python-format
msgid "${value} has no ${key} key"
msgstr ""
#: ./deform/widget.py:718 ./deform/widget.py:1800
msgid "Incomplete date"
msgstr ""
#: ./deform/widget.py:721
msgid "Incomplete time"
msgstr ""
#: ./deform/widget.py:1253
msgid "Fields did not match"
msgstr ""
#: ./deform/widget.py:1254
msgid "Value"
msgstr ""
#: ./deform/widget.py:1255
msgid "Confirm Value"
msgstr ""
#: ./deform/widget.py:1321
msgid "Password did not match confirm"
msgstr ""
#: ./deform/widget.py:1485
#, python-format
msgid "Add ${subitem_title}"
msgstr ""
#: ./deform/templates/checked_password.pt:15
msgid "Password"
msgstr ""
#: ./deform/templates/checked_password.pt:25
msgid "Confirm Password"
msgstr ""
#: ./deform/templates/dateparts.pt:10
msgid "Year"
msgstr ""
#: ./deform/templates/dateparts.pt:18
msgid "Month"
msgstr ""
#: ./deform/templates/dateparts.pt:26
msgid "Day"
msgstr ""
#: ./deform/templates/datetimeinput.pt:9
msgid "Date"
msgstr ""
#: ./deform/templates/datetimeinput.pt:16
msgid "Time"
msgstr ""
#: ./deform/templates/form.pt:35
msgid "There was a problem with your submission"
msgstr ""
#: ./deform/templates/form.pt:37
msgid "Errors have been highlighted below"
msgstr ""
#: ./deform/templates/mapping.pt:13 ./deform/templates/mapping_accordion.pt:29
msgid "There was a problem with this section"
msgstr ""
#. Default: en
#: ./deform/templates/richtext.pt:30
msgid "language-code"
msgstr ""
#: ./deform/templates/sequence_item.pt:26
msgid "Reorder (via drag and drop)"
msgstr ""
#: ./deform/templates/sequence_item.pt:31
msgid "Remove"
msgstr ""
#: ./deform/templates/readonly/checkbox.pt:8
msgid "True"
msgstr ""
#: ./deform/templates/readonly/checkbox.pt:12
msgid "False"
msgstr ""
#: ./deform/templates/readonly/checked_password.pt:4
msgid "Password not displayed."
msgstr ""
#: ./deform/tests/test_widget.py:1752
#, python-format
msgid "Yo ${subitem_title}"
msgstr ""
#
# Indonesian translations for PACKAGE package
# This file is distributed under the same license as the PACKAGE package.
# Owo Sugiana <sugiana@gmail.com>, 2018.
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE 1.0\n"
"POT-Creation-Date: 2018-10-15 20:15+0700\n"
"PO-Revision-Date: 2018-10-15 20:17+0700\n"
"Last-Translator: Owo Sugiana <sugiana@gmail.com>\n"
"Language-Team: Indonesian\n"
"Language: id\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Lingua 4.13\n"
#. Default: Copyright &copy; Pylons Project
msgid "copyright"
msgstr "Hak cipta &copy; Pylons Project"
msgid "welcome"
msgstr "Selamat datang di <span class=\"font-normal\">Web Starter</span>, sebuah aplikasi Pyramid yang dibentuk oleh<br><span class=\"font-normal\">Cookiecutter</span>."
msgid "you-are-logged-in"
msgstr "Anda sedang masuk sebagai ${username}. Klik <a href=\"/logout\">di sini</a> untuk keluar."
No preview for this file type
# Translations template for colander.
# Copyright (C) 2016 ORGANIZATION
# This file is distributed under the same license as the colander project.
# Owo Sugiana <sugiana@gmail.com>, 2016.
#
msgid ""
msgstr ""
"Project-Id-Version: colander 1.3.1\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2016-09-18 22:57+0200\n"
"PO-Revision-Date: 2018-10-14 19:52+0700\n"
"Last-Translator: Owo Sugiana <sugiana@gmail.com>\n"
"Language-Team: Indonesian\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 2.3.4\n"
"Language: id\n"
#: colander/__init__.py:294
msgid "Invalid value"
msgstr ""
#: colander/__init__.py:340
msgid "String does not match expected pattern"
msgstr ""
#: colander/__init__.py:359
msgid "Invalid email address"
msgstr ""
#: colander/__init__.py:387
msgid "${val} is less than minimum value ${min}"
msgstr ""
#: colander/__init__.py:388
msgid "${val} is greater than maximum value ${max}"
msgstr ""
#: colander/__init__.py:435
msgid "Shorter than minimum length ${min}"
msgstr ""
#: colander/__init__.py:436
msgid "Longer than maximum length ${max}"
msgstr ""
#: colander/__init__.py:464
msgid "\"${val}\" is not one of ${choices}"
msgstr ""
#: colander/__init__.py:479
msgid "\"${val}\" must not be one of ${choices}"
msgstr ""
#: colander/__init__.py:501
msgid "One or more of the choices you made was not acceptable"
msgstr ""
#: colander/__init__.py:523 colander/__init__.py:528
msgid "\"${val}\" is not a valid credit card number"
msgstr ""
#: colander/__init__.py:549
msgid "Must be a URL"
msgstr ""
#: colander/__init__.py:553
msgid "Invalid UUID string"
msgstr ""
#: colander/__init__.py:650
msgid "\"${val}\" is not a mapping type: ${err}"
msgstr ""
#: colander/__init__.py:694
msgid "Unrecognized keys in mapping: \"${val}\""
msgstr ""
#: colander/__init__.py:789 colander/__init__.py:1020
msgid "\"${val}\" is not iterable"
msgstr ""
#: colander/__init__.py:797
msgid ""
"\"${val}\" has an incorrect number of elements (expected ${exp}, was ${was})"
msgstr ""
#: colander/__init__.py:936 colander/__init__.py:967
msgid "${cstruct} is not iterable"
msgstr ""
#: colander/__init__.py:1254
msgid "${val} cannot be serialized: ${err}"
msgstr ""
#: colander/__init__.py:1275
msgid "${val} is not a string: ${err}"
msgstr ""
#: colander/__init__.py:1295 colander/__init__.py:1306
msgid "\"${val}\" is not a number"
msgstr ""
#: colander/__init__.py:1454
msgid "${val} is not a string"
msgstr ""
#: colander/__init__.py:1465
msgid "\"${val}\" is neither in (${false_choices}) nor in (${true_choices})"
msgstr ""
#: colander/__init__.py:1525 colander/__init__.py:1542
#: colander/__init__.py:1552
msgid "relative name \"${val}\" irresolveable without package"
msgstr ""
#: colander/__init__.py:1582
msgid "\"${val}\" has no __name__"
msgstr ""
#: colander/__init__.py:1591
msgid "\"${val}\" is not a string"
msgstr ""
#: colander/__init__.py:1600
msgid "The dotted name \"${name}\" cannot be imported"
msgstr ""
#: colander/__init__.py:1648 colander/__init__.py:1722
msgid "Invalid date"
msgstr ""
#: colander/__init__.py:1662
msgid "\"${val}\" is not a datetime object"
msgstr ""
#: colander/__init__.py:1733
msgid "\"${val}\" is not a date object"
msgstr ""
#: colander/__init__.py:1794
msgid "Invalid time"
msgstr ""
#: colander/__init__.py:1804
msgid "\"${val}\" is not a time object"
msgstr ""
#: colander/tests/test_colander.py:334 colander/tests/test_colander.py:341
msgid "fail ${val}"
msgstr ""
#: colander/tests/test_colander.py:537
msgid "${val}: ${choices}"
msgstr ""
msgid "Required"
msgstr "Dibutuhkan"
No preview for this file type
#
# Indonesian translations for PACKAGE package
# This file is distributed under the same license as the PACKAGE package.
# Owo Sugiana <sugiana@gmail.com>, 2016.
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE 1.0\n"
"POT-Creation-Date: 2016-11-19 13:28+0200\n"
"PO-Revision-Date: 2018-10-14 19:45+0700\n"
"Last-Translator: Owo Sugiana <sugiana@gmail.com>\n"
"Language-Team: Indonesian\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: id\n"
"Generated-By: Lingua 4.11\n"
#: deform/schema.py:109
#, python-format
msgid "${value} is not a dictionary"
msgstr ""
#: deform/schema.py:116
#, python-format
msgid "${value} has no ${key} key"
msgstr ""
#: deform/widget.py:718 deform/widget.py:1800
msgid "Incomplete date"
msgstr ""
#: deform/widget.py:721
msgid "Incomplete time"
msgstr ""
#: deform/widget.py:1253
msgid "Fields did not match"
msgstr ""
#: deform/widget.py:1254
msgid "Value"
msgstr ""
#: deform/widget.py:1255
msgid "Confirm Value"
msgstr ""
#: deform/widget.py:1321
msgid "Password did not match confirm"
msgstr ""
#: deform/widget.py:1485
#, python-format
msgid "Add ${subitem_title}"
msgstr ""
#: deform/templates/checked_password.pt:15
msgid "Password"
msgstr ""
#: deform/templates/checked_password.pt:25
msgid "Confirm Password"
msgstr ""
#: deform/templates/dateparts.pt:10
msgid "Year"
msgstr ""
#: deform/templates/dateparts.pt:18
msgid "Month"
msgstr ""
#: deform/templates/dateparts.pt:26
msgid "Day"
msgstr ""
#: deform/templates/datetimeinput.pt:9
msgid "Date"
msgstr ""
#: deform/templates/datetimeinput.pt:16
msgid "Time"
msgstr ""
#: deform/templates/form.pt:35
msgid "There was a problem with your submission"
msgstr "Ada masalah dengan apa yang Anda kirim"
#: deform/templates/form.pt:37
msgid "Errors have been highlighted below"
msgstr "Pesan kesalahannya ada di bawah ini"
#: deform/templates/mapping.pt:13 deform/templates/mapping_accordion.pt:29
msgid "There was a problem with this section"
msgstr ""
#. Default: en
#: deform/templates/richtext.pt:30
msgid "language-code"
msgstr ""
#: deform/templates/sequence_item.pt:26
msgid "Reorder (via drag and drop)"
msgstr ""
#: deform/templates/sequence_item.pt:31
msgid "Remove"
msgstr ""
#: deform/templates/readonly/checkbox.pt:8
msgid "True"
msgstr ""
#: deform/templates/readonly/checkbox.pt:12
msgid "False"
msgstr ""
#: deform/templates/readonly/checked_password.pt:4
msgid "Password not displayed."
msgstr ""
#: deform/tests/test_widget.py:1752
#, python-format
msgid "Yo ${subitem_title}"
msgstr ""
#
# Indonesian translations for PACKAGE package
# This file is distributed under the same license as the PACKAGE package.
# Owo Sugiana <sugiana@gmail.com>, 2018.
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE 1.0\n"
"POT-Creation-Date: 2018-10-15 08:41+0700\n"
"PO-Revision-Date: 2018-10-15 08:41+0700\n"
"Last-Translator: Owo Sugiana <sugiana@gmail.com>\n"
"Language-Team: Indonesian\n"
"Language: id\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Lingua 4.13\n"
#. Default: Enter new password for ${name}:
msgid "Enter new password for ${name}: "
msgstr "Masukkan password baru untuk ${name}: "
#. Default: Retype new password for ${name}:
msgid "Retype new password for ${name}: "
msgstr "Ulangi password baru untuk ${name}: "
msgid "Sorry, passwords do not match"
msgstr "Maaf, kedua password tidak sama"
No preview for this file type
#
# Indonesian translations for PACKAGE package
# This file is distributed under the same license as the PACKAGE package.
# Owo Sugiana <sugiana@gmail.com>, 2018.
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE 1.0\n"
"POT-Creation-Date: 2018-10-13 22:28+0700\n"
"PO-Revision-Date: 2018-10-13 22:30+0700\n"
"Last-Translator: Owo Sugiana <sugiana@gmail.com>\n"
"Language-Team: Indonesian\n"
"Language: id\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Lingua 4.13\n"
#. Default: Login failed
#: views/login.py:42
msgid "Login failed"
msgstr "Gagal masuk"
#. Default: Login
#: views/login.py:58
msgid "Login"
msgstr "Masuk"
msgid "Submit"
msgstr "Kirim"
msgid "Username"
msgstr "Nama"
msgid "Password"
msgstr "Kata kunci"
#
# SOME DESCRIPTIVE TITLE
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2018.
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE 1.0\n"
"POT-Creation-Date: 2018-10-15 08:41+0700\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Lingua 4.13\n"
#. Default: Enter new password for ${name}:
msgid "ask-password-1"
msgstr ""
#. Default: Retype new password for ${name}:
msgid "ask-password-2"
msgstr ""
msgid "Sorry, passwords do not match"
msgstr ""
#
# SOME DESCRIPTIVE TITLE
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2018.
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE 1.0\n"
"POT-Creation-Date: 2018-10-13 22:28+0700\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Lingua 4.13\n"
#. Default: Login failed
#: ./views/login.py:42
msgid "login-failed"
msgstr ""
#. Default: Login
#: ./views/login.py:58
msgid "login"
msgstr ""
from sqlalchemy.orm import (
sessionmaker,
scoped_session,
)
from sqlalchemy.ext.declarative import declarative_base
from zope.sqlalchemy import ZopeTransactionExtension
DBSession = scoped_session(
sessionmaker(extension=ZopeTransactionExtension()))
Base = declarative_base()
from pyramid.security import (
Allow,
Authenticated,
)
from sqlalchemy import PrimaryKeyConstraint
import ziggurat_foundations.models
from ziggurat_foundations.models.base import BaseModel
from ziggurat_foundations.models.external_identity import ExternalIdentityMixin
from ziggurat_foundations.models.group import GroupMixin
from ziggurat_foundations.models.group_permission import GroupPermissionMixin
from ziggurat_foundations.models.group_resource_permission import GroupResourcePermissionMixin
from ziggurat_foundations.models.resource import ResourceMixin
from ziggurat_foundations.models.user import UserMixin
from ziggurat_foundations.models.user_group import UserGroupMixin
from ziggurat_foundations.models.user_permission import UserPermissionMixin
from ziggurat_foundations.models.user_resource_permission import UserResourcePermissionMixin
from ziggurat_foundations import ziggurat_model_init
from . import (
Base,
DBSession,
)
# this is needed for scoped session approach like in pylons 1.0
ziggurat_foundations.models.DBSession = DBSession
# optional for folks who pass request.db to model methods
# Base is sqlalchemy's Base = declarative_base() from your project
class Group(GroupMixin, Base):
pass
class GroupPermission(GroupPermissionMixin, Base):
pass
class UserGroup(UserGroupMixin, Base):
pass
class GroupResourcePermission(GroupResourcePermissionMixin, Base):
__table_args__ = (
PrimaryKeyConstraint(
"group_id",
"resource_id",
"perm_name"),)
class Resource(ResourceMixin, Base):
# ... your own properties....
# example implementation of ACLS for pyramid application
@property
def __acl__(self):
acls = []
if self.owner_user_id:
acls.extend([(Allow, self.owner_user_id, ALL_PERMISSIONS,), ])
if self.owner_group_id:
acls.extend([(Allow, "group:%s" % self.owner_group_id,
ALL_PERMISSIONS,), ])
return acls
class UserPermission(UserPermissionMixin, Base):
pass
class UserResourcePermission(UserResourcePermissionMixin, Base):
pass
class User(UserMixin, Base):
# ... your own properties....
pass
class ExternalIdentity(ExternalIdentityMixin, Base):
pass
# you can define multiple resource derived models to build a complex
# application like CMS, forum or other permission based solution
#class Entry(Resource):
# """
# Resource of `entry` type
# """
# __tablename__ = 'entries'
# __mapper_args__ = {'polymorphic_identity': 'entry'}
# resource_id = sa.Column(sa.Integer(),
# sa.ForeignKey('resources.resource_id',
# onupdate='CASCADE',
# ondelete='CASCADE', ),
# primary_key=True, )
# ... your own properties....
# some_property = sa.Column(sa.UnicodeText())
class RootFactory:
def __init__(self, request):
self.__acl__ = [
(Allow, Authenticated, 'view'),
]
for gp in DBSession.query(GroupPermission):
acl_name = 'group:{gid}'.format(gid=gp.group_id)
self.__acl__.append((Allow, acl_name, gp.perm_name))
#ziggurat_model_init(User, Group, UserGroup, GroupPermission, passwordmanager=None)
ziggurat_model_init(User, Group, UserGroup, GroupPermission, UserPermission,
UserResourcePermission, GroupResourcePermission, Resource,
ExternalIdentity, passwordmanager=None)
id,email,status,user_name
0,anonymous@local,0
,admin@local,1,admin
import os
import sys
import csv
import subprocess
import transaction
from getpass import getpass
from translationstring import (
TranslationString,
Translator,
ugettext_policy,
)
from translationstring.tests.translations import Translations
from sqlalchemy import engine_from_config
from ziggurat_foundations.models.services.user import UserService
from pyramid.paster import (
get_appsettings,
setup_logging,
)
from ..models import (
DBSession,
Base,
)
from ..models.ziggurat import (
Group,
GroupPermission,
UserGroup,
User,
)
class Penerjemah:
def __init__(self, domain='initialize_db'):
self.domain = domain
def configure(self, settings):
locale_ids = [settings['pyramid.default_locale_name']]
here = os.path.abspath(os.path.dirname(__file__))
localedir = os.path.join(here, '..', 'locale')
try:
self.translations = Translations.load(
localedir, locale_ids, self.domain)
except TypeError:
self.translations = None
self.translator = self.translations and Translator(
self.translations, ugettext_policy)
def terjemahkan(self, msg, mapping=None):
ts = TranslationString(msg, mapping=mapping)
if self.translator:
return self.translator(ts)
return ts.interpolate()
obj_penerjemah = Penerjemah()
def _(msg, mapping=None):
return obj_penerjemah.terjemahkan(msg, mapping)
def usage(argv):
cmd = os.path.basename(argv[0])
print('usage: %s <config_uri>\n'
'(example: "%s development.ini")' % (cmd, cmd))
sys.exit(1)
def read_file(filename):
f = open(filename)
s = f.read()
f.close()
return s
def alembic_run(ini_file, url):
bin_path = os.path.split(sys.executable)[0]
alembic_bin = os.path.join(bin_path, 'alembic')
command = (alembic_bin, 'upgrade', 'head')
s = read_file(ini_file)
s = s.replace('{db_url}', url)
f = open('alembic.ini', 'w')
f.write(s)
f.close()
subprocess.call(command)
os.remove('alembic.ini')
def get_file(filename):
base_dir = os.path.split(__file__)[0]
fullpath = os.path.join(base_dir, 'data', filename)
return open(fullpath)
def ask_password(name):
data = dict(name=name)
msg1 = _('Enter new password for ${name}: ', mapping=data)
msg2 = _('Retype new password for ${name}: ', mapping=data)
while True:
pass1 = getpass(msg1)
if not pass1:
continue
pass2 = getpass(msg2)
if pass1 == pass2:
return pass1
print(_('Sorry, passwords do not match'))
def restore_csv(table, filename):
q = DBSession.query(table)
if q.first():
return
with get_file(filename) as f:
reader = csv.DictReader(f)
for cf in reader:
row = table()
for fieldname in cf:
val = cf[fieldname]
if not val:
continue
setattr(row, fieldname, val)
DBSession.add(row)
return True
def main(argv=sys.argv):
if len(argv) != 2:
usage(argv)
config_uri = argv[1]
setup_logging(config_uri)
settings = get_appsettings(config_uri)
obj_penerjemah.configure(settings)
engine = engine_from_config(settings, 'sqlalchemy.')
Base.metadata.bind = engine
Base.metadata.create_all()
alembic_run('alembic.ini.tpl', settings['sqlalchemy.url'])
with transaction.manager:
if restore_csv(User, 'users.csv'):
DBSession.flush()
q = DBSession.query(User).filter_by(id=1)
user = q.first()
password = ask_password(user.user_name)
UserService.set_password(user, password)
UserService.regenerate_security_code(user)
restore_csv(Group, 'groups.csv')
restore_csv(UserGroup, 'users_groups.csv')
from .models import DBSession
from .models.ziggurat import (
User,
UserGroup,
)
def group_finder(login, request):
q = DBSession.query(User).filter_by(id=login)
u = q.first()
if not u or not u.status:
return # None means logout
r = []
q = DBSession.query(UserGroup).filter_by(user_id=u.id)
for ug in q:
acl_name = 'group:{gid}'.format(gid=ug.group_id)
r.append(acl_name)
return r
def get_user(request):
uid = request.authenticated_userid
if uid:
q = DBSession.query(User).filter_by(id=uid)
return q.first()
body {
padding-top: 40px;
padding-bottom: 40px;
background-color: #eee;
}
.form-signin {
max-width: 330px;
padding: 15px;
margin: 0 auto;
}
.form-signin .form-signin-heading,
.form-signin .checkbox {
margin-bottom: 10px;
}
.form-signin .checkbox {
font-weight: normal;
}
.form-signin .form-control {
position: relative;
font-size: 16px;
height: auto;
padding: 10px;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
}
.form-signin .form-control:focus {
z-index: 2;
}
.form-signin input[type="text"] {
margin-bottom: -1px;
border-bottom-left-radius: 0;
border-bottom-right-radius: 0;
}
.form-signin input[type="password"] {
margin-bottom: 10px;
border-top-left-radius: 0;
border-top-right-radius: 0;
}
\ No newline at end of file
@import url(//fonts.googleapis.com/css?family=Open+Sans:300,400,600,700);
body {
font-family: "Open Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
font-weight: 300;
color: #ffffff;
background: #bc2131;
}
h1,
h2,
h3,
h4,
h5,
h6 {
font-family: "Open Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
font-weight: 300;
}
p {
font-weight: 300;
}
.font-normal {
font-weight: 400;
}
.font-semi-bold {
font-weight: 600;
}
.font-bold {
font-weight: 700;
}
.starter-template {
margin-top: 250px;
}
.starter-template .content {
margin-left: 10px;
}
.starter-template .content h1 {
margin-top: 10px;
font-size: 60px;
}
.starter-template .content h1 .smaller {
font-size: 40px;
color: #f2b7bd;
}
.starter-template .content .lead {
font-size: 25px;
color: #f2b7bd;
}
.starter-template .content .lead .font-normal {
color: #ffffff;
}
.starter-template .links {
float: right;
right: 0;
margin-top: 125px;
}
.starter-template .links ul {
display: block;
padding: 0;
margin: 0;
}
.starter-template .links ul li {
list-style: none;
display: inline;
margin: 0 10px;
}
.starter-template .links ul li:first-child {
margin-left: 0;
}
.starter-template .links ul li:last-child {
margin-right: 0;
}
.starter-template .links ul li.current-version {
color: #f2b7bd;
font-weight: 400;
}
.starter-template .links ul li a, a {
color: #f2b7bd;
text-decoration: underline;
}
.starter-template .links ul li a:hover, a:hover {
color: #ffffff;
text-decoration: underline;
}
.starter-template .links ul li .icon-muted {
color: #eb8b95;
margin-right: 5px;
}
.starter-template .links ul li:hover .icon-muted {
color: #ffffff;
}
.starter-template .copyright {
margin-top: 10px;
font-size: 0.9em;
color: #f2b7bd;
text-transform: lowercase;
float: right;
right: 0;
}
@media (max-width: 1199px) {
.starter-template .content h1 {
font-size: 45px;
}
.starter-template .content h1 .smaller {
font-size: 30px;
}
.starter-template .content .lead {
font-size: 20px;
}
}
@media (max-width: 991px) {
.starter-template {
margin-top: 0;
}
.starter-template .logo {
margin: 40px auto;
}
.starter-template .content {
margin-left: 0;
text-align: center;
}
.starter-template .content h1 {
margin-bottom: 20px;
}
.starter-template .links {
float: none;
text-align: center;
margin-top: 60px;
}
.starter-template .copyright {
float: none;
text-align: center;
}
}
@media (max-width: 767px) {
.starter-template .content h1 .smaller {
font-size: 25px;
display: block;
}
.starter-template .content .lead {
font-size: 16px;
}
.starter-template .links {
margin-top: 40px;
}
.starter-template .links ul li {
display: block;
margin: 0;
}
.starter-template .links ul li .icon-muted {
display: none;
}
.starter-template .copyright {
margin-top: 20px;
}
}
import unittest
from pyramid import testing
class ViewTests(unittest.TestCase):
def setUp(self):
self.config = testing.setUp()
def tearDown(self):
testing.tearDown()
def test_my_view(self):
from .views import my_view
request = testing.DummyRequest()
info = my_view(request)
self.assertEqual(info['project'], 'Web Starter')
class FunctionalTests(unittest.TestCase):
def setUp(self):
from web_starter import main
app = main({})
from webtest import TestApp
self.testapp = TestApp(app)
def test_root(self):
res = self.testapp.get('/', status=200)
self.assertTrue(b'Pyramid' in res.body)
from pyramid.threadlocal import get_current_registry
from pyramid.events import (
subscriber,
BeforeRender,
)
from pyramid.i18n import default_locale_negotiator
LOCALE_NAMES = {
'en': 'English',
'id': 'Indonesia',
}
LOCALE_IDS = list(LOCALE_NAMES.keys())
def get_settings():
return get_current_registry().settings
def get_locale_should_be(request):
if 'HTTP_ACCEPT_LANGUAGE' not in request.environ:
return 'id'
# id-ID,id;q=0.9,en-US;q=0.8,en;q=0.7
vals = request.environ['HTTP_ACCEPT_LANGUAGE'].split(',')
for val in vals:
for locale_id in LOCALE_IDS:
if val.find(locale_id) == 0:
return locale_id
def get_locale_name(request):
return default_locale_negotiator(request) or \
get_locale_should_be(request)
def get_locale_title(name):
if name in LOCALE_NAMES:
return LOCALE_NAMES[name]
return name
def get_locale_title_(request):
name = get_locale_name(request)
return get_locale_title(name)
@subscriber(BeforeRender)
def add_global(event):
event['locale_name'] = get_locale_title_
event['locale_should_be'] = get_locale_should_be
import calendar
from datetime import (
date,
datetime,
)
import pytz
from .this_framework import get_settings
def get_timezone():
settings = get_settings()
return pytz.timezone(settings['timezone'])
def create_datetime(year, month, day, hour=0, minute=7, second=0,
microsecond=0):
tz = get_timezone()
return datetime(year, month, day, hour, minute, second,
microsecond, tzinfo=tz)
def create_date(year, month, day):
return create_datetime(year, month, day)
def as_timezone(tz_date):
localtz = get_timezone()
if not tz_date.tzinfo:
tz_date = create_datetime(tz_date.year, tz_date.month, tz_date.day,
tz_date.hour, tz_date.minute, tz_date.second,
tz_date.microsecond)
return tz_date.astimezone(localtz)
def create_now():
tz = get_timezone()
return datetime.now(tz)
def date_from_str(value):
separator = None
value = value.split()[0] # dd-mm-yyyy HH:MM:SS
for s in ['-', '/']:
if value.find(s) > -1:
separator = s
break
if separator:
t = map(lambda x: int(x), value.split(separator))
y, m, d = t[2], t[1], t[0]
if d > 999: # yyyy-mm-dd
y, d = d, y
else:
y, m, d = int(value[:4]), int(value[4:6]), int(value[6:])
return date(y, m, d)
def dmy(tgl):
return tgl.strftime('%d-%m-%Y')
def dmyhms(t):
return t.strftime('%d-%m-%Y %H:%M:%S')
def next_month(year, month):
if month == 12:
month = 1
year += 1
else:
month += 1
return year, month
def best_date(year, month, day):
try:
return date(year, month, day)
except ValueError:
last_day = calendar.monthrange(year, month)[1]
return date(year, month, last_day)
def next_month_day(year, month, day):
year, month = next_month(year, month)
return best_date(year, month, day)
from pyramid.view import view_config
from pyramid.httpexceptions import (
default_exceptionresponse_view,
HTTPFound,
)
from pyramid.interfaces import IRoutesMapper
from pyramid.response import Response
# http://stackoverflow.com/questions/9845669/pyramid-inverse-to-add-notfound-viewappend-slash-true
class RemoveSlashNotFoundViewFactory:
def __init__(self, notfound_view=None):
if notfound_view is None:
notfound_view = default_exceptionresponse_view
self.notfound_view = notfound_view
def __call__(self, context, request):
if not isinstance(context, Exception):
# backwards compat for an append_notslash_view registered via
# config.set_notfound_view instead of as a proper exception view
context = getattr(request, 'exception', None) or context
path = request.path
registry = request.registry
mapper = registry.queryUtility(IRoutesMapper)
if mapper is not None and path.endswith('/'):
noslash_path = path.rstrip('/')
for route in mapper.get_routes():
if route.match(noslash_path) is not None:
qs = request.query_string
if qs:
noslash_path += '?' + qs
return HTTPFound(location=noslash_path)
return self.notfound_view(context, request)
@view_config(route_name='home', renderer='templates/mytemplate.pt',
permission='view')
def my_view(request):
if '_LOCALE_' in request.GET:
resp = Response()
resp.set_cookie('_LOCALE_', request.GET['_LOCALE_'], 31536000)
return HTTPFound(
location=request.route_url('home'), headers=resp.headers)
return {'project': 'Web Starter'}
from datetime import datetime
from pyramid.response import Response
from pyramid.view import view_config
from pyramid.httpexceptions import (
HTTPFound,
HTTPForbidden,
)
from pyramid.security import (
remember,
forget,
authenticated_userid,
)
from pyramid.i18n import TranslationStringFactory
import transaction
import colander
from deform import (
Form,
ValidationFailure,
widget,
Button,
)
from ziggurat_foundations.models.services.user import UserService
from ..tools.waktu import create_now
from ..models import DBSession
from ..models.ziggurat import User
_ = TranslationStringFactory('login')
class Login(colander.Schema):
username = colander.SchemaNode(colander.String(), title=_('Username'))
password = colander.SchemaNode(
colander.String(), widget=widget.PasswordWidget(),
title=_('Password'))
# http://deformdemo.repoze.org/interfield/
def login_validator(form, value):
user = form.user
if not user or \
not user.status or \
not user.user_password or \
not UserService.check_password(user, value['password']):
raise colander.Invalid(form, _('Login failed'))
def get_login_headers(request, user):
headers = remember(request, user.id)
user.last_login_date = create_now()
DBSession.add(user)
DBSession.flush()
transaction.commit()
return headers
@view_config(context=HTTPForbidden, renderer='templates/login.pt')
@view_config(route_name='login', renderer='templates/login.pt')
def view_login(request):
if authenticated_userid(request):
return HTTPFound(location=request.route_url('home'))
if '_LOCALE_' in request.GET:
resp = Response()
resp.set_cookie('_LOCALE_', request.GET['_LOCALE_'], 31536000)
return HTTPFound(
location=request.route_url('login'), headers=resp.headers)
resp = dict(title=_('Login'))
schema = Login(validator=login_validator)
btn_submit = Button('submit', _('Submit'))
form = Form(schema, buttons=(btn_submit,))
if 'submit' in request.POST:
controls = request.POST.items()
identity = request.POST.get('username')
if identity.find('@') > -1:
q = DBSession.query(User).filter_by(email=identity)
else:
q = DBSession.query(User).filter_by(user_name=identity)
schema.user = user = q.first()
try:
c = form.validate(controls)
except ValidationFailure:
resp['form'] = form.render()
return resp
headers = get_login_headers(request, user)
return HTTPFound(location=request.route_url('home'),
headers=headers)
resp['form'] = form.render()
return resp
@view_config(route_name='logout')
def view_logout(request):
headers = forget(request)
return HTTPFound(location = request.route_url('home'),
headers = headers)
###################
# Change password #
###################
class Password(colander.Schema):
old_password = colander.SchemaNode(colander.String(),
title="Kata Sandi Lama",
widget=widget.PasswordWidget())
new_password = colander.SchemaNode(colander.String(),
title="Kata Sandi Baru",
widget=widget.PasswordWidget())
retype_password = colander.SchemaNode(colander.String(),
title="Ketik Ulang Kata Sandi",
widget=widget.PasswordWidget())
def password_validator(form, value):
if not form.request.user.check_password(value['old_password']):
raise colander.Invalid(form, 'Invalid old password.')
if value['new_password'] != value['retype_password']:
raise colander.Invalid(form, 'Retype mismatch.')
@view_config(route_name='password', renderer='templates/password.pt',
permission='view')
def view_password(request):
schema = Password(validator=password_validator)
form = Form(schema, buttons=('simpan','batal'))
if request.POST:
if 'simpan' in request.POST:
schema.request = request
controls = request.POST.items()
try:
c = form.validate(controls)
except ValidationFailure as e:
request.session['invalid password'] = e.render()
return HTTPFound(location=request.route_url('password'))
user = request.user
user.password = c['new_password']
DBSession.add(user)
DBSession.flush()
transaction.commit()
#request.session.flash('Your password has been changed.')
request.session.flash('Password telah berhasil diubah.')
return HTTPFound(location=request.route_url('home'))
elif 'invalid password' in request.session:
r = dict(form=request.session['invalid password'])
del request.session['invalid password']
return r
return dict(form=form.render())
<!DOCTYPE html>
<html lang="en">
<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="">
<meta name="author" content="">
<link rel="shortcut icon" href="/static/img/favicon.png">
<title tal:content="request.title" />
<!-- Bootstrap core CSS -->
<link href="/deform_static/css/bootstrap.min.css" rel="stylesheet">
<link href="/deform_static/css/form.css" rel="stylesheet">
<!-- Custom styles for this template -->
<link href="/static/css/navbar-fixed-top.css" rel="stylesheet">
<link href="/static/css/theme.css" rel="stylesheet">
</head>
<body>
<!-- Fixed navbar -->
<div class="navbar navbar-default navbar-fixed-top" role="navigation">
<div class="container">
<div class="navbar-header">
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="#"></a>
</div>
<div class="navbar-collapse collapse">
<ul class="nav navbar-nav">
<li tal:attributes="class request.path == '/' and 'active'"><a href="/">Home</a></li>
<li class="dropdown" tal:attributes="class request.matched_route.name in ['imgw-message', 'imgw-agent'] and 'active'">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">IM <b class="caret"></b></a>
<ul class="dropdown-menu">
<li><a href="/imgw/message">Pesan</a></li>
<li><a href="/imgw/message/add">Kirim pesan</a></li>
<li><a href="/imgw/agent">Agent</a></li>
</ul>
</li>
<li class="dropdown" tal:attributes="class request.matched_route.name in ['user', 'user-add', 'user-edit', 'user-delete'] and 'active'">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">Admin <b class="caret"></b></a>
<ul class="dropdown-menu">
<li><a href="/bank">Mutasi Transaksi Bank</a></li>
<li><a href="/user">User</a></li>
</ul>
</li>
<li class="dropdown" tal:attributes="class request.path in ['/password'] and 'active'">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">My Account <b class="caret"></b></a>
<ul class="dropdown-menu">
<li><a href="/logout">${request.user.nice_username()} Logout</a></li>
<li><a href="/password">Change password</a></li>
</ul>
</li>
</ul>
</div><!--/.nav-collapse -->
</div>
</div>
<div class="container">
<div tal:condition="request.session.peek_flash()">
<div class="alert alert-success" tal:repeat="message request.session.pop_flash()">${message}</div>
</div>
<div tal:condition="request.session.peek_flash('error')">
<div class="alert alert-danger" tal:repeat="message request.session.pop_flash('error')">${message}</div>
</div>
<div metal:define-slot="content"></div>
</div> <!-- /container -->
<!-- Bootstrap core JavaScript
================================================== -->
<!-- Placed at the end of the document so the pages load faster -->
<script type="text/javascript" src="/deform_static/scripts/jquery-2.0.3.min.js"></script>
<script type="text/javascript" src="/deform_static/scripts/bootstrap.min.js"></script>
<script type="text/javascript" src="/deform_static/scripts/deform.js"></script>
</body>
</html>
<!DOCTYPE html metal:define-macro="layout">
<html lang="${request.locale_name}"
xmlns:i18n="http://xml.zope.org/namespaces/i18n"
i18n:domain="ChameleonI18n">
<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('web_starter:static/pyramid-16x16.png')}">
<title>Cookiecutter Starter project for the Pyramid Web Framework</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('web_starter: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('web_starter:static/pyramid.png')}" alt="pyramid web framework">
</div>
<div class="col-md-10">
<div metal:define-slot="content">No content</div>
</div>
</div>
<div class="row">
<div class="links">
<ul>
<li><i class="glyphicon glyphicon-cog icon-muted"></i><a href="https://github.com/Pylons/pyramid">Github Project</a></li>
<li><i class="glyphicon glyphicon-globe icon-muted"></i><a href="https://webchat.freenode.net/?channels=pyramid">IRC Channel</a></li>
<li><i class="glyphicon glyphicon-home icon-muted"></i><a href="https://pylonsproject.org">Pylons Project</a></li>
</ul>
</div>
</div>
<div class="row">
<div class="copyright" i18n:translate="copyright">
Copyright &copy; 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>
<!DOCTYPE html>
<html lang="en">
<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="">
<meta name="author" content="">
<link rel="shortcut icon" href="/static/pyramid-16x16.png">
<title tal:content="title"/>
<!-- Bootstrap core CSS -->
<link href="/deform_static/css/bootstrap.min.css" rel="stylesheet"/>
<link href="/deform_static/css/form.css" rel="stylesheet"/>
<!-- Custom styles for this template -->
<link href="/static/signin.css" rel="stylesheet">
</head>
<body>
<div class="container">
<div tal:content="structure form"/>
<p tal:condition="request.locale_name != 'en'">
<a href="?_LOCALE_=en">English</a>
</p>
<p tal:condition="request.locale_name != 'id'">
<a href="?_LOCALE_=id">Indonesia</a>
</p>
</div> <!-- /container -->
<!-- Bootstrap core JavaScript
================================================== -->
<!-- Placed at the end of the document so the pages load faster -->
<script type="text/javascript" src="/deform_static/scripts/jquery-2.0.3.min.js"></script>
<script type="text/javascript" src="/deform_static/scripts/bootstrap.min.js"></script>
<script type="text/javascript" src="/deform_static/scripts/deform.js"></script>
</body>
</html>
<div metal:use-macro="load: layout.pt">
<div metal:fill-slot="content" i18n:domain="ChameleonI18n">
<div class="content">
<h1><span class="font-semi-bold">Pyramid</span> <span class="smaller">Starter project</span></h1>
<p i18n:translate="welcome" class="lead">Welcome to <span
class="font-normal">Web Starter</span>, a&nbsp;Pyramid
application generated&nbsp;by<br><span class="font-normal">Cookiecutter</span>.</p>
<p i18n:translate="you-are-logged-in" tal:condition="request.user">You are logged in as
<span tal:replace="request.user.user_name" i18n:name="username" />. Click <a href="/logout">here</a> to
logout.</p>
</div>
</div>
</div>
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!