Ejemplo n.º 1
0
    def get(self):

      if capabilities.CapabilitySet('datastore_v3', capabilities=['write']).is_enabled() \
          and capabilities.CapabilitySet('memcache').is_enabled():
        self.update_counter(main_counter)
        self.update_counter(stats_counter)
        self.update_counter(unlisted_counter)
        self.update_counter(queries_counter)
        self.update_counter(steamids_counter)
        self.update_counter(report_counter)
Ejemplo n.º 2
0
def get_apis_statuses(e):
    if not isinstance(e, DeadlineExceededError):
        return {}
    t1 = time.time()
    statuses = {
        'blobstore':
        capabilities.CapabilitySet('blobstore').is_enabled(),
        'datastore_v3':
        capabilities.CapabilitySet('datastore_v3').is_enabled(),
        'datastore_v3_write':
        capabilities.CapabilitySet('datastore_v3', ['write']).is_enabled(),
        'images':
        capabilities.CapabilitySet('images').is_enabled(),
        'mail':
        capabilities.CapabilitySet('mail').is_enabled(),
        'memcache':
        capabilities.CapabilitySet('memcache').is_enabled(),
        'taskqueue':
        capabilities.CapabilitySet('taskqueue').is_enabled(),
        'urlfetch':
        capabilities.CapabilitySet('urlfetch').is_enabled(),
    }
    t2 = time.time()
    statuses['time'] = t2 - t1
    return statuses
  def testEnabledCapability(self):
    admin_msg = 'Write performance is degraded for the next 15-20 minutes.'
    expected_request = IsEnabledRequest()
    expected_request.package = 'datastore_v3'
    expected_request.capability.append('*')
    expected_request.capability.append('write')

    actual_response = IsEnabledResponse()
    actual_response.summary_status = IsEnabledResponse.ENABLED
    config = actual_response.config.add()
    config.package = 'datastore_v3'
    config.capability = '*'
    config.status = CapabilityConfig.ENABLED
    config = actual_response.config.add()
    config.package = 'datastore_v3'
    config.capability = 'write'
    config.status = CapabilityConfig.ENABLED
    config.admin_message = admin_msg

    stub_map = MockCapabilityImpl(expected_request, actual_response)
    capability_set = capabilities.CapabilitySet(
        'datastore_v3', capabilities=['write'], stub_map=stub_map)

    self.assertEqual(True, capability_set.is_enabled());
    self.assertEqual(admin_msg, capability_set.admin_message())
    self.assertEqual(actual_response, capability_set._get_status())
  def testMultipleCalls(self):
    expected_request = IsEnabledRequest()
    expected_request.package = 'datastore_v3'
    expected_request.capability.append('*')
    expected_request.call.append('Put')
    expected_request.call.append('Delete')


    actual_response = IsEnabledResponse()
    actual_response.summary_status = IsEnabledResponse.ENABLED
    config = actual_response.config.add()
    config.package = 'datastore_v3'
    config.capability = '*'
    config.status = CapabilityConfig.ENABLED

    config = actual_response.config.add()
    config.package = 'datastore_v3'
    config.capability = 'write'
    config.status = CapabilityConfig.ENABLED

    config = actual_response.config.add()
    config.package = 'datastore_v3'
    config.capability = 'erase'
    config.status = CapabilityConfig.ENABLED

    stub_map = MockCapabilityImpl(expected_request, actual_response)
    capability_set = capabilities.CapabilitySet(
        'datastore_v3', methods=['Put', 'Delete'], stub_map=stub_map)
    self.assertEqual(True, capability_set.is_enabled());
    self.assertEqual('', capability_set.admin_message())
    self.assertEqual(actual_response, capability_set._get_status())
  def testDisabledCapability(self):
    admin_msg = 'The datastore is in read-only mode for scheduled maintenance.'
    expected_request = IsEnabledRequest()
    expected_request.package = 'datastore_v3'
    expected_request.capability.append('*')
    expected_request.capability.append('write')

    actual_response = IsEnabledResponse()
    actual_response.summary_status = IsEnabledResponse.DISABLED
    config = actual_response.config.add()
    config.package = 'datastore_v3'
    config.capability = '*'
    config.status = CapabilityConfig.ENABLED

    config = actual_response.config.add()
    config.package = 'datastore_v3'
    config.capability = 'write'
    config.status = CapabilityConfig.DISABLED
    config.admin_message = admin_msg

    stub_map = MockCapabilityImpl(expected_request, actual_response)
    capability_set = capabilities.CapabilitySet(
        'datastore_v3', capabilities=['write'], stub_map=stub_map)

    self.assertEqual(False, capability_set.is_enabled());
    self.assertEqual(admin_msg, capability_set.admin_message())
    self.assertEqual(actual_response, capability_set._get_status())
    def Render(cls, handler):
        """Rendering method that can be called by main.py.

    Args:
      handler: the webapp.RequestHandler invoking the method
    """
        namespace = handler.request.get('namespace')
        kinds = handler.request.get('kind', allow_multiple=True)
        sizes_known, size_total, remainder = utils.ParseKindsAndSizes(kinds)

        (namespace_str, kind_str) = utils.GetPrintableStrs(namespace, kinds)
        notreadonly_warning = capabilities.CapabilitySet(
            'datastore_v3', capabilities=['write']).is_enabled()
        blob_warning = bool(blobstore.BlobInfo.all().fetch(1))
        datastore_type = datastore._GetConnection().get_datastore_type()
        high_replication_warning = (datastore_type == datastore_rpc.Connection.
                                    HIGH_REPLICATION_DATASTORE)

        template_params = {
            'form_target': DoCopyHandler.SUFFIX,
            'kind_list': kinds,
            'remainder': remainder,
            'sizes_known': sizes_known,
            'size_total': size_total,
            'app_id': handler.request.get('app_id'),
            'cancel_url': handler.request.get('cancel_url'),
            'kind_str': kind_str,
            'namespace_str': namespace_str,
            'xsrf_token': utils.CreateXsrfToken(XSRF_ACTION),
            'notreadonly_warning': notreadonly_warning,
            'blob_warning': blob_warning,
            'high_replication_warning': high_replication_warning,
        }
        utils.RenderToResponse(handler, 'confirm_copy.html', template_params)
