예제 #1
0
파일: image.py 프로젝트: dNG-git/pas_upnp
    def execute_resource_thumbnail(self):
        """
Action for "resource_thumbnail"

:since: v0.2.00
        """

        rid = InputFilter.filter_control_chars(self.request.get_dsd("urid", ""))
        mimetype = InputFilter.filter_control_chars(self.request.get_dsd("umimetype", ""))
        width = InputFilter.filter_int(self.request.get_dsd("uwidth", 0))
        height = InputFilter.filter_int(self.request.get_dsd("uheight", 0))
        depth = InputFilter.filter_int(self.request.get_dsd("udepth", 24))

        if (width < 1 or height < 1): raise TranslatableException("pas_http_core_400", 400)

        resource = Resource.load_cds_id(rid, self.get_client_settings())

        if (resource is not None and resource.is_supported("thumbnail_source_vfs_url")):
            Image._add_dlna_headers(self.request, self.response, resource)

            self._stream_transformed_vfs_url(resource.get_thumbnail_source_vfs_url(),
                                             mimetype,
                                             width,
                                             height,
                                             depth,
                                             AbstractImage.RESIZE_SCALED_CROP
                                            )
예제 #2
0
파일: image.py 프로젝트: dNG-git/pas_upnp
    def execute_device_icon(self):
        """
Action for "device_icon"

:since: v0.2.00
        """

        usn = InputFilter.filter_control_chars(self.request.get_dsd("uusn", ""))
        mimetype = InputFilter.filter_control_chars(self.request.get_dsd("umimetype", ""))
        width = InputFilter.filter_int(self.request.get_dsd("uwidth", 0))
        height = InputFilter.filter_int(self.request.get_dsd("uheight", 0))
        depth = InputFilter.filter_int(self.request.get_dsd("udepth", 24))

        if (width < 1 or height < 1): raise TranslatableException("pas_http_core_400", 400)

        upnp_control_point = ControlPoint.get_instance()

        if (usn != ""):
            device = upnp_control_point.get_device(Device.get_identifier(usn))
            if (device is None): raise TranslatableException("pas_http_core_400", 400)
            file_path_name = device.get_icon_file_path_name()

            if (file_path_name is not None):
                self._stream_transformed_vfs_url("file:///{0}".format(quote(file_path_name, "/")),
                                                 mimetype,
                                                 width,
                                                 height,
                                                 depth
                                                )
예제 #3
0
	def execute_delete(self):
	#
		"""
Action for "delete"

:since: v0.1.00
		"""

		form_field_id = InputFilter.filter_control_chars(self.request.get_dsd("oform_field_id", "")).strip()
		form_field_entry_position = InputFilter.filter_int(self.request.get_dsd("oform_field_entry_position", -1))

		if (form_field_entry_position > -1):
		#
			form_store = self._get_form_store()
			form_store_dict = form_store.get_value_dict()
			form_store_field_id = "form_api_dynamic_{0}".format(form_field_id)

			entry_list = form_store_dict.get(form_store_field_id, [ ])
			if (len(entry_list) > form_field_entry_position): del(entry_list[form_field_entry_position])

			form_store_dict[form_store_field_id] = entry_list
			form_store.set_value_dict(form_store_dict)
		#

		redirect_request = PredefinedHttpRequest()
		redirect_request.set_module(self.request.get_module())
		redirect_request.set_service(self.request.get_service())
		redirect_request.set_action("get")
		redirect_request.set_dsd_dict(self.request.get_dsd_dict())

		self.request.redirect(redirect_request)
예제 #4
0
파일: index.py 프로젝트: dNG-git/mp_core
    def execute_list_root_containers(self):
        """
Action for "list_root_containers"

:since: v0.2.00
        """

        page = InputFilter.filter_int(self.request.get_dsd("mpage", 1))

        L10n.init("mp_core")

        session = (self.request.get_session() if (self.request.is_supported("session")) else None)
        user_profile = (None if (session is None) else session.get_user_profile())

        if (user_profile is None
            or (not user_profile.is_type("ad"))
           ): raise TranslatableError("core_access_denied", 403)

        if (self.response.is_supported("html_css_files")): self.response.add_theme_css_file("mini_default_sprite.min.css")

        Link.set_store("servicemenu",
                       (Link.TYPE_RELATIVE_URL | Link.TYPE_JS_REQUIRED),
                       L10n.get("mp_core_root_container_new"),
                       { "m": "mp", "s": "root_container", "a": "new" },
                       icon = "mini-default-option",
                       priority = 3
                      )

        content = { "title": L10n.get("mp_core_root_container_list"),
                    "entries": MpEntry.get_root_containers_count(),
                    "page": page
                  }

        self.response.init()
        self.response.set_title(content['title'])
        self.response.add_oset_content("mp.container", content)
