mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-11 06:18:09 +00:00
typing
This commit is contained in:
@@ -34,13 +34,14 @@ class Login(bpy.types.Operator):
|
|||||||
bl_options = {"REGISTER", "UNDO"}
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
token = api.login(context.scene.CoveToolProperties.username, context.scene.CoveToolProperties.password)
|
props = tool.Blender.get_covetool_props()
|
||||||
|
token = api.login(props.username, props.password)
|
||||||
if token:
|
if token:
|
||||||
context.scene.CoveToolProperties.token = token
|
props.token = token
|
||||||
|
|
||||||
projects = api.get_request("projects")
|
projects = api.get_request("projects")
|
||||||
for project in projects:
|
for project in projects:
|
||||||
new_project = context.scene.CoveToolProperties.projects.add()
|
new_project = props.projects.add()
|
||||||
new_project.name = project["name"]
|
new_project.name = project["name"]
|
||||||
new_project.run_set = project["run_set"][0]
|
new_project.run_set = project["run_set"][0]
|
||||||
new_project.url = project["url"]
|
new_project.url = project["url"]
|
||||||
@@ -54,11 +55,10 @@ class RunSimpleAnalysis(bpy.types.Operator):
|
|||||||
bl_label = "Run Simple Analysis"
|
bl_label = "Run Simple Analysis"
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
simple_analysis = context.scene.CoveToolProperties.simple_analysis
|
props = tool.Blender.get_covetool_props()
|
||||||
|
simple_analysis = props.simple_analysis
|
||||||
data = {
|
data = {
|
||||||
"run": context.scene.CoveToolProperties.projects[
|
"run": props.projects[props.active_project_index].run_set,
|
||||||
context.scene.CoveToolProperties.active_project_index
|
|
||||||
].run_set,
|
|
||||||
"si_units": simple_analysis.si_units,
|
"si_units": simple_analysis.si_units,
|
||||||
"building_height": simple_analysis.building_height,
|
"building_height": simple_analysis.building_height,
|
||||||
"roof_area": simple_analysis.roof_area,
|
"roof_area": simple_analysis.roof_area,
|
||||||
@@ -92,6 +92,7 @@ class RunAnalysis(bpy.types.Operator):
|
|||||||
bl_label = "Run Analysis"
|
bl_label = "Run Analysis"
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
|
props = tool.Blender.get_covetool_props()
|
||||||
self.file = tool.Ifc.get()
|
self.file = tool.Ifc.get()
|
||||||
self.inputs = {
|
self.inputs = {
|
||||||
"floors": [],
|
"floors": [],
|
||||||
@@ -104,9 +105,7 @@ class RunAnalysis(bpy.types.Operator):
|
|||||||
}
|
}
|
||||||
self.parse_objects(context)
|
self.parse_objects(context)
|
||||||
data = {
|
data = {
|
||||||
"run": context.scene.CoveToolProperties.projects[
|
"run": props.projects[props.active_project_index].run_set,
|
||||||
context.scene.CoveToolProperties.active_project_index
|
|
||||||
].run_set,
|
|
||||||
"source": "Bonsai",
|
"source": "Bonsai",
|
||||||
"rotation_angle": self.get_rotation_angle(),
|
"rotation_angle": self.get_rotation_angle(),
|
||||||
**self.inputs,
|
**self.inputs,
|
||||||
|
|||||||
@@ -16,15 +16,18 @@
|
|||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with Bonsai. If not, see <http://www.gnu.org/licenses/>.
|
# along with Bonsai. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import bpy.props
|
import bpy
|
||||||
import bpy.types
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
|
|
||||||
class CoveToolProject(bpy.types.PropertyGroup):
|
class CoveToolProject(bpy.types.PropertyGroup):
|
||||||
name: bpy.props.StringProperty(name="Name")
|
|
||||||
run_set: bpy.props.StringProperty(name="Run Set")
|
run_set: bpy.props.StringProperty(name="Run Set")
|
||||||
url: bpy.props.StringProperty(name="URL")
|
url: bpy.props.StringProperty(name="URL")
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
run_set: str
|
||||||
|
url: set
|
||||||
|
|
||||||
|
|
||||||
class CoveToolSimpleAnalysis(bpy.types.PropertyGroup):
|
class CoveToolSimpleAnalysis(bpy.types.PropertyGroup):
|
||||||
si_units: bpy.props.BoolProperty(name="SI Units")
|
si_units: bpy.props.BoolProperty(name="SI Units")
|
||||||
@@ -49,6 +52,29 @@ class CoveToolSimpleAnalysis(bpy.types.PropertyGroup):
|
|||||||
window_area_s: bpy.props.StringProperty(name="Window Area S")
|
window_area_s: bpy.props.StringProperty(name="Window Area S")
|
||||||
window_area_se: bpy.props.StringProperty(name="Window Area SE")
|
window_area_se: bpy.props.StringProperty(name="Window Area SE")
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
si_units: bool
|
||||||
|
building_height: str
|
||||||
|
roof_area: str
|
||||||
|
floor_area: str
|
||||||
|
skylight_area: str
|
||||||
|
wall_area_e: str
|
||||||
|
wall_area_ne: str
|
||||||
|
wall_area_n: str
|
||||||
|
wall_area_nw: str
|
||||||
|
wall_area_w: str
|
||||||
|
wall_area_sw: str
|
||||||
|
wall_area_s: str
|
||||||
|
wall_area_se: str
|
||||||
|
window_area_e: str
|
||||||
|
window_area_ne: str
|
||||||
|
window_area_n: str
|
||||||
|
window_area_nw: str
|
||||||
|
window_area_w: str
|
||||||
|
window_area_sw: str
|
||||||
|
window_area_s: str
|
||||||
|
window_area_se: str
|
||||||
|
|
||||||
|
|
||||||
class CoveToolProperties(bpy.types.PropertyGroup):
|
class CoveToolProperties(bpy.types.PropertyGroup):
|
||||||
username: bpy.props.StringProperty(name="Username")
|
username: bpy.props.StringProperty(name="Username")
|
||||||
@@ -57,3 +83,11 @@ class CoveToolProperties(bpy.types.PropertyGroup):
|
|||||||
projects: bpy.props.CollectionProperty(name="Projects", type=CoveToolProject)
|
projects: bpy.props.CollectionProperty(name="Projects", type=CoveToolProject)
|
||||||
active_project_index: bpy.props.IntProperty(name="Active Project Index")
|
active_project_index: bpy.props.IntProperty(name="Active Project Index")
|
||||||
simple_analysis: bpy.props.PointerProperty(type=CoveToolSimpleAnalysis)
|
simple_analysis: bpy.props.PointerProperty(type=CoveToolSimpleAnalysis)
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
username: str
|
||||||
|
password: str
|
||||||
|
token: str
|
||||||
|
projects: bpy.types.bpy_prop_collection_idprop[CoveToolProject]
|
||||||
|
active_project_index: int
|
||||||
|
simple_analysis: CoveToolSimpleAnalysis
|
||||||
|
|||||||
@@ -16,7 +16,13 @@
|
|||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with Bonsai. If not, see <http://www.gnu.org/licenses/>.
|
# along with Bonsai. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
import bpy.types
|
import bpy.types
|
||||||
|
import bonsai.tool as tool
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from bonsai.bim.module.covetool.prop import CoveToolProperties, CoveToolProject
|
||||||
|
|
||||||
|
|
||||||
class BIM_PT_covetool(bpy.types.Panel):
|
class BIM_PT_covetool(bpy.types.Panel):
|
||||||
@@ -32,8 +38,7 @@ class BIM_PT_covetool(bpy.types.Panel):
|
|||||||
layout = self.layout
|
layout = self.layout
|
||||||
layout.use_property_split = True
|
layout.use_property_split = True
|
||||||
|
|
||||||
scene = context.scene
|
props = tool.Blender.get_covetool_props()
|
||||||
props = scene.CoveToolProperties
|
|
||||||
|
|
||||||
if not props.token:
|
if not props.token:
|
||||||
row = layout.row()
|
row = layout.row()
|
||||||
@@ -81,7 +86,16 @@ class BIM_PT_covetool(bpy.types.Panel):
|
|||||||
|
|
||||||
|
|
||||||
class BIM_UL_covetool_projects(bpy.types.UIList):
|
class BIM_UL_covetool_projects(bpy.types.UIList):
|
||||||
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
|
def draw_item(
|
||||||
|
self,
|
||||||
|
context,
|
||||||
|
layout: bpy.types.UILayout,
|
||||||
|
data: CoveToolProperties,
|
||||||
|
item: CoveToolProject,
|
||||||
|
icon,
|
||||||
|
active_data,
|
||||||
|
active_propname,
|
||||||
|
) -> None:
|
||||||
ob = data
|
ob = data
|
||||||
if item:
|
if item:
|
||||||
layout.prop(item, "name", text="", emboss=False)
|
layout.prop(item, "name", text="", emboss=False)
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ from bonsai.bim.module.drawing.data import (
|
|||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from bonsai.bim.module.drawing.prop import DocProperties, Drawing
|
from bonsai.bim.module.drawing.prop import DocProperties, Drawing, Sheet
|
||||||
|
|
||||||
|
|
||||||
class BIM_PT_camera(Panel):
|
class BIM_PT_camera(Panel):
|
||||||
@@ -678,7 +678,16 @@ class BIM_UL_drawinglist(bpy.types.UIList):
|
|||||||
|
|
||||||
|
|
||||||
class BIM_UL_sheets(bpy.types.UIList):
|
class BIM_UL_sheets(bpy.types.UIList):
|
||||||
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
|
def draw_item(
|
||||||
|
self,
|
||||||
|
context,
|
||||||
|
layout: bpy.types.UILayout,
|
||||||
|
data: DocProperties,
|
||||||
|
item: Sheet,
|
||||||
|
icon,
|
||||||
|
active_data,
|
||||||
|
active_propname,
|
||||||
|
) -> None:
|
||||||
if not item:
|
if not item:
|
||||||
layout.label(text="", translate=False)
|
layout.label(text="", translate=False)
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -16,17 +16,31 @@
|
|||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with Bonsai. If not, see <http://www.gnu.org/licenses/>.
|
# along with Bonsai. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
import bpy
|
import bpy
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from bonsai.bim.module.light.prop import RadianceExporterProperties, RadianceMaterial
|
||||||
|
|
||||||
with open(os.path.join(os.path.dirname(__file__), "spectraldb.json"), "r") as f:
|
with open(os.path.join(os.path.dirname(__file__), "spectraldb.json"), "r") as f:
|
||||||
spectraldb = json.load(f)
|
spectraldb = json.load(f)
|
||||||
|
|
||||||
|
|
||||||
class MATERIAL_UL_radiance_materials(bpy.types.UIList):
|
class MATERIAL_UL_radiance_materials(bpy.types.UIList):
|
||||||
def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index):
|
def draw_item(
|
||||||
|
self,
|
||||||
|
context,
|
||||||
|
layout: bpy.types.UILayout,
|
||||||
|
data: RadianceExporterProperties,
|
||||||
|
item: RadianceMaterial,
|
||||||
|
icon,
|
||||||
|
active_data,
|
||||||
|
active_propname,
|
||||||
|
index,
|
||||||
|
) -> None:
|
||||||
if self.layout_type in {"DEFAULT", "COMPACT"}:
|
if self.layout_type in {"DEFAULT", "COMPACT"}:
|
||||||
row = layout.row(align=True)
|
row = layout.row(align=True)
|
||||||
|
|
||||||
|
|||||||
@@ -16,11 +16,16 @@
|
|||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with Bonsai. If not, see <http://www.gnu.org/licenses/>.
|
# along with Bonsai. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
import bpy
|
import bpy
|
||||||
import bonsai.tool as tool
|
import bonsai.tool as tool
|
||||||
import bonsai.bim.helper
|
import bonsai.bim.helper
|
||||||
from bpy.types import Panel
|
from bpy.types import Panel
|
||||||
from bonsai.bim.module.search.data import SearchData, ColourByPropertyData, SelectSimilarData
|
from bonsai.bim.module.search.data import SearchData, ColourByPropertyData, SelectSimilarData
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from bonsai.bim.module.search.prop import BIMSearchProperties, BIMColour, BIMFilterClasses, BIMFilterBuildingStoreys
|
||||||
|
|
||||||
|
|
||||||
class BIM_PT_search(Panel):
|
class BIM_PT_search(Panel):
|
||||||
@@ -141,7 +146,16 @@ class BIM_PT_select_similar(Panel):
|
|||||||
|
|
||||||
|
|
||||||
class BIM_UL_colourscheme(bpy.types.UIList):
|
class BIM_UL_colourscheme(bpy.types.UIList):
|
||||||
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
|
def draw_item(
|
||||||
|
self,
|
||||||
|
context,
|
||||||
|
layout: bpy.types.UILayout,
|
||||||
|
data: BIMSearchProperties,
|
||||||
|
item: BIMColour,
|
||||||
|
icon,
|
||||||
|
active_data,
|
||||||
|
active_propname,
|
||||||
|
) -> None:
|
||||||
if not item:
|
if not item:
|
||||||
return
|
return
|
||||||
row = layout.row(align=True)
|
row = layout.row(align=True)
|
||||||
@@ -153,7 +167,17 @@ class BIM_UL_colourscheme(bpy.types.UIList):
|
|||||||
class BIM_UL_ifc_class_filter(bpy.types.UIList):
|
class BIM_UL_ifc_class_filter(bpy.types.UIList):
|
||||||
use_filter_linked: bpy.props.BoolProperty(name="Included", default=True, options=set(), description="Filter")
|
use_filter_linked: bpy.props.BoolProperty(name="Included", default=True, options=set(), description="Filter")
|
||||||
|
|
||||||
def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index):
|
def draw_item(
|
||||||
|
self,
|
||||||
|
context,
|
||||||
|
layout: bpy.types.UILayout,
|
||||||
|
data: BIMSearchProperties,
|
||||||
|
item: BIMFilterClasses,
|
||||||
|
icon,
|
||||||
|
active_data,
|
||||||
|
active_propname,
|
||||||
|
index,
|
||||||
|
) -> None:
|
||||||
split = layout
|
split = layout
|
||||||
split.use_property_split = True
|
split.use_property_split = True
|
||||||
split.use_property_decorate = False
|
split.use_property_decorate = False
|
||||||
@@ -169,7 +193,17 @@ class BIM_UL_ifc_class_filter(bpy.types.UIList):
|
|||||||
class BIM_UL_ifc_building_storey_filter(bpy.types.UIList):
|
class BIM_UL_ifc_building_storey_filter(bpy.types.UIList):
|
||||||
use_filter_linked: bpy.props.BoolProperty(name="Included", default=True, options=set(), description="Filter")
|
use_filter_linked: bpy.props.BoolProperty(name="Included", default=True, options=set(), description="Filter")
|
||||||
|
|
||||||
def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index):
|
def draw_item(
|
||||||
|
self,
|
||||||
|
context,
|
||||||
|
layout: bpy.types.UILayout,
|
||||||
|
data: BIMSearchProperties,
|
||||||
|
item: BIMFilterBuildingStoreys,
|
||||||
|
icon,
|
||||||
|
active_data,
|
||||||
|
active_propname,
|
||||||
|
index,
|
||||||
|
) -> None:
|
||||||
split = layout
|
split = layout
|
||||||
split.use_property_split = True
|
split.use_property_split = True
|
||||||
split.use_property_decorate = False
|
split.use_property_decorate = False
|
||||||
|
|||||||
@@ -1033,6 +1033,11 @@ class BIM_OT_enum_property_search(bpy.types.Operator):
|
|||||||
|
|
||||||
identifiers: list[str]
|
identifiers: list[str]
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
collection_names: bpy.types.bpy_prop_collection_idprop[StrProperty]
|
||||||
|
collection_identifiers: bpy.types.bpy_prop_collection_idprop[StrProperty]
|
||||||
|
collection_predefined_types: bpy.types.bpy_prop_collection_idprop[StrProperty]
|
||||||
|
|
||||||
def invoke(self, context, event):
|
def invoke(self, context, event):
|
||||||
self.clear_collections()
|
self.clear_collections()
|
||||||
self.data = context.data
|
self.data = context.data
|
||||||
|
|||||||
@@ -26,12 +26,12 @@ if TYPE_CHECKING:
|
|||||||
import bonsai.tool as tool
|
import bonsai.tool as tool
|
||||||
|
|
||||||
|
|
||||||
def import_bsdd_classes(bsdd: type[tool.Bsdd], obj, obj_type) -> int:
|
def import_bsdd_classes(bsdd: type[tool.Bsdd], obj: str, obj_type: tool.Ifc.OBJECT_TYPE) -> None:
|
||||||
return bsdd.import_classes(obj, obj_type)
|
bsdd.import_classes(obj, obj_type)
|
||||||
|
|
||||||
|
|
||||||
def search_bsdd_properties(bsdd: type[tool.Bsdd], keyword: str, obj, obj_type) -> int:
|
def search_bsdd_properties(bsdd: type[tool.Bsdd], keyword: str, obj: str, obj_type: tool.Ifc.OBJECT_TYPE) -> None:
|
||||||
return bsdd.import_properties(obj, obj_type, keyword)
|
bsdd.import_properties(obj, obj_type, keyword)
|
||||||
|
|
||||||
|
|
||||||
def load_bsdd(bsdd: type[tool.Bsdd]) -> None:
|
def load_bsdd(bsdd: type[tool.Bsdd]) -> None:
|
||||||
|
|||||||
@@ -59,6 +59,7 @@ if TYPE_CHECKING:
|
|||||||
from bonsai.bim.prop import BIMProperties, BIMObjectProperties
|
from bonsai.bim.prop import BIMProperties, BIMObjectProperties
|
||||||
from bonsai.bim.module.attribute.prop import BIMAttributeProperties
|
from bonsai.bim.module.attribute.prop import BIMAttributeProperties
|
||||||
from bonsai.bim.module.constraint.prop import BIMConstraintProperties, BIMObjectConstraintProperties
|
from bonsai.bim.module.constraint.prop import BIMConstraintProperties, BIMObjectConstraintProperties
|
||||||
|
from bonsai.bim.module.covetool.prop import CoveToolProperties
|
||||||
from bonsai.bim.module.csv.prop import CsvProperties
|
from bonsai.bim.module.csv.prop import CsvProperties
|
||||||
from bonsai.bim.module.diff.prop import DiffProperties
|
from bonsai.bim.module.diff.prop import DiffProperties
|
||||||
from bonsai.bim.module.fm.prop import BIMFMProperties
|
from bonsai.bim.module.fm.prop import BIMFMProperties
|
||||||
@@ -1783,6 +1784,11 @@ class Blender(bonsai.core.tool.Blender):
|
|||||||
assert (scene := bpy.context.scene)
|
assert (scene := bpy.context.scene)
|
||||||
return scene.BIMFMProperties # pyright: ignore[reportAttributeAccessIssue]
|
return scene.BIMFMProperties # pyright: ignore[reportAttributeAccessIssue]
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_covetool_props(cls) -> CoveToolProperties:
|
||||||
|
assert (scene := bpy.context.scene)
|
||||||
|
return scene.CoveToolProperties # pyright: ignore[reportAttributeAccessIssue]
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_ifc_definition_id(cls, obj: IFC_CONNECTED_TYPE) -> int:
|
def get_ifc_definition_id(cls, obj: IFC_CONNECTED_TYPE) -> int:
|
||||||
if isinstance(obj, bpy.types.Object):
|
if isinstance(obj, bpy.types.Object):
|
||||||
|
|||||||
@@ -306,7 +306,7 @@ class Bsdd(bonsai.core.tool.Bsdd):
|
|||||||
new.uri = bsdd_prop["uri"]
|
new.uri = bsdd_prop["uri"]
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def import_properties(cls, obj, obj_type, keyword) -> None:
|
def import_properties(cls, obj: str, obj_type: tool.Ifc.OBJECT_TYPE, keyword: str) -> None:
|
||||||
props = cls.get_bsdd_props()
|
props = cls.get_bsdd_props()
|
||||||
props.properties.clear()
|
props.properties.clear()
|
||||||
pprops = tool.Pset.get_pset_props(obj, obj_type)
|
pprops = tool.Pset.get_pset_props(obj, obj_type)
|
||||||
|
|||||||
@@ -933,12 +933,12 @@ class Drawing(bonsai.core.tool.Drawing):
|
|||||||
|
|
||||||
camera_props.update_props = update_props
|
camera_props.update_props = update_props
|
||||||
|
|
||||||
|
drawing_selected_states: dict[int, bool] = {}
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def import_drawings(cls) -> None:
|
def import_drawings(cls) -> None:
|
||||||
props = tool.Drawing.get_document_props()
|
props = tool.Drawing.get_document_props()
|
||||||
expanded_target_views = {d.target_view for d in props.drawings if not d.is_drawing and d.is_expanded}
|
expanded_target_views = {d.target_view for d in props.drawings if not d.is_drawing and d.is_expanded}
|
||||||
if not hasattr(cls, "drawing_selected_states"):
|
|
||||||
cls.drawing_selected_states = {}
|
|
||||||
cls.drawing_selected_states.update({d.ifc_definition_id: d.is_selected for d in props.drawings if d.is_drawing})
|
cls.drawing_selected_states.update({d.ifc_definition_id: d.is_selected for d in props.drawings if d.is_drawing})
|
||||||
props.drawings.clear()
|
props.drawings.clear()
|
||||||
drawings = [e for e in tool.Ifc.get().by_type("IfcAnnotation") if e.ObjectType == "DRAWING"]
|
drawings = [e for e in tool.Ifc.get().by_type("IfcAnnotation") if e.ObjectType == "DRAWING"]
|
||||||
|
|||||||
Reference in New Issue
Block a user