Ejemplo n.º 7
0
    def Render(cls, handler):
        """Rendering method that can be called by main.py or get.

    This method executes no action, so the method by which it is accessed is
    immaterial.  Creating a form with get may be a desirable function.  That is,
    if this builtin is turned on, anyone can create a form to delete a kind by
    simply linking to the ConfirmDeleteHandler like so:
    <a href="/_ah/datastore_admin/confirm_delete?kind=trash">
        Delete all Trash Objects</a>

    Args:
      handler: the webapp.RequestHandler invoking the method
    """
        readonly_warning = not capabilities.CapabilitySet(
            'datastore_v3', capabilities=['write']).is_enabled()
        namespace = handler.request.get('namespace')
        kinds = handler.request.get_all('kind')
        sizes_known, size_total, remainder = utils.ParseKindsAndSizes(kinds)

        (namespace_str, kind_str) = utils.GetPrintableStrs(namespace, kinds)
        template_params = {
            'readonly_warning': readonly_warning,
            'form_target': DoDeleteHandler.SUFFIX,
            'kind_list': kinds,
            'remainder': remainder,
            'sizes_known': sizes_known,
            'size_total': size_total,
            'app_id': handler.request.get('app_id'),
            'datastore_admin_home': utils.GenerateHomeUrl(handler.request),
            'kind_str': kind_str,
            'namespace_str': namespace_str,
            'xsrf_token': utils.CreateXsrfToken(XSRF_ACTION),
        }
        utils.RenderToResponse(handler, 'confirm_delete.html', template_params)