예제 #5
0
    def execute_error(self):
        """
Action for "error"

:since: v1.0.0
        """

        code = InputFilter.filter_int(self.request.get_parameter("code", "500"))

        if (L10n.is_defined("errors_pas_http_core_{0:d}".format(code))):
            if (self.response.is_supported("headers")):
                self.response.set_header("HTTP",
                                         ("HTTP/2.0 {0:d} {1}".format(code, self.error_messages[code])
                                          if (code in self.error_messages) else
                                          "HTTP/2.0 500 Internal Server Error"
                                         ),
                                         True
                                        )
            #

            self.response.handle_error("pas_http_core_{0:d}".format(code))
        else:
            if (self.response.is_supported("headers")): self.response.set_header("HTTP", "HTTP/2.0 500 Internal Server Error", True)
            self.response.handle_critical_error("core_unknown_error")
예제 #6
0
    def execute_list(self):
        """
Action for "list"

:since: v0.2.00
        """

        page = InputFilter.filter_int(self.request.get_dsd("tpage", 1))

        L10n.init("pas_http_tasks")

        session = (self.request.get_session() if (self.request.is_supported("session")) else None)
        user_profile = (None if (session is None) else session.get_user_profile())

        if (user_profile is None or (not user_profile.is_type("ad"))): raise TranslatableError("core_access_denied", 403)

        tasks_count = DatabaseTask.get_list_count()

        hookable_settings = HookableSettings("dNG.pas.http.tasks.List.getLimit")
        limit = hookable_settings.get("pas_http_tasks_list_limit", 40)

        pages = (1 if (tasks_count == 0) else ceil(float(tasks_count) / limit))
        offset = (0 if (page < 1 or page > pages) else (page - 1) * limit)

        tasks_list = DatabaseTask.load_list(offset = offset, limit = limit)

        content = { "title": L10n.get("pas_http_tasks_database_entries_list"), "tasks_count": tasks_count }

        if (tasks_count > 0):
            details_renderer_attributes = { "type": CustomTable.COLUMN_RENDERER_CALLBACK_OSET,
                                            "callback": self._get_details_cell_content,
                                            "oset_template_name": "tasks.details_column",
                                            "oset_row_attributes": [ "id", "tid", "name", "hook", "time_updated" ]
                                          }

            status_renderer_attributes = { "type": CustomTable.COLUMN_RENDERER_CALLBACK_OSET,
                                           "callback": self._get_status_cell_content,
                                           "oset_template_name": "tasks.status_column",
                                           "oset_row_attributes": [ "status", "time_started", "timeout" ]
                                         }

            time_scheduled_renderer_attributes = { "type": CustomTable.COLUMN_RENDERER_OSET,
                                                   "oset_template_name": "tasks.time_scheduled_column"
                                                 }

            table = CustomTable()
            table.add_column("details", L10n.get("pas_http_tasks_entry_details"), 50, renderer = details_renderer_attributes)
            table.add_column("status", L10n.get("pas_http_tasks_entry_status"), 30, renderer = status_renderer_attributes)
            table.add_column("time_scheduled", L10n.get("pas_http_tasks_entry_time_scheduled"), 20, renderer = time_scheduled_renderer_attributes)

            table.set_limit(limit)
            table.set_row_count(tasks_count)

            for task in tasks_list:
                task_data = task.get_data_attributes("id",
                                                     "tid",
                                                     "name",
                                                     "status",
                                                     "hook",
                                                     "time_started",
                                                     "time_scheduled",
                                                     "time_updated",
                                                     "timeout"
                                                    )

                table.add_row(**task_data)
            #

            content['tasks'] = { "object": table,
                                 "dsd_page_key": "tpage",
                                 "page": page
                               }
        #

        self.response.init(True)
        self.response.set_expires_relative(+5)
        self.response.set_title(content['title'])

        self.response.add_oset_content("tasks.list", content)