Ejemplo n.º 8
0
  def Render(cls, handler, default_backup_id=None,
             default_delete_backup_after_restore=False):
    """Rendering method that can be called by main.py.

    Args:
      handler: the webapp.RequestHandler invoking the method
      default_backup_id: default value for handler.request
      default_delete_backup_after_restore: default value for handler.request
    """
    backup_id = handler.request.get('backup_id', default_backup_id)
    backup = db.get(backup_id) if backup_id else None
    notreadonly_warning = capabilities.CapabilitySet(
        'datastore_v3', capabilities=['write']).is_enabled()
    original_app_warning = backup.original_app
    if os.getenv('APPLICATION_ID') == original_app_warning:
      original_app_warning = None
    template_params = {
        'form_target': DoBackupRestoreHandler.SUFFIX,
        'queues': None,
        'cancel_url': handler.request.get('cancel_url'),
        'backup': backup,
        'delete_backup_after_restore': handler.request.get(
            'delete_backup_after_restore', default_delete_backup_after_restore),
        'xsrf_token': utils.CreateXsrfToken(XSRF_ACTION),
        'notreadonly_warning': notreadonly_warning,
        'original_app_warning': original_app_warning
    }
    utils.RenderToResponse(handler, 'confirm_restore_from_backup.html',
                           template_params)
  def testUnknownApi(self):
    expected_request = IsEnabledRequest()
    expected_request.package = 'kittens'
    expected_request.capability.append('*')
    expected_request.capability.append('cuteness')
    expected_request.call.append('Meow')

    actual_response = IsEnabledResponse()
    actual_response.summary_status = IsEnabledResponse.ENABLED
    config = actual_response.config.add()
    config.package = 'kittens'
    config.capability = '*'
    config.status = CapabilityConfig.ENABLED

    config = actual_response.config.add()
    config.package = 'kittens'
    config.capability = 'cuteness'
    config.status = CapabilityConfig.ENABLED

    stub_map = MockCapabilityImpl(expected_request, actual_response)
    capability_set = capabilities.CapabilitySet('kittens',
                                                capabilities=['cuteness'],
                                                methods=['Meow'],
                                                stub_map=stub_map)

    self.assertEqual(True, capability_set.is_enabled())
    self.assertEqual(actual_response, capability_set._get_status())
Ejemplo n.º 10
0
    def Render(cls, handler):
        """Rendering method that can be called by main.py.

    Args:
      handler: the webapp.RequestHandler invoking the method
    """
        namespace = handler.request.get('namespace', None)
        has_namespace = namespace is not None
        kinds = handler.request.get_all('kind')
        sizes_known, size_total, remainder = utils.ParseKindsAndSizes(kinds)
        notreadonly_warning = capabilities.CapabilitySet(
            'datastore_v3', capabilities=['write']).is_enabled()
        blob_warning = bool(blobstore.BlobInfo.all().count(1))
        app_id = handler.request.get('app_id')
        template_params = {
            'form_target': DoBackupHandler.SUFFIX,
            'kind_list': kinds,
            'remainder': remainder,
            'sizes_known': sizes_known,
            'size_total': size_total,
            'app_id': app_id,
            'queues': None,
            'cancel_url': handler.request.get('cancel_url'),
            'has_namespace': has_namespace,
            'namespace': namespace,
            'xsrf_token': utils.CreateXsrfToken(XSRF_ACTION),
            'notreadonly_warning': notreadonly_warning,
            'blob_warning': blob_warning,
            'backup_name': 'datastore_backup_%s' % time.strftime('%Y_%m_%d')
        }
        utils.RenderToResponse(handler, 'confirm_backup.html', template_params)
Ejemplo n.º 11
0
def GetStatus(user):
  """Return the status of Snipper, according to the datastore write."""
  status = 'OK'
  help_msg = 'Snipper should be working properly.'
  datastore_write = capabilities.CapabilitySet('datastore_v3',
                                               capabilities=['write'])
  if not datastore_write.is_enabled():
    status = 'Read only'
    help_msg = 'App Engine is down for maintenance, please try again later.'
  return _STATUS_TEMPLATE % {'help': help_msg, 'status': status, 'user': user}
Ejemplo n.º 12
0
 def services(self):
     """Return the status and admin message list for all available services."""
     blobstore_api = capabilities.CapabilitySet('blobstore')
     datastore_read_api = capabilities.CapabilitySet('datastore_v3')
     datastore_write_api = capabilities.CapabilitySet(
         'datastore_v3', ['write'])
     mail_api = capabilities.CapabilitySet('mail')
     memcache_api = capabilities.CapabilitySet('memcache')
     taskqueue_api = capabilities.CapabilitySet('taskqueue')
     urlfetch_api = capabilities.CapabilitySet('urlfetch')
     return [
         {
             'blobstore': {
                 'enabled': blobstore_api.is_enabled(),
                 'message': blobstore_api.admin_message(),
             }
         },
         {
             'datastore_read': {
                 'enabled': datastore_read_api.is_enabled(),
                 'message': datastore_read_api.admin_message(),
             }
         },
         {
             'datastore_write': {
                 'enabled': datastore_write_api.is_enabled(),
                 'message': datastore_write_api.admin_message(),
             }
         },
         {
             'mail': {
                 'enabled': mail_api.is_enabled(),
                 'message': mail_api.admin_message(),
             }
         },
         {
             'memcache': {
                 'enabled': memcache_api.is_enabled(),
                 'message': memcache_api.admin_message(),
             }
         },
         {
             'taskqueue': {
                 'enabled': taskqueue_api.is_enabled(),
                 'message': taskqueue_api.admin_message(),
             }
         },
         {
             'urlfetch': {
                 'enabled': urlfetch_api.is_enabled(),
                 'message': urlfetch_api.admin_message(),
             }
         },
     ]
Ejemplo n.º 13
0
    def get(self):
        self.generate_manage_bar()
        if not capabilities.CapabilitySet('datastore_v3',
                                          ['write']).is_enabled():
            self.abort(500, "The datastore is down")

        self.template_vars[
            'MaxHomepageSlides'] = self.settings.MaxHomepageSlides
        self.template_vars['LinkPrefix'] = '/'.join(
            (os.environ['HTTP_HOST'], ))
        self.template_vars['form'] = self.generate_form(HomepageSlide_Form)
        self.template_vars['error_msg'] = self.session.get('new_slide_error')

        self.render_template("manage/homepage_slides/new_slide.html")
Ejemplo n.º 14
0
    def wsgi_app(environ, start_response):
        app = application(environ, start_response)
        if not capabilities.CapabilitySet('memcache').is_enabled():
            logging.error("Memcache service not available.")
            return app

        record = False
        path_info = os.environ['PATH_INFO']
        for re_obj in compiled_url_patterns:
            if re.match(re_obj, path_info):
                record = True
                break

        if record: recorder.trace()

        return app
  def testUnknownCall(self):
    expected_request = IsEnabledRequest()
    expected_request.package = 'datastore_v3'
    expected_request.capability.append('*')
    expected_request.call.append('ShipMeADVD')

    actual_response = IsEnabledResponse()
    actual_response.summary_status = IsEnabledResponse.ENABLED
    config = actual_response.config.add()
    config.package = 'datastore_v3'
    config.capability = '*'
    config.status = CapabilityConfig.ENABLED

    stub_map = MockCapabilityImpl(expected_request, actual_response)
    capability_set = capabilities.CapabilitySet(
        'datastore_v3', methods=['ShipMeADVD'], stub_map=stub_map)



    self.assertEqual(True, capability_set.is_enabled())
    self.assertEqual(actual_response, capability_set._get_status())
Ejemplo n.º 16
0
    def Render(cls, handler):
        """Rendering method that can be called by main.py.

    Args:
      handler: the webapp.RequestHandler invoking the method
    """
        backup_id = handler.request.get('backup_id')
        backup = db.get(backup_id) if backup_id else None
        notreadonly_warning = capabilities.CapabilitySet(
            'datastore_v3', capabilities=['write']).is_enabled()
        app_id = handler.request.get('app_id')
        template_params = {
            'form_target': DoBackupRestoreHandler.SUFFIX,
            'app_id': app_id,
            'queues': None,
            'cancel_url': handler.request.get('cancel_url'),
            'backup': backup,
            'xsrf_token': utils.CreateXsrfToken(XSRF_ACTION),
            'notreadonly_warning': notreadonly_warning
        }
        utils.RenderToResponse(handler, 'confirm_restore_from_backup.html',
                               template_params)