예제 #7
0
    def execute_list(self):
        """
Action for "list"

:since: v0.2.00
        """

        cid = InputFilter.filter_file_path(self.request.get_dsd("ccid", ""))
        page = InputFilter.filter_int(self.request.get_dsd("cpage", 1))

        if (cid == ""): cid = Settings.get("pas_http_contentor_category_default", "")

        L10n.init("pas_http_contentor")
        L10n.init("pas_http_datalinker")

        try: category = Category.load_id(cid)
        except NothingMatchedException as handled_exception: raise TranslatableError("pas_http_contentor_cid_invalid", 404, _exception = handled_exception)

        session = (self.request.get_session() if (self.request.is_supported("session")) else None)
        if (session is not None): category.set_permission_session(session)

        if (not category.is_readable()):
            if (session is None or session.get_user_profile() is None): raise TranslatableError("pas_http_contentor_cid_invalid", 404)
            else: raise TranslatableError("core_access_denied", 403)
        #

        if (self.response.is_supported("html_css_files")): self.response.add_theme_css_file("mini_default_sprite.min.css")

        if (category.is_writable()):
            Link.set_store("servicemenu",
                           (Link.TYPE_RELATIVE_URL | Link.TYPE_JS_REQUIRED),
                           L10n.get("pas_http_contentor_document_new"),
                           { "m": "contentor", "s": "document", "a": "new", "dsd": { "ccid": cid } },
                           icon = "mini-default-option",
                           priority = 3
                          )
        #

        if (category.is_manageable()):
            Link.set_store("servicemenu",
                           (Link.TYPE_RELATIVE_URL | Link.TYPE_JS_REQUIRED),
                           L10n.get("pas_http_contentor_category_manage"),
                           { "m": "contentor", "s": "category", "a": "manage", "dsd": { "ccid": cid } },
                           icon = "mini-default-option",
                           priority = 3
                          )
        #

        category_data = category.get_data_attributes("id", "id_main", "title", "time_sortable", "sub_entries", "entry_type")

        content = { "id": category_data['id'],
                    "title": category_data['title'],
                    "time": category_data['time_sortable'],
                    "sub_entries_count": category_data['sub_entries']
                  }

        if (category_data['sub_entries'] > 0): content['sub_entries'] = { "id": category_data['id'], "page": page }

        category_parent = category.load_parent()

        if (category_parent is not None
            and ((not isinstance(category_parent, OwnableInstance))
                 or category_parent.is_readable_for_session_user(session)
                )
           ):
            category_parent_data = category_parent.get_data_attributes("id", "id_main", "title")

            if (category_parent_data['id'] != cid):
                content['parent'] = { "id": category_parent_data['id'],
                                      "main_id": category_parent_data['id_main'],
                                      "title": category_parent_data['title']
                                    }
            #
        #

        self.response.init(True)
        self.response.set_expires_relative(+15)
        self.response.set_title(category_data['title'])
        self.response.add_oset_content("contentor.{0}_list".format(category_data['entry_type']), content)

        if (self.response.is_supported("html_canonical_url")):
            link_parameters = { "__virtual__": "/contentor/view",
                                "dsd": { "ccid": cid, "cpage": page }
                              }

            self.response.set_html_canonical_url(Link().build_url(Link.TYPE_VIRTUAL_PATH, link_parameters))