Ejemplo n.º 17
0
    def get(self):
        # データブックの名前を取得
        databook_name = get_databook_name(self.request.get('db'))
        # データブックの表示タイトルを取得
        databook_title = get_databook_title(self.request.get('db'))
        # 全文検索用インデックスの名前を取得
        databook_indexname = get_databook_indexname(self.request.get('db'))
        # 表示メッセージの初期化
        message_data = ''

        # 管理者ログインのチェック
        admin_login = False
        if users.is_current_user_admin():
            admin_login = True

        # 管理者ログイン中の表示
        admin_message = ''
        if users.get_current_user():
            if admin_login:
                admin_message = '(管理者としてログインしています)'
            else:
                admin_message = '(管理者ではありません)'

        # ログイン/ログアウトURL設定
        if users.get_current_user():
            login_url = users.create_logout_url(self.request.uri)
            login_text = '[ログアウト]'
        else:
            login_url = users.create_login_url(self.request.uri)
            # login_text = '[ログイン]'
            login_text = '[管理]'

        # 書き込み禁止の判定
        write_disabled_message = ''
        if not capabilities.CapabilitySet('datastore_v3',
                                          ['write']).is_enabled():
            write_disabled_message = '【現在書き込みは禁止しています】'

        # 全文検索の単語を取得
        search_flag = False
        search_count = 0
        search_word = self.request.get('word').strip()
        # 全文検索の単語の先頭が「=」のときは特別扱い
        show_all_flag = False
        show_offset = 0
        if search_word.startswith('='):
            i = 1
            while i < len(search_word):
                ch = search_word[i]
                # 「*」のときは表示フラグを無視して全て表示する
                if ch == '*':
                    i += 1
                    show_all_flag = True
                    continue
                # 数字のときは表示件数のオフセットとする
                if ch.isdigit():
                    i += 1
                    j = i - 1
                    while i < len(search_word):
                        ch = search_word[i]
                        if ch.isdigit():
                            i += 1
                            continue
                        break
                    k = i
                    if (k - j) > 5: k = j + 5
                    show_offset = int(search_word[j:k])
                    continue
                # その他のときは抜ける
                break
            search_word2 = search_word[i:]
        else:
            search_word2 = search_word
        # 全文検索の単語をチェック
        if search_word2:
            # 全文検索を行うとき
            articles = []
            # 検索結果を日付の降順でソートする指定
            expr_list = [
                search.SortExpression(
                    expression='date',
                    default_value=datetime.datetime.min,
                    direction=search.SortExpression.DESCENDING)
            ]
            # ソートオプションに設定する
            sort_opts = search.SortOptions(expressions=expr_list)
            # クエリーオプションに設定する
            # (表示件数指定、ソートオプション指定、検索結果はタイトルのみ取得)
            query_opts = search.QueryOptions(limit=mainpage_show_num,
                                             offset=show_offset,
                                             sort_options=sort_opts,
                                             returned_fields=['title'])
            try:
                # 単語とクエリーオプションを指定して全文検索実行
                query_obj = search.Query(query_string=search_word2,
                                         options=query_opts)
                search_results = search.Index(name=databook_indexname).search(
                    query=query_obj)
                # 検索結果から記事のタイトルを取得する
                req_titles = []
                for scored_doc in search_results:
                    req_titles.append(scored_doc.field('title').value)
                if len(req_titles) >= 1:
                    # 記事を検索(タイトルで表示件数まで)
                    if show_all_flag:
                        articles_query = Article.query(
                            Article.title.IN(req_titles),
                            ancestor=databook_key(databook_name)).order(
                                -Article.date)
                    else:
                        articles_query = Article.query(
                            Article.title.IN(req_titles),
                            Article.show_flag == 1,
                            ancestor=databook_key(databook_name)).order(
                                -Article.date)
                    articles = articles_query.fetch(mainpage_show_num)
            except (search.QueryError, search.InvalidRequest), e:
                # クエリーエラーのとき
                message_data = message_data + '(クエリーエラー:検索文字列に記号が含まれると発生することがあります)'
            search_flag = True
            search_count = len(articles)
Ejemplo n.º 18
0
    def post(self):
        # データブックの名前を取得
        databook_name = get_databook_name(self.request.get('db'))
        # 表示メッセージの初期化
        message_data = ''

        # 記事のタイトルをチェック
        req_title = self.request.get('title').strip()
        if not req_title:
            self.redirect(mainpage_url + '?' +
                          urllib.urlencode({'db': databook_name}))
            return

        # 管理者ログインのチェック
        admin_login = False
        if users.is_current_user_admin():
            admin_login = True

        # 書き込み禁止の判定
        write_disabled_message = ''
        if not capabilities.CapabilitySet('datastore_v3',
                                          ['write']).is_enabled():
            write_disabled_message = '【現在書き込みは禁止しています】'

        # 日時更新のチェック(デフォルトON)
        datechg_flag = 1
        if self.request.get('datechg') == '0':
            datechg_flag = 0

        # 記事を検索(タイトルで1件だけ)
        articles_query = Article.query(
            Article.title == req_title,
            ancestor=databook_key(databook_name)).order(-Article.date)
        articles = articles_query.fetch(1)
        # 記事が存在しなければ新規作成
        if len(articles) < 1:
            article = Article(parent=databook_key(databook_name))
            article.title = req_title
            article.author = ''
            article.content = ''
            article.source = ''
            article.date = datetime.datetime.now()
            article.bkup_authors = []
            article.bkup_contents = []
            article.bkup_sources = []
            article.bkup_dates = []
            article.bkup_lastupdate = datetime.datetime.min
            article.search_doc_id = ''
            article.show_flag = 1
        else:
            article = articles[0]
            # バックアップをロードするかのチェック
            req_bkup_sel = self.request.get('bkup_sel')
            if req_bkup_sel and req_bkup_sel.isdigit():
                req_bkup_no = int(req_bkup_sel) - 1
                if req_bkup_no >= 0 and req_bkup_no < len(article.bkup_dates):
                    article.author = article.bkup_authors[req_bkup_no]
                    article.content = article.bkup_contents[req_bkup_no]
                    article.source = article.bkup_sources[req_bkup_no]
                    # article.date = article.bkup_dates[req_bkup_no]
                    date_temp = article.bkup_dates[req_bkup_no]
                    date_temp = date_temp.replace(tzinfo=UTC()).astimezone(
                        JapanTZ())
                    message_data = message_data + '(「' + date_temp.strftime(
                        '%Y-%m-%d %H:%M:%S %Z') + '」の履歴をロードしました)'
            # 全文検索用ドキュメントの登録チェック
            if not article.search_doc_id:
                message_data = message_data + '(全文検索未登録)'

        # 文字コード変換(表示用)
        message_data = message_data.decode('utf-8')
        write_disabled_message = write_disabled_message.decode('utf-8')

        # ローカル日時変換(表示用)
        article.date = article.date.replace(tzinfo=UTC()).astimezone(JapanTZ())
        for i in range(len(article.bkup_dates)):
            article.bkup_dates[i] = article.bkup_dates[i].replace(
                tzinfo=UTC()).astimezone(JapanTZ())

        # 編集ページのテンプレートに記事データを埋め込んで表示
        template = jinja_environment.get_template(editpage_html)
        self.response.out.write(
            template.render(databook_name=databook_name,
                            article=article,
                            update_url=update_url,
                            runpage_url=runpage_url,
                            mainpage_url=mainpage_url,
                            editpage_url=editpage_url,
                            message_data=message_data,
                            admin_login=admin_login,
                            datechg_flag=datechg_flag,
                            write_disabled_message=write_disabled_message))
Ejemplo n.º 19
0
FLAG_TYPE_MASK = 7
FLAG_COMPRESSED = 1 << 3





TYPE_STR = 0
TYPE_UNICODE = 1
TYPE_PICKLED = 2
TYPE_INT = 3
TYPE_LONG = 4
TYPE_BOOL = 5


CAPABILITY = capabilities.CapabilitySet('memcache')


def _add_name_space(message, namespace=None):
  """Populate the name_space field in a messagecol buffer.

  Args:
    message: A messagecol buffer supporting the set_name_space() operation.
    namespace: The name of the namespace part. If None, use the
      default namespace. The empty namespace (i.e. '') will clear
      the name_space field.
  """
  if namespace is None:
    namespace = namespace_manager.get_namespace()
  if not namespace:
    message.clear_name_space()
 def get(self):
   for c in CAPABILITIES:
     if capabilities.CapabilitySet(c).is_enabled():
       self.response.out.write("<br />capability %s is enabled!" % c)
     else:
       self.response.out.write("<br />capability %s is disabled!" % c)