예제 #8
0
	def execute_list(self):
	#
		"""
Action for "list"

:since: v0.1.00
		"""

		lid = InputFilter.filter_file_path(self.request.get_dsd("dlid", ""))
		page = InputFilter.filter_int(self.request.get_dsd("dpage", 1))
		sort_value = InputFilter.filter_control_chars(self.request.get_dsd("dsort", ""))

		if (lid == ""): lid = Settings.get("pas_http_discuss_list_default", "")

		L10n.init("pas_http_datalinker")
		L10n.init("pas_http_discuss")
		L10n.init("pas_http_subscription")

		try: _list = List.load_id(lid)
		except NothingMatchedException as handled_exception: raise TranslatableError("pas_http_discuss_lid_invalid", 404, _exception = handled_exception)

		session = (self.request.get_session() if (self.request.is_supported("session")) else None)
		if (session is not None): _list.set_permission_session(session)

		if (not _list.is_readable()):
		#
			if (session is None or session.get_user_profile() is None): raise TranslatableError("pas_http_discuss_lid_invalid", 404)
			else: raise TranslatableError("core_access_denied", 403)
		#

		if (self.response.is_supported("html_css_files")): self.response.add_theme_css_file("mini_default_sprite.min.css")

		is_hybrid_list = _list.is_hybrid_list()

		if (is_hybrid_list and _list.is_writable()):
		#
			Link.set_store("servicemenu",
			               (Link.TYPE_RELATIVE_URL | Link.TYPE_JS_REQUIRED),
			               L10n.get("pas_http_discuss_topic_new"),
			               { "m": "discuss", "s": "topic", "a": "new", "dsd": { "dlid": lid } },
			               icon = "mini-default-option",
			               priority = 3
			              )
		#

		subscription_handler = (_list.get_subscription_handler() if (is_hybrid_list) else None)

		if (subscription_handler is not None and subscription_handler.is_subscribable_for_session_user(session)):
		#
			source = "m=discuss;dsd=dlid+{0}++dpage+{1}".format(lid, page)
			subscription_dsd = { "oid": lid, "source": source }

			if (subscription_handler.is_subscribed_by_session_user(session)):
			#
				Link.set_store("servicemenu",
				               Link.TYPE_RELATIVE_URL,
				               L10n.get("pas_http_subscription_unsubscribe"),
				               { "m": "subscription", "s": "datalinker", "a": "unsubscribe", "dsd": subscription_dsd },
				               icon = "mini-default-option",
				               priority = 3
				              )
			#
			else:
			#
				Link.set_store("servicemenu",
				               Link.TYPE_RELATIVE_URL,
				               L10n.get("pas_http_subscription_subscribe"),
				               { "m": "subscription", "s": "datalinker", "a": "subscribe", "dsd": subscription_dsd },
				               icon = "mini-default-option",
				               priority = 3
				              )
			#
		#

		if (_list.is_manageable()):
		#
			Link.set_store("servicemenu",
			               (Link.TYPE_RELATIVE_URL | Link.TYPE_JS_REQUIRED),
			               L10n.get("pas_http_discuss_list_manage"),
			               { "m": "discuss", "s": "list", "a": "manage", "dsd": { "dlid": lid } },
			               icon = "mini-default-option",
			               priority = 3
			              )
		#

		list_data = _list.get_data_attributes("id",
		                                      "id_main",
		                                      "title",
		                                      "time_sortable",
		                                      "sub_entries",
		                                      "hybrid_list",
		                                      "description",
		                                      "topics",
		                                      "posts"
		                                     )

		content = { "id": list_data['id'],
		            "title": list_data['title'],
		            "description": list_data['description'],
		            "time": list_data['time_sortable'],
		            "topics": _list.get_total_topics_count(),
		            "posts": _list.get_total_posts_count()
		          }

		if (list_data['sub_entries'] > 0):
		#
			entry_renderer_attributes = { "type": DataLinkerTable.COLUMN_RENDERER_OSET,
			                              "oset_template_name": "discuss.list_column",
			                              "oset_row_attributes": [ "id", "title", "description" ]
			                            }

			latest_post_renderer_attributes = { "type": DataLinkerTable.COLUMN_RENDERER_OSET,
			                                    "oset_template_name": "discuss.latest_post_column",
			                                    "oset_row_attributes": [ "latest_timestamp",
			                                                             "latest_topic_id",
			                                                             "latest_author_id",
			                                                             "latest_preview"
			                                                           ]
			                                  }

			table = DataLinkerTable(_list)

			table.add_column("entry",
			                 L10n.get("pas_http_discuss_list"),
			                 30,
			                 sort_key = "title",
			                 renderer = entry_renderer_attributes
			                )

			table.add_column("total_topics",
			                 L10n.get("pas_http_discuss_topics"),
			                 10,
			                 renderer = { "type": DataLinkerTable.COLUMN_RENDERER_SAFE_CONTENT,
			                              "css_text_align": "center"
			                            }
			                )

			table.add_column("total_posts",
			                 L10n.get("pas_http_discuss_posts"),
			                 10,
			                 renderer = { "type": DataLinkerTable.COLUMN_RENDERER_SAFE_CONTENT,
			                              "css_text_align": "center"
			                            }
			                )

			table.add_column("latest_post",
			                 L10n.get("pas_http_discuss_latest_post"),
			                 50,
			                 renderer = latest_post_renderer_attributes
			                )

			table.disable_sort("total_topics", "total_posts", "latest_post")
			table.set_limit(15)

			hookable_settings = HookableSettings("dNG.pas.http.discuss.List.getLimit",
			                                     id = list_data['id']
			                                    )

			limit = hookable_settings.get("pas_http_discuss_list_limit", 20)

			content['sub_entries'] = { "object": table }

			if (not is_hybrid_list):
			#
				content['sub_entries']['dsd_page_key'] = "dpage"
				content['sub_entries']['page'] = page
				table.set_limit(limit)

				content['sub_entries']['dsd_sort_key'] = "dsort"
				content['sub_entries']['sort_value'] = sort_value
			#
		#

		if (is_hybrid_list and list_data['topics'] > 0):
		#
			topic_renderer_attributes = { "type": DataLinkerTable.COLUMN_RENDERER_OSET,
			                              "oset_template_name": "discuss.topic_column",
			                              "oset_row_attributes": [ "id", "title", "description" ]
			                            }

			latest_post_renderer_attributes = { "type": DataLinkerTable.COLUMN_RENDERER_OSET,
			                                    "oset_template_name": "discuss.last_post_column",
			                                    "oset_row_attributes": [ "time_sortable",
			                                                             "last_id_author",
			                                                             "last_preview"
			                                                           ]
			                                  }

			table = DataLinkerTable(_list)

			table.add_column("topic",
			                 L10n.get("pas_http_discuss_topic"),
			                 40,
			                 sort_key = "title",
			                 renderer = topic_renderer_attributes
			                )

			table.add_column("posts",
			                 L10n.get("pas_http_discuss_posts"),
			                 10,
			                 renderer = { "type": DataLinkerTable.COLUMN_RENDERER_SAFE_CONTENT,
			                              "css_text_align": "center"
			                            }
			                )

			table.add_column("latest_post",
			                 L10n.get("pas_http_discuss_latest_post"),
			                 50,
			                 sort_key = "time_sortable",
			                 renderer = latest_post_renderer_attributes
			                )

			table.set_limit(15)
			table.set_sort_context("DiscussTopic")
			table.set_source_callbacks(_list.get_topics, _list.get_topics_count)

			content['topic_entries'] = { "object": table,
			                             "dsd_page_key": "dpage",
			                             "page": page,
			                             "dsd_sort_key": "dsort",
			                             "sort_value": sort_value
			                           }
		#

		list_parent = _list.load_parent()

		if (list_parent is not None
		    and ((not isinstance(list_parent, OwnableInstance))
		         or list_parent.is_readable_for_session_user(session)
		        )
		   ):
		#
			list_parent_data = list_parent.get_data_attributes("id", "id_main", "title")

			if (list_parent_data['id'] != lid):
			#
				content['parent'] = { "id": list_parent_data['id'],
				                      "main_id": list_parent_data['id_main'],
				                      "title": list_parent_data['title']
				                    }
			#
		#

		self.response.init(True)
		self.response.set_expires_relative(+15)
		self.response.set_title(list_data['title'])

		self.response.add_oset_content(("discuss.hybrid_list" if (is_hybrid_list) else "discuss.list"),
		                               content
		                              )

		if (self.response.is_supported("html_canonical_url")):
		#
			link_parameters = { "__virtual__": "/discuss/view/list",
			                    "dsd": { "dlid": lid, "dpage": page, "dsort": sort_value }
			                  }

			self.response.set_html_canonical_url(Link().build_url(Link.TYPE_VIRTUAL_PATH, link_parameters))