Ejemplo n.º 21
0
    def post(self):
        # データブックの名前を取得
        databook_name = get_databook_name(self.request.get('db'))
        # 全文検索用インデックスの名前を取得
        databook_indexname = get_databook_indexname(self.request.get('db'))
        # 表示メッセージの初期化
        message_data = ''

        # 記事のタイトルをチェック
        req_title = self.request.get('title').strip()
        if not req_title:
            self.redirect(mainpage_url + '?' +
                          urllib.urlencode({'db': databook_name}))
            return

        # 管理者ログインのチェック
        admin_login = False
        if users.is_current_user_admin():
            admin_login = True

        # 書き込み禁止の判定
        write_enabled = True
        write_disabled_message = ''
        if not capabilities.CapabilitySet('datastore_v3',
                                          ['write']).is_enabled():
            write_enabled = False
            write_disabled_message = '【現在書き込みは禁止しています】'

        # 日時更新のチェック(デフォルトOFF)
        datechg_flag = 0
        if self.request.get('datechg') == '1':
            datechg_flag = 1

        # 記事を検索(タイトルで1件だけ)
        articles_query = Article.query(
            Article.title == req_title,
            ancestor=databook_key(databook_name)).order(-Article.date)
        articles = articles_query.fetch(1)
        # 記事が存在しなければ新規作成
        if len(articles) < 1:
            article = Article(parent=databook_key(databook_name))
            article.title = req_title
            article.author = ''
            article.content = ''
            article.source = ''
            article.date = datetime.datetime.now()
            article.bkup_authors = []
            article.bkup_contents = []
            article.bkup_sources = []
            article.bkup_dates = []
            article.bkup_lastupdate = datetime.datetime.min
            article.search_doc_id = ''
            article.show_flag = 1
        else:
            article = articles[0]

        # ログインユーザー名をセット(今回未使用)
        # if users.get_current_user():
        #     article.author = users.get_current_user().nickname()

        # 送信されたデータを記事に設定
        if self.request.get('delete') != '1' and self.request.get(
                'rename') != '1':
            article.author = self.request.get('author').strip()
            article.content = self.request.get('content').strip()
            article.source = self.request.get('source')
            if datechg_flag == 1:
                article.date = datetime.datetime.now()

        # 記事の非表示(保守用)
        if write_enabled and article.author.startswith('=hide'):
            article.show_flag = 0
        else:
            article.show_flag = 1

        # 記事の削除(保守用)
        # if article.author.startswith('=delete'):
        if write_enabled and self.request.get('delete') == '1':
            if admin_login and article.bkup_dates:
                # (関連する全文検索用ドキュメントがあればそれも削除)
                if article.search_doc_id:
                    search.Index(name=databook_indexname).delete(
                        article.search_doc_id)
                article.key.delete()
            self.redirect(mainpage_url + '?' +
                          urllib.urlencode({'db': databook_name}))
            return

        # 全文検索用ドキュメントの個別削除(保守用)
        if write_enabled and article.author.startswith('=index_delete'):
            if admin_login:
                doc_id = article.content
                if doc_id:
                    search.Index(name=databook_indexname).delete(doc_id)
            self.redirect(mainpage_url + '?' +
                          urllib.urlencode({'db': databook_name}))
            return

        # 全文検索用ドキュメントの全削除(保守用)
        if write_enabled and article.author.startswith('=all_index_delete'):
            if admin_login:
                search_index = search.Index(name=databook_indexname)
                while True:
                    doc_ids = [
                        doc.doc_id
                        for doc in search_index.get_range(ids_only=True)
                    ]
                    if not doc_ids:
                        break
                    search_index.delete(doc_ids)
            self.redirect(mainpage_url + '?' +
                          urllib.urlencode({'db': databook_name}))
            return

        # 記事のタイトル変更(保守用)
        rename_flag = 0
        if write_enabled and self.request.get('rename') == '1':
            new_title = self.request.get('newtitle').strip()
            if admin_login and new_title and new_title != article.title and article.bkup_dates:
                # 記事を検索(新タイトルで1件だけ)
                articles_query = Article.query(
                    Article.title == new_title,
                    ancestor=databook_key(databook_name)).order(-Article.date)
                articles_temp = articles_query.fetch(1)
                # 記事が存在しなければ、タイトルを変更できる
                if len(articles_temp) < 1:
                    article.title = new_title
                    rename_flag = 1
                else:
                    rename_flag = 2
            else:
                rename_flag = 2

        # バックアップの保存
        # (10分以内のときは、バックアップを追加しないで上書きとする)
        if write_enabled and rename_flag == 0:
            time_diff_minutes = -1
            if article.bkup_lastupdate:
                time_diff = datetime.datetime.now() - article.bkup_lastupdate
                time_diff_minutes = time_diff.days * 24 * 60 + time_diff.seconds / 60
            if time_diff_minutes >= 0 and time_diff_minutes <= backup_time:
                # 最新のバックアップを上書き
                article.bkup_authors[0] = article.author
                article.bkup_contents[0] = article.content
                article.bkup_sources[0] = article.source
                article.bkup_dates[0] = article.date
            else:
                # バックアップを追加(最大10件)
                article.bkup_authors.insert(0, article.author)
                article.bkup_contents.insert(0, article.content)
                article.bkup_sources.insert(0, article.source)
                article.bkup_dates.insert(0, article.date)
                if len(article.bkup_dates) > backup_num:
                    article.bkup_authors = article.bkup_authors[:backup_num]
                    article.bkup_contents = article.bkup_contents[:backup_num]
                    article.bkup_sources = article.bkup_sources[:backup_num]
                    article.bkup_dates = article.bkup_dates[:backup_num]
                article.bkup_lastupdate = datetime.datetime.now()

        # 全文検索用ドキュメントを登録する
        if write_enabled and (rename_flag == 0 or rename_flag == 1):
            date_str = article.date.replace(tzinfo=UTC()).astimezone(
                JapanTZ()).strftime('%Y-%m-%d %H:%M:%S %Z')
            doc_content = article.title + ' ' + article.author + ' ' + article.content + ' ' + date_str
            if article.search_doc_id:
                # すでに登録されていれば上書き
                doc = search.Document(doc_id=article.search_doc_id,
                                      fields=[
                                          search.TextField(name='title',
                                                           value=article.title,
                                                           language='ja'),
                                          search.TextField(name='content',
                                                           value=doc_content,
                                                           language='ja'),
                                          search.DateField(name='date',
                                                           value=article.date)
                                      ])
                put_result = search.Index(name=databook_indexname).put(doc)
            else:
                # 登録されていなければ新規作成(このときドキュメントIDを記憶しておく)
                doc = search.Document(fields=[
                    search.TextField(
                        name='title', value=article.title, language='ja'),
                    search.TextField(
                        name='content', value=doc_content, language='ja'),
                    search.DateField(name='date', value=article.date)
                ])
                put_result = search.Index(name=databook_indexname).put(doc)
                # ↓これではドキュメントIDとれないので注意。putの戻り値から取得する必要がある
                # article.search_doc_id = doc.doc_id
                article.search_doc_id = put_result[0].id

        # 記事をデータブックに登録
        if write_enabled:
            if rename_flag == 0:
                article.put()
                message_data = message_data + '(セーブしました)'
            elif rename_flag == 1:
                article.put()
                message_data = message_data + '(タイトルを変更しました)'
            else:
                message_data = message_data + '(タイトルを変更できません(名称が不正もしくは同名が存在する等))'
        else:
            message_data = message_data + '(書き込みが禁止されています)'

        # # メインページに戻る
        # self.redirect(mainpage_url + '?' + urllib.urlencode({'db': databook_name}))

        # 文字コード変換(表示用)
        message_data = message_data.decode('utf-8')
        write_disabled_message = write_disabled_message.decode('utf-8')

        # ローカル日時変換(表示用)
        article.date = article.date.replace(tzinfo=UTC()).astimezone(JapanTZ())
        for i in range(len(article.bkup_dates)):
            article.bkup_dates[i] = article.bkup_dates[i].replace(
                tzinfo=UTC()).astimezone(JapanTZ())

        # 編集ページのテンプレートに記事データを埋め込んで表示
        template = jinja_environment.get_template(editpage_html)
        self.response.out.write(
            template.render(databook_name=databook_name,
                            article=article,
                            update_url=update_url,
                            runpage_url=runpage_url,
                            mainpage_url=mainpage_url,
                            editpage_url=editpage_url,
                            message_data=message_data,
                            admin_login=admin_login,
                            datechg_flag=datechg_flag,
                            write_disabled_message=write_disabled_message))