예제 #9
0
	def execute_topic(self):
	#
		"""
Action for "topic"

:since: v0.1.00
		"""

		tid = InputFilter.filter_file_path(self.request.get_dsd("dtid", ""))
		page = InputFilter.filter_int(self.request.get_dsd("dpage", 1))

		L10n.init("pas_http_datalinker")
		L10n.init("pas_http_discuss")

		try: topic = Topic.load_id(tid)
		except NothingMatchedException as handled_exception: raise TranslatableError("pas_http_discuss_tid_invalid", 404, _exception = handled_exception)

		session = (self.request.get_session() if (self.request.is_supported("session")) else None)
		if (session is not None): topic.set_permission_session(session)

		if (not topic.is_readable()):
		#
			if (session is None or session.get_user_profile() is None): raise TranslatableError("pas_http_discuss_tid_invalid", 404)
			else: raise TranslatableError("core_access_denied", 403)
		#

		if (self.response.is_supported("html_css_files")): self.response.add_theme_css_file("mini_default_sprite.min.css")

		if (topic.is_writable()):
		#
			Link.set_store("servicemenu",
			               (Link.TYPE_RELATIVE_URL | Link.TYPE_JS_REQUIRED),
			               L10n.get("pas_http_discuss_post_new"),
			               { "m": "discuss", "s": "post", "a": "new", "dsd": { "dtid": tid } },
			               icon = "mini-default-option",
			               priority = 3
			              )
		#

		subscription_handler = topic.get_subscription_handler()

		if (subscription_handler is not None and subscription_handler.is_subscribable_for_session_user(session)):
		#
			if (subscription_handler.is_subscribed_by_session_user(session)):
			#
				Link.set_store("servicemenu",
				               Link.TYPE_RELATIVE_URL,
				               L10n.get("pas_http_discuss_topic_unsubscribe"),
				               { "m": "discuss", "s": "topic_subscription", "a": "unsubscribe", "dsd": { "dtid": tid } },
				               icon = "mini-default-option",
				               priority = 3
				              )
			#
			else:
			#
				Link.set_store("servicemenu",
				               Link.TYPE_RELATIVE_URL,
				               L10n.get("pas_http_discuss_topic_subscribe"),
				               { "m": "discuss", "s": "topic_subscription", "a": "subscribe", "dsd": { "dtid": tid } },
				               icon = "mini-default-option",
				               priority = 3
				              )
			#
		#

		topic_parent = topic.load_parent()
		is_list = isinstance(topic_parent, List)

		if (isinstance(topic_parent, OwnableInstance)):
		#
			if (not topic_parent.is_readable_for_session_user(session)): raise TranslatableError("core_access_denied", 403)

			if (topic_parent.is_writable_for_session_user(session)):
			#
				topic_parent_id = topic_parent.get_id()

				dsd_parameters = ({ "dlid": topic_parent_id }
				                  if (is_list) else
				                  { "doid": tid }
				                 )

				Link.set_store("servicemenu",
				               (Link.TYPE_RELATIVE_URL | Link.TYPE_JS_REQUIRED),
				               L10n.get("pas_http_discuss_topic_new"),
				               { "m": "discuss", "s": "topic", "a": "new", "dsd": dsd_parameters },
				               icon = "mini-default-option",
				               priority = 3
				              )
			#

			if (is_list and topic_parent.is_manageable_for_session_user(session)):
			#
				Link.set_store("servicemenu",
				               (Link.TYPE_RELATIVE_URL | Link.TYPE_JS_REQUIRED),
				               L10n.get("pas_http_discuss_list_manage"),
				               { "m": "discuss", "s": "list", "a": "manage", "dsd": { "dlid": topic_parent_id } },
				               icon = "mini-default-option",
				               priority = 3
				              )
			#
		#

		topic_data = topic.get_data_attributes("id",
		                                       "id_main",
		                                       "title",
		                                       "time_sortable",
		                                       "sub_entries",
		                                       "sub_entries_type",
		                                       "author_id",
		                                       "author_ip",
		                                       "posts",
		                                       "description",
		                                       "time_published"
		                                      )

		content = { "id": topic_data['id'],
		            "title": topic_data['title'],
		            "author": { "id": topic_data['author_id'], "ip": topic_data['author_ip'] },
		            "posts": topic_data['posts'],
		            "description": topic_data['description'],
		            "time_published": topic_data['time_published'],
		            "post_entries": { "id": topic_data['id'], "main_id": topic_data['id_main'], "page": page }
		          }

		if (topic_data['time_published'] != topic_data['time_sortable']):
		#
			content['last_timestamp'] = topic_data['time_sortable']
		#

		if (topic_data['sub_entries'] > 0):
		#
			content['sub_entries'] = { "type": topic_data['sub_entries_type'], "id": topic_data['id'] }
		#

		if (topic_parent is not None
		    and ((not isinstance(topic_parent, OwnableInstance))
		         or topic_parent.is_readable_for_session_user(session)
		        )
		   ):
		#
			topic_parent_data = topic_parent.get_data_attributes("id", "id_main", "title")

			content['parent'] = { "id": topic_parent_data['id'],
			                      "main_id": topic_parent_data['id_main'],
			                      "title": topic_parent_data['title']
			                    }
		#

		self.response.init(True)
		self.response.set_title(topic_data['title'])
		self.response.set_expires_relative(+15)
		self.response.set_last_modified(topic_data['time_sortable'])
		self.response.add_oset_content("discuss.topic", content)

		if (self.response.is_supported("html_canonical_url")):
		#
			link_parameters = { "__virtual__": "/discuss/view/topic",
			                    "dsd": { "dtid": tid, "dpage": page }
			                  }

			self.response.set_html_canonical_url(Link().build_url(Link.TYPE_VIRTUAL_PATH, link_parameters))
		#

		if (self.response.is_supported("html_page_description")
		    and topic_data['description'] != ""
		   ): self.response.set_html_page_description(topic_data['description'])
	#
#

##j## EOF