def execute(my): my.init() # create the filters my.filters = [] """ for element_name in my.config.get_element_names(): filter = my.config.get_display_widget(element_name) my.filters.append(filter) # make sure there is at least one filter defined assert my.filters """ config = "<config>\n" config += "<filter>\n" # get all of the serialized versions of the filters """ for filter in my.filters: config += filter.serialize() + "\n" """ filter_data = FilterData.get() json = filter_data.serialize() value_type = "json" config += "<values type='%s'>%s</values>\n" % (value_type, json) config += "</filter>\n" config += "</config>\n" # format the xml xml = Xml() xml.read_string(config) if not my.view: saved_view = "saved_search:%s" % my.search_type else: saved_view = my.view # if my.view.startswith("saved_search:"): # saved_view = my.view # else: # saved_view = "saved_search:%s" % my.view # use widget config instead search = Search("config/widget_config") search.add_filter("view", saved_view) search.add_filter("search_type", my.search_type) if my.personal: search.add_user_filter() config = search.get_sobject() if not config: config = SearchType.create("config/widget_config") config.set_value("view", saved_view) config.set_value("search_type", my.search_type) if my.personal: config.set_user() config.set_value("config", xml.to_string()) config.commit()
def execute(my): my.init() # create the filters my.filters = [] """ for element_name in my.config.get_element_names(): filter = my.config.get_display_widget(element_name) my.filters.append(filter) # make sure there is at least one filter defined assert my.filters """ config = "<config>\n" config += "<filter>\n" # get all of the serialized versions of the filters """ for filter in my.filters: config += filter.serialize() + "\n" """ filter_data = FilterData.get() json = filter_data.serialize() value_type = "json" config += "<values type='%s'>%s</values>\n" % (value_type, json) config += "</filter>\n" config += "</config>\n" # format the xml xml = Xml() xml.read_string(config) if not my.view: saved_view = "saved_search:%s" % my.search_type else: saved_view = my.view # if my.view.startswith("saved_search:"): # saved_view = my.view # else: # saved_view = "saved_search:%s" % my.view # use widget config instead search = Search('config/widget_config') search.add_filter("view", saved_view) search.add_filter("search_type", my.search_type) if my.personal: search.add_user_filter() config = search.get_sobject() if not config: config = SearchType.create('config/widget_config') config.set_value("view", saved_view) config.set_value("search_type", my.search_type) if my.personal: config.set_user() config.set_value("config", xml.to_string()) config.commit()
def handle_include(my, node): path = my.xml.get_attribute(node, "path") if not path: raise TacticException("No path found for include in manifest") path = "%s/%s" % (my.plugin_dir, path) if path.endswith(".py"): from tactic.command import PythonCmd cmd = PythonCmd(file_path=path) manifest = cmd.execute() if not manifest: print "No manifest discovered in [%s]" %path return xml = Xml() xml.read_string(manifest) nodes = xml.get_nodes("manifest/*") sobjects = [] for i, node in enumerate(nodes): name = my.xml.get_node_name(node) if name == 'sobject': dumped_sobjects = my.handle_sobject(node) if not dumped_sobjects: dumped_sobjects = [] sobjects.extend(dumped_sobjects) elif name == 'search_type': my.handle_search_type(node) elif name == 'include': my.handle_include(node)
def get_file_paths(my, transaction, mode='lib'): transaction_xml = transaction.get_xml_value("transaction") if not transaction_xml: return [] from pyasm.common import Xml, Environment if isinstance(transaction_xml, basestring): xml = Xml() xml.read_string(transaction_xml) else: xml = transaction_xml base_dir = Environment.get_asset_dir() paths = [] # get all of the file nodes nodes = xml.get_nodes("transaction/file") for node in nodes: if xml.get_attribute(node, "type") == 'create': src = xml.get_attribute(node, "src") if mode == 'relative': path = src else: path = "%s/%s" % (base_dir, src) paths.append(path) return paths
def delete_files(my, nodes): # clean out all of the files for node in nodes: name = my.xml.get_node_name(node) if name == "include": path = my.xml.get_attribute(node, "path") if not path: print("WARNING: No path found for search type in manifest") continue path = "%s/%s" % (my.plugin_dir, path) if path.endswith(".py"): from tactic.command import PythonCmd cmd = PythonCmd(file_path=path) manifest = cmd.execute() if manifest: xml = Xml() xml.read_string(manifest) include_nodes = xml.get_nodes("manifest/*") my.delete_files(include_nodes) elif name == "python": # don't delete python node file pass else: path = my.get_path_from_node(node) if path and os.path.exists(path): print "Deleting: ", path os.unlink(path)
def _test_guest_allow(self): '''test Config tag allow_guest in security tag. Note: Since it is hard to emulate AppServer class, this is based on logic which handles in _get_display of BaseAppServer. 1. If allow_guest is false, then it is necessary that Sudo is instantiated. 2. If allow_guest is true, then it is necessary that guest login rules are added and login_as_guest is executed. ''' security = Security() Environment.set_security(security) #1. allow_guest is false fail = False try: sudo = Sudo() except Exception as e: fail = True self.assertEquals(False, fail) sudo.exit() key = [{'code': "*"}] project_access = security.check_access("project", key, "allow") self.assertEquals(project_access, False) #2. allow_guest is true Site.set_site("default") try: security.login_as_guest() ticket_key = security.get_ticket_key() access_manager = security.get_access_manager() xml = Xml() xml.read_string(''' <rules> <rule column="login" value="{$LOGIN}" search_type="sthpw/login" access="deny" op="!=" group="search_filter"/> <rule group="project" code="default" access="allow"/> </rules> ''') access_manager.add_xml_rules(xml) finally: Site.pop_site() default_key = [{'code': "default"}] project_access = security.check_access("project", default_key, "allow") self.assertEquals(project_access, True) unittest_key = [{'code', "sample3d"}] project_access = security.check_access("project", unittest_key, "allow") self.assertEquals(project_access, False)
def _test_guest_allow(self): '''test Config tag allow_guest in security tag. Note: Since it is hard to emulate AppServer class, this is based on logic which handles in _get_display of BaseAppServer. 1. If allow_guest is false, then it is necessary that Sudo is instantiated. 2. If allow_guest is true, then it is necessary that guest login rules are added and login_as_guest is executed. ''' security = Security() Environment.set_security(security) #1. allow_guest is false fail = False try: sudo = Sudo() except Exception as e: fail = True self.assertEquals( False, fail ) sudo.exit() key = [{'code': "*"}] project_access = security.check_access("project", key, "allow") self.assertEquals(project_access, False) #2. allow_guest is true Site.set_site("default") try: security.login_as_guest() ticket_key = security.get_ticket_key() access_manager = security.get_access_manager() xml = Xml() xml.read_string(''' <rules> <rule column="login" value="{$LOGIN}" search_type="sthpw/login" access="deny" op="!=" group="search_filter"/> <rule group="project" code="default" access="allow"/> </rules> ''') access_manager.add_xml_rules(xml) finally: Site.pop_site() default_key = [{'code': "default"}] project_access = security.check_access("project", default_key, "allow") self.assertEquals(project_access, True) unittest_key = [{'code', "sample3d"}] project_access = security.check_access("project", unittest_key, "allow") self.assertEquals(project_access, False)
def _test_sobject_access_manager(my): '''test a more realistic example''' # create a test person person = Person.create("Donald", "Duck", "DisneyLand", "A duck!!!") my.person = person for project_code in ['unittest','unittest','sample3d']: task = SearchType.create('sthpw/task') task.set_sobject_value(person) task.set_value('assigned', 'unittest_guy') task.set_value('project_code', project_code) task.set_value('description', 'do something good') task.set_value('process', 'unittest') task.set_value('context', 'unittest') task.commit() # an extra task for list-based search_filter test task = SearchType.create('sthpw/task') task.set_sobject_value(person) task.set_value('assigned', 'unittest_gal') task.set_value('project_code', 'unittest') task.set_value('description', 'do something good') task.set_value('process', 'unittest2') task.set_value('context', 'unittest2') task.commit() # add these rules to the current user rules = """ <rules> <rule group="sobject_column" default="edit"/> <rule group="sobject_column" search_type="unittest/person" column="name_first" access="edit"/> <rule group="sobject_column" search_type="unittest/person" column="name_last" access="deny"/> <rule group="sobject_column" search_type="unittest/person" column="nationality" access="deny"/> </rules> """ xml = Xml() xml.read_string(rules) access_manager = Environment.get_security().get_access_manager() access_manager.add_xml_rules(xml) # disable admin for this test access_manager.set_admin(False) # should succeed person.set_value("name_first", "Donny") # should fail try: person.set_value("name_last", "Ducky") except SecurityException, e: pass
class Package(Command): def __init__(self, search_key, context, package): self.search_key = search_key self.context = context self.package = package self.package_xml = Xml() self.package_xml.read_string(package) super(Package, self).__init__() def execute(self): from tactic_client_lib import TacticServerStub server = TacticServerStub.get(protocol='local') # create a new snapshot snapshot = server.create_snapshot(self.search_key, self.context) # get all of the file_types file_nodes = self.package_xml.get_nodes("package/file_type") count = 0 for file_node in file_nodes: name = self.package_xml.get_attribute(file_node, "name") values = self.package_xml.get_node_values_of_children(file_node) expression = values.get("expression") dir_naming = values.get("dir_naming") file_naming = values.get("file_naming") files = Search.eval(expression) for file in files: file_type = "%s%s" % (name, count) try: # FIXME: the assumed action is to checkin server.add_file(snapshot, file, file_type=file_type, mode='copy', dir_naming=dir_naming, file_naming=file_naming) # What if we just wished to copy? Can we run the files # through a naming convention filter? count += 1 except Exception as e: print "WARNING: ", str(e)
def create(name, desc, search_type, xml=None, code=None, color=None): '''will only create if it does not exist, otherwise it just updates''' if code: sobject = Pipeline.get_by_code(code) else: sobject = None if sobject == None: #sobject = Pipeline( Pipeline.SEARCH_TYPE ) sobject = SearchType.create(Pipeline.SEARCH_TYPE) else: return sobject if not xml: xml = Xml() xml.create_doc('pipeline') if isinstance(xml, basestring): xml_string = xml xml = Xml() xml.read_string(xml_string) sobject.set_value("pipeline", xml.get_xml()) sobject.set_pipeline(xml.to_string()) sobject.set_value('timestamp', Sql.get_default_timestamp_now(), quoted=False) if code: sobject.set_value('code', code.strip()) sobject.set_value('name', name.strip()) sobject.set_value('search_type', search_type) sobject.set_value('description', desc) if color: sobject.set_value("color", color) sobject.commit() process_names = sobject.get_process_names() for i, process_name in enumerate(process_names): process = SearchType.create("config/process") process.set_value("pipeline_code", sobject.get_code()) process.set_value("process", process_name) process.set_value("sort_order", i) process.set_value("subcontext_options", "(main)") process.commit() return sobject
def get_default_task_xml(): global TASK_PIPELINE from pyasm.web import Palette palette = Palette.get() xml = Xml() xml.read_string(TASK_PIPELINE) nodes = Xml.get_nodes(xml, "pipeline/process") for node in nodes: process = Xml.get_attribute(node, "name") color = Task.get_default_color(process) Xml.set_attribute(node, "color", color) return xml.to_string()
def execute(self): self.init() # create the filters self.filters = [] config = "<config>\n" config += "<filter>\n" # get all of the serialized versions of the filters filter_data = FilterData.get() json = filter_data.serialize() value_type = "json" config += "<values type='%s'>%s</values>\n" % (value_type, json) config += "</filter>\n" config += "</config>\n" # format the xml xml = Xml() xml.read_string(config) if not self.view: saved_view = "saved_search:%s" % self.search_type else: saved_view = self.view # if self.view.startswith("saved_search:"): # saved_view = self.view # else: # saved_view = "saved_search:%s" % self.view # use widget config instead search = Search('config/widget_config') search.add_filter("view", saved_view) search.add_filter("search_type", self.search_type) if self.personal: search.add_user_filter() config = search.get_sobject() if not config: config = SearchType.create('config/widget_config') config.set_value("view", saved_view) config.set_value("search_type", self.search_type) if self.personal: config.set_user() config.set_value("category", "search_filter") config.set_value("config", xml.to_string()) config.commit()
def handle_include(my, node): path = my.xml.get_attribute(node, "path") if not path: raise TacticException("No path found for search type in manifest") path = "%s/%s" % (my.plugin_dir, path) if path.endswith(".py"): from tactic.command import PythonCmd cmd = PythonCmd(file_path=path) manifest = cmd.execute() xml = Xml() xml.read_string(manifest) nodes = xml.get_nodes("manifest/*") nodes.reverse() my.handle_nodes(nodes)
def get_default_filter_config(my): custom_filter_view = my.kwargs.get('custom_filter_view') config = ''' <config> <filter> <element name='Filter'> <display class='tactic.ui.filter.GeneralFilterWdg'> <prefix>%(prefix_namespace)s_main_body</prefix> <search_type>%(search_type)s</search_type> <mode>sobject</mode> </display> </element> </filter> </config> ''' % {'search_type': my.searchable_search_type, 'prefix_namespace': my.prefix_namespace } config_xml = Xml() config_xml.read_string(config) config = WidgetConfig.get(xml=config_xml, view='filter') return config
def get_default_filter_config(self): custom_filter_view = self.kwargs.get('custom_filter_view') config = ''' <config> <filter> <element name='Filter'> <display class='tactic.ui.filter.GeneralFilterWdg'> <prefix>%(prefix_namespace)s_main_body</prefix> <search_type>%(search_type)s</search_type> <mode>sobject</mode> </display> </element> </filter> </config> ''' % {'search_type': self.searchable_search_type, 'prefix_namespace': self.prefix_namespace } config_xml = Xml() config_xml.read_string(config) config = WidgetConfig.get(xml=config_xml, view='filter') return config
def handle_include(my, node): path = my.xml.get_attribute(node, "path") if not path: raise TacticException("No path found for search type in manifest") path = "%s/%s" % (my.plugin_dir, path) if path.endswith(".py"): from tactic.command import PythonCmd cmd = PythonCmd(file_path=path) manifest = cmd.execute() if not manifest: return xml = Xml() xml.read_string(manifest) nodes = xml.get_nodes("manifest/*") nodes.reverse() my.handle_nodes(nodes)
def execute(self): mode = self.kwargs.get('mode') if not mode: mode = 'lib' transaction_xml = self.kwargs.get("transaction_xml") assert(transaction_xml) from pyasm.common import Xml, Environment if isinstance(transaction_xml, basestring): xml = Xml() xml.read_string(transaction_xml) else: xml = transaction_xml base_dir = Environment.get_asset_dir() paths = [] # get all of the file nodes nodes = xml.get_nodes("transaction/file") for node in nodes: if xml.get_attribute(node, "type") == 'create': src = xml.get_attribute(node, "src") if mode == 'relative': path = src else: if src.startswith(base_dir): path = src else: path = "%s/%s" % (base_dir, src) paths.append(path) return paths
def replace_elements(my, html_str): """ # NOTE: this likely is a better way to extract elements, but still # need to find a way to inject html back into the xml xml = Xml() xml.read_string("<div>%s</div>" % html_str) elements = xml.get_nodes("//element") for element in elements: # create a complete config full_line_str = xml.to_string(element) tmp_config = '''<config><tmp>%s</tmp></config>''' % full_line_str try: element_wdg = my.get_element_wdg(xml, my.def_config) element_html = element_wdg.get_buffer_display() except Exception, e: from pyasm.widget import ExceptionWdg element_html = ExceptionWdg(e).get_buffer_display() xml = Xml() try: xml.read_string(element_html) except Exception, e: print "Error: ", e xml.read_string("<h1>%s</h1>" % str(e) ) root = xml.get_root_node() parent = xml.get_parent(element) xml.replace_child(parent, element, root) return xml.to_string() """ # a simple readline interpreter html = Html() full_line = [] parse_context = None for line in html_str.split("\n"): line2 = line.strip() #if not parse_context and not line2.startswith('<element '): index = line2.find('<element>') if index == -1: index = line2.find('<element ') if not parse_context and index == -1: #line = Common.process_unicode_string(line) html.writeln(line) continue if index != -1: part1 = line2[:index] html.write(part1) line2 = line2[index:] full_line.append(line2) xml = Xml() # determine if this is valid xml try: # create a complete config full_line_str = "".join(full_line) tmp_config = '''<config><tmp>%s</tmp></config>''' % full_line_str xml.read_string(tmp_config, print_error=False) full_line = [] parse_context = '' except XmlException, e: parse_context = 'element' #raise e continue try: element_wdg = my.get_element_wdg(xml, my.def_config) if element_wdg: element_html = element_wdg.get_buffer_display() else: element_html = '' except Exception, e: from pyasm.widget import ExceptionWdg element_html = ExceptionWdg(e).get_buffer_display()
class PageNavContainerWdg(BaseRefreshWdg): def init(self): link = self.kwargs.get('link') hash = self.kwargs.get('hash') self.widget = None if link: from tactic.ui.panel import SideBarBookmarkMenuWdg personal = False if '.' in link: personal = True config = SideBarBookmarkMenuWdg.get_config("SideBarWdg", link, personal=personal) options = config.get_display_options(link) # this is vital for view saving element_name = link attr_dict = config.get_element_attributes(link) title = attr_dict.get('title') hash = "/tab/%s" % link config = ''' <config> <application> <element name="left_nav"> <display class="tactic.ui.panel.SideBarPanelWdg"> </display> </element> <element name="main_body"> <display class="tactic.ui.panel.HashPanelWdg"> <hash>%s</hash> <element_name>%s</element_name> <title>%s</title> </display> <web/> </element> </application> </config> ''' % (hash, element_name, title) elif hash: from tactic.ui.panel import HashPanelWdg self.widget = HashPanelWdg.get_widget_from_hash( hash, force_no_index=True) config = None else: security = Environment.get_security() start_link = security.get_start_link() if start_link: self.kwargs['link'] = start_link return self.init() # search for a defined welcome view search = Search("config/widget_config") search.add_filter("category", "top_layout") search.add_filter("view", "welcome") config_sobj = search.get_sobject() if config_sobj: config = config_sobj.get_value("config") else: config = WidgetSettings.get_value_by_key("top_layout") if not config: config = self.get_default_config() self.config_xml = Xml() self.config_xml.read_string(config) def get_default_config(self): use_sidebar = self.kwargs.get('use_sidebar') if use_sidebar == False: config = ''' <config> <application> <element name="main_body"> <display class="tactic.ui.startup.MainWdg"/> <web/> </element> </application> </config> ''' else: config = ''' <config> <application> <element name="left_nav"> <display class="tactic.ui.panel.SideBarPanelWdg"> <auto_size>True</auto_size> </display> </element> <element name="main_body"> <display class="tactic.ui.startup.MainWdg"/> <web/> </element> </application> </config> ''' return config def set_state(self, panel_name, widget_class, options, values): '''this is called by side_bar.js mostly''' # set the class name display_node = self.config_xml.get_node( "config/application/element[@name='%s']/display" % (panel_name)) self.config_xml.set_attribute(display_node, "class", widget_class) # remove all the old options #display_node = self.config_xml.get_node("config/application/element[@name='%s']/display" % panel_name ) for child_node in self.config_xml.get_children(display_node): self.config_xml.remove_child(display_node, child_node) # set the options for name, value in options.items(): node = self.config_xml.get_node( "config/application/element[@name='%s']/display/%s" % (panel_name, name)) if isinstance(value, basestring): #print("WARNING: set application: skipping [%s] with value [%s]" % (name, value)) #continue element = self.config_xml.create_text_element(name, value) self.config_xml.append_child(display_node, element) elif isinstance(value, dict): # if it is a dictionary # TODO: run recursively.. supports 2 level only now sub_element = self.config_xml.create_element(name) self.config_xml.append_child(display_node, element) for name2, value2 in value.items(): if isinstance(value2, dict): sub_element2 = self.config_xml.create_element(name2) self.config_xml.append_child(sub_element, sub_element2) for name3, value3 in value2.items(): element = self.config_xml.create_text_element( name3, value3) self.config_xml.append_child(sub_element2, element) else: element = self.config_xml.create_text_element( name2, value2) self.config_xml.append_child(sub_element, element) # web value node value_node = self.config_xml.get_node( "config/application/element[@name='%s']/web" % (panel_name)) if value_node != None: for child_node in self.config_xml.get_children(value_node): self.config_xml.remove_child(value_node, child_node) else: # create it value_node = self.config_xml.create_element('web') element_node = self.config_xml.get_node( "config/application/element[@name='%s']" % (panel_name)) self.config_xml.append_child(element_node, value_node) # set the values for name, value in values.items(): node = self.config_xml.get_node( "config/application/element[@name='%s']/web/%s" % (panel_name, name)) if not isinstance(value, basestring): print( "WARNING: set application: skipping [%s] with value [%s]" % (name, value)) continue element = self.config_xml.create_text_element(name, value) self.config_xml.append_child(value_node, element) WidgetSettings.set_key_values("top_layout", [self.config_xml.to_string()]) def get_state(self): return self.config_xml def get_side_bar_cache(self, left_nav_wdg): project = Project.get() project_code = project.get_code() # do it with sobject #key = "%s_side_bar" % project.get_code() #cache = Search.get("sthpw/widget_cache") #cache.add_filter("key", key) #sobject = cache.get_sobject() #value = sobject.get_value("cache") login = Environment.get_user_name() tmp_dir = "%s/cache/side_bar" % Environment.get_tmp_dir() filename = "%s__%s.html" % (project_code, login) path = "%s/%s" % (tmp_dir, filename) # use files import os if os.path.exists(path): f = open(path, "r") html = f.read() f.close() else: dirname = os.path.dirname(path) if not os.path.exists(dirname): os.makedirs(dirname) f = open(path, "w") html = left_nav_wdg.get_buffer_display() f.write(html) f.close() return html def get_display(self): is_admin_project = Project.get().is_admin() security = Environment.get_security() if is_admin_project and not security.check_access( "builtin", "view_site_admin", "allow"): return Error403Wdg() # create the elements config = WidgetConfig.get(xml=self.config_xml, view="application") left_nav_handler = config.get_display_handler("left_nav") left_nav_options = config.get_display_options("left_nav") view_side_bar = None if left_nav_handler: left_nav_wdg = Common.create_from_class_path( left_nav_handler, [], left_nav_options) # caching side_bar_cache = self.get_side_bar_cache(left_nav_wdg) else: view_side_bar = False # create the main table core_table = Table() core_table.add_tbody() core_table.set_style( "border: 0px; border-collapse: collapse; width: 100%;") # add a spacer row #spacer_tr = core_table.add_row() #spacer_tr.add_style("width: 100%") #td = core_table.add_cell() #td.set_style("min-height: 1px; height: 1px;") #core_table.add_cell() #core_table.add_cell() # determine if the side bar is visible if view_side_bar == None: view_side_bar = security.check_access("builtin", "view_side_bar", "allow", default='allow') # add the main cells tr, td = core_table.add_row_cell() td.add_style("padding: 0px") td.add_style("margin: 0px") # add the main resizable table from tactic.ui.container import ResizableTableWdg main_table = ResizableTableWdg() main_table.set_keep_table_size() main_table.add_style("width: 100%") td.add(main_table) left_nav_td = main_table.add_cell() if view_side_bar: left_nav_td.add_class("spt_panel") left_nav_td.add_style("padding: 0px") main_body_td = main_table.add_cell(resize=False) main_body_td.add_style("padding: 10px") main_body_td.set_style( "width: 100%; vertical-align: top; text-align: center; padding-top: 3px" ) if view_side_bar: left_nav_td.set_style("vertical-align: top") # create the left navigation panel left_nav_div = DivWdg() left_nav_td.add(left_nav_div) left_nav_div.set_id("side_bar") # add the detail to the panel left_nav_div.add_attr("spt_class_name", left_nav_handler) for name, value in left_nav_options.items(): left_nav_div.add_attr("spt_%s" % name, value) left_nav_div.add_style("max_width: 185px") left_nav_div.add_style("width: 185px") left_nav_div.add_style("text-align: right") left_nav_div.add_style("vertical-align: top") left_nav_div.add_style("overflow: hidden") left_nav_div.add_class("spt_resizable") side_bar_inner = DivWdg() left_nav_div.add(side_bar_inner) #side_bar_inner.add_style("padding-left: 1px") side_bar_inner.add_style("width: 100%") # add side bar to nav side_bar_inner.add(side_bar_cache) left_nav_div.add_style("border-style: solid") left_nav_div.add_style("border-width: 0px 1px 0px 0px") #left_nav_div.add_color("border-color", "border") left_nav_div.add_color("border-color", "border", -10) web = WebContainer.get_web() browser = web.get_browser() if browser in ['Qt', 'Webkit']: min_width = "1px" else: min_width = "0px" left_nav_div.add_behavior({ 'type': 'listen', 'event_name': 'side_bar|hide_now', 'min_width': min_width, 'cbjs_action': ''' var size = bvr.src_el.getSize(); bvr.src_el.setAttribute("spt_size", size.x); bvr.src_el.setStyle("width", bvr.min_width); ''' }) left_nav_div.add_behavior({ 'type': 'listen', 'event_name': 'side_bar|hide', 'min_width': min_width, 'cbjs_action': ''' var size = bvr.src_el.getSize(); bvr.src_el.setAttribute("spt_size", size.x); new Fx.Tween(bvr.src_el, {duration:'short'}).start('width', bvr.min_width); ''' }) left_nav_div.add_behavior({ 'type': 'listen', 'event_name': 'side_bar|show', 'min_width': min_width, 'cbjs_action': ''' var width = bvr.src_el.getAttribute("spt_size"); if (!width) { width = 185; } if (parseInt(width) < 5) { width = 185; } //bvr.src_el.setStyle("width", width + "px"); new Fx.Tween(bvr.src_el, {duration:'short'}).start('width', bvr.min_width, width+"px"); ''' }) left_nav_div.add_behavior({ 'type': 'listen', 'event_name': 'side_bar|toggle', 'cbjs_action': ''' var size = bvr.src_el.getSize(); if (size.x < 5) { spt.named_events.fire_event("side_bar|show", {} ); } else { spt.named_events.fire_event("side_bar|hide", {} ); } ''' }) # create the main body panel palette = WebContainer.get_palette() color = palette.color("background2") main_body_rounded = DivWdg() main_body_inner = main_body_rounded main_body_inner.add_style("min-height: 500px") # DEBREACATED """ # add a breadcrumb breadcrumb_wdg = DivWdg() # hide the breadcrumb breadcrumb_wdg.add_style("display", "none") Container.put("breadcrumb", breadcrumb_wdg) breadcrumb_wdg.set_id("breadcrumb") breadcrumb_wdg.add_style("text-align: left") breadcrumb_wdg.add_looks( "fnt_title_3" ) main_body_inner.add(breadcrumb_wdg) """ main_body_panel = DivWdg() main_body_panel.set_id("main_body") main_body_panel.add_class("spt_main_panel") main_body_inner.add(main_body_panel) tab = MainBodyTabWdg() main_body_panel.add(tab) # TEST: NEW LAYOUT if Config.get_value("install", "layout") == "fixed": main_body_panel.add_style("margin-top: 31px") main_body_rounded.add_color("background", "background") main_body_rounded.add_style("padding: 3px 0px 0px 0px") # add the content to the main body panel try: if self.widget: tab.add(self.widget) element_name = self.widget.get_name() else: main_body_handler = config.get_display_handler("main_body") main_body_options = config.get_display_options("main_body") element_name = main_body_options.get("element_name") title = main_body_options.get("title") main_body_content = Common.create_from_class_path( main_body_handler, [], main_body_options) # get the web values from top_layout main_body_values = config.get_web_options("main_body") web = WebContainer.get_web() if isinstance(main_body_values, dict): for name, value in main_body_values.items(): web.set_form_value(name, value) main_body_content.set_name(element_name) tab.add(main_body_content, element_name, title) self.set_as_panel(main_body_panel, class_name=main_body_handler, kwargs=main_body_options) main_body_panel.add_behavior({ 'type': 'load', 'element_name': element_name, 'cbjs_action': ''' if (spt.help) spt.help.set_view(bvr.element_name); ''' }) except Exception as e: # handle an error in the drawing buffer = self.get_buffer_on_exception() error_wdg = self.handle_exception(e) main_body_content = DivWdg() main_body_content.add(error_wdg) main_body_content = main_body_content.get_buffer_display() tab.add(main_body_content, element_name, title) # add the main container container_div = DivWdg() container_div.set_style("width: 100%;") # NOTE: the td should not be the sliding element! So we create a div inside the TD to be the sliding element main_body_div = DivWdg() main_body_div.set_id("horizontal_main_body_slider") main_body_div.add(main_body_inner) """ # get the global drag_ghost_div drag_ghost_div = self.get_drag_ghost_div() drag_ghost_div.set_id( "drag_ghost_copy" ) drag_ghost_div.add_class( "SPT_PUW" ) # make it a Page Utility Widget (now processed client side) drag_ghost_div.set_z_start( 400 ) """ from page_header_wdg import PageHeaderWdg header_panel = DivWdg() header_panel.set_id("main_header") header_panel.add_attr("spt_class_name", "tactic.ui.app.PageHeaderWdg") header_wdg = PageHeaderWdg() header_panel.add(header_wdg) container_div.add(header_panel.get_buffer_display()) main_body_dis = main_body_div.get_buffer_display() main_body_td.add(main_body_dis) container_div.add(core_table) #container_div.add( drag_ghost_div ) is_admin = False security = Environment.get_security() if security.check_access("builtin", "view_site_admin", "allow"): is_admin = True if is_admin: from quick_box_wdg import QuickBoxWdg quick_box = QuickBoxWdg() container_div.add(quick_box) container_div.add_behavior({ 'type': 'load', 'cbjs_action': ''' spt.named_events.fire_event("close_admin_bar"); ''' }) return container_div def handle_exception(self, e): '''The tab widget is a special widget concerning exceptions because it usually represents the outer skin of the content of the web page. The titles of the tab must be displayed in order for the site to remain functional in the case of an exception''' from pyasm.widget import ExceptionWdg widget = ExceptionWdg(e) self.error_wdg = Widget() self.error_wdg.add(widget) return self.error_wdg """ def get_drag_ghost_div(self): drag_ghost_div = HtmlElement.div() drag_ghost_div.set_attr( "id", "drag_ghost_copy" ) drag_ghost_div.set_attr( "element_copied", "_NONE_" ) drag_ghost_div.add_class( "REG_ID" ) drag_ghost_div.set_style("background: #393950; color: #c2c2c2; border: solid 1px black;' \ 'text-align: left; padding: 10px;', filter: alpha(opacity=60); " + "opacity: 0.6; position: absolute; display: none; left: 0px; top: 0px;" + " z-index: 110;" ) drag_ghost_div.add("Ghost Div") return drag_ghost_div """ def get_drag_div(self): drag_div = HtmlElement.div() drag_div.set_style( "position: absolute; left: 100px; top: 400px; min-width: 400px; width: 400px; " + "background-color: white; border: solid black;") drag_handle_div = HtmlElement.div() drag_handle_div.set_style( "cursor: default; background-color: gray; border-bottom: dotted black; " + "padding: 3px; font-family: sans-serif; font-weight: bold;") # NOTE: to specify behaviors on a widget, you can use the .add_behavior() method ... use one behavior # specification dictionary (hash) string per call to add_behavior (but you can add multiple # behaviors by making multiple calls to add_behavior) # drag_handle_div.add_behavior( "{type:'click', mouse_btn:'LMB', modkeys:'SHIFT', dst_el:'@.parentNode', " + "cbfn_action: spt.cb.change_color_action}") drag_handle_div.add_behavior( "{type:'drag', mouse_btn:'LMB', modkeys:'', src_el:'@.parentNode'}" ) drag_handle_div.add("Drag Handle -- click and drag me!") drag_div.add(drag_handle_div) drag_div.add( "<p>This is a draggable div ... click on the drag handle above and drag this thing around!</p>" ) return drag_div
def execute(my): web = WebContainer.get_web() alter_mode = my.kwargs.get("alter_mode") title = my.kwargs.get("title") config_mode = web.get_form_value("config_mode") view = web.get_form_value('view') constraint = web.get_form_value("config_constraint") data_type = '' if config_mode == "advanced": config_string = web.get_form_value("config_xml") if config_string: xml = Xml() xml.read_string(config_string) node = xml.get_root_node() data_type = xml.get_attribute(node, "data_type") nullable = xml.get_attribute(node, "nullable") in ['true', 'True'] else: data_type = web.get_form_value("config_data_type") if data_type == 'Other...': data_type = web.get_form_value("config_data_type_custom") cb = CheckboxWdg("config_nullable") nullable = cb.is_checked() # if advanced is selected in the Widget Column view, data_type is '' # read from UI if not data_type and view == 'definition': data_type = web.get_form_value("config_data_type") if data_type == 'Other...': data_type = web.get_form_value("config_data_type_custom") cb = CheckboxWdg("config_nullable") nullable = cb.is_checked() column_name = web.get_form_value("column_name") search_type = web.get_form_value("target_search_type") if alter_mode == ManageSearchTypeDetailWdg.REMOVE_COLUMN: cmd = ColumnDropCmd(search_type, column_name) Command.execute_cmd(cmd) # delete widget config from definition view widget_config = WidgetDbConfig.get_by_search_type( search_type, 'definition') if widget_config: config = WidgetConfig.get( 'definition', xml=widget_config.get_xml_value('config')) config.remove_xml_element(column_name) new_xml = config.get_xml().to_string() widget_config.set_value("config", new_xml) widget_config.commit() # set cache to {} from pyasm.common import Container Container.put("WidgetConfigView:config_cache", {}) #Container.put("WidgetConfig:config_cache", {}) elif alter_mode == ManageSearchTypeDetailWdg.MODIFY_COLUMN: cmd = ColumnAlterCmd(search_type, column_name, data_type, nullable) Command.execute_cmd(cmd) element_options = {} element_options['type'] = data_type if title: element_options['title'] = title # handle the "default" view # update the widget config data type in the xml view = my.DEFAULT_VIEW config = WidgetDbConfig.get_by_search_type(search_type, view) if config: config.append_display_element(column_name, options={}, \ element_attrs=element_options) config.commit_config() elif alter_mode == ManageSearchTypeDetailWdg.ADD_COLUMN: cmd = ColumnAddCmd(search_type, column_name, data_type, nullable) Command.execute_cmd(cmd) if constraint: # add constraint from pyasm.command import ColumnAddIndexWdg cmd = ColumnAddIndexWdg() cmd.execute() else: # remove constraint pass
def get_widget_from_hashXX(cls, hash, return_none=False, force_no_index=False, kwargs={}): from pyasm.web import DivWdg if hash.startswith("//"): use_top = False hash = hash[1:] else: use_top = True import re p = re.compile("^/(\w+)") m = p.search(hash) if not m: print "Cannot parse hash[%s]" % hash return DivWdg("Cannot parse hash [%s]" % hash) key = m.groups()[0] # add some predefined ones if key == "link": expression = "/link/{link}" options = Common.extract_dict(hash, expression) # This is the standard way of communicating through main interface # It uses the link keyword to draw the main widget if use_top: top_class_name = WebEnvironment.get_top_class_name() kwargs = { "link": options.get("link") } else: top_class_name = 'tactic.ui.panel.HashPanelWdg' kwargs = { "hash": hash.replace("/link", "/tab") } widget = Common.create_from_class_path(top_class_name, [], kwargs) return widget #expression = "/link/{link}" #options = Common.extract_dict(hash, expression) #return cls.build_widget(options) elif key == 'tab': # this is called by PageNav expression = "/tab/{link}" options = Common.extract_dict(hash, expression) link = options.get("link") # test link security project_code = Project.get_project_code() security = Environment.get_security() link = options.get("link") keys = [ { "element": link }, { "element": "*" }, { "element": link, "project": project_code }, { "element": "*", "project": project_code } ] if not security.check_access("link", keys, "allow", default="deny"): widget = DivWdg() widget.add_color("color", "color") widget.add_color("background", "background3") widget.add_style("width: 600px") widget.add_style("height: 200px") widget.add_style("margin: 50px auto") widget.add_style("text-align: center") widget.add_border() widget.add("<br/>"*5) widget.add("This link [%s] either does not exist or you are not permitted to see it" % link) return widget from tactic.ui.panel import SideBarBookmarkMenuWdg personal = False if '.' in link: personal = True config = SideBarBookmarkMenuWdg.get_config("SideBarWdg", link, personal=personal) options = config.get_display_options(link) class_name = options.get("class_name") widget_key = options.get("widget_key") if widget_key: class_name = WidgetClassHandler().get_display_handler(widget_key) elif not class_name: class_name = 'tactic.ui.panel.ViewPanelWdg' widget = Common.create_from_class_path(class_name, [], options) return widget # these show only the widget without a top elif key == "encoded": expression = "/encoded/{encoded}" options = Common.extract_dict(hash, expression) encoded = options['encoded'] import json, binascii data = json.loads( binascii.unhexlify(encoded) ) class_name = data[0] args = data[1] kwargs = data[2] widget = Common.create_from_class_path(class_name, args, kwargs) return cls.build_widget(options) else: if key == "top": kwargs["use_index"] = True sobject = None else: # look up the url search = Search("config/url") search.add_filter("url", "/%s/%%"%key, "like") search.add_filter("url", "/%s"%key) search.add_where("or") sobject = search.get_sobject() if not sobject: if return_none: return None return DivWdg("No Widget found for hash [%s]" % hash) config = sobject.get_value("widget") xml = Xml() xml.read_string(config) use_index = kwargs.get("use_index") if use_index in [True, 'true']: use_index = True elif use_index in [False, 'false']: use_index = False use_admin = kwargs.get("use_admin") if use_admin in [True, 'true']: use_admin = True elif use_admin in [False, 'false']: use_admin = False use_sidebar = kwargs.get("use_sidebar") if use_sidebar in [False, 'false']: use_sidebar = False elif use_admin in [True, 'true']: use_sidebar = True if use_index is not None or use_admin is not None: pass elif force_no_index in [True, 'true']: use_index = False else: use_index = sobject.get_value("index", no_exception=True) if not use_index: use_index = xml.get_value("/element/@index"); if use_index in ['true', True]: use_index = True use_admin = sobject.get_value("admin", no_exception=True) if not use_admin: use_admin = xml.get_value("/element/@admin"); if use_admin in ['true', True]: use_admin = True use_sidebar = xml.get_value("/element/@sidebar"); if use_sidebar in ['false', False]: use_sidebar = False if use_index or use_admin: # check if there is an index search = Search("config/url") search.add_filter("url", "/index") index = search.get_sobject() if not index or use_admin: # use admin from tactic.ui.app import PageNavContainerWdg top = PageNavContainerWdg( hash=hash, use_sidebar=use_sidebar ) else: config = index.get_value("widget") xml = Xml() xml.read_string(config) node = xml.get_node("element/display") options = {} options.update(xml.get_node_values_of_children(node)) class_name = xml.get_value("element/display/@class") if class_name: options['class_name'] = class_name # this passes the hash value to the index widget # which must handle it accordingly if key == "top": hash = hash.replace("/top", "/tab") options['hash'] = hash top = cls.build_widget(options) return top.get_buffer_display() # build the widget if key == "top": class_name = 'tactic.ui.panel.HashPanelWdg' options = { "hash": hash.replace("/link", "/tab"), "class_name": class_name } else: url = sobject.get_value("url") url = url.strip() options = Common.extract_dict(hash, url) for name, value in kwargs.items(): options[name] = value node = xml.get_node("element/display") options.update(xml.get_node_values_of_children(node)) class_name = xml.get_value("element/display/@class") if class_name: options['class_name'] = class_name widget = cls.build_widget(options) name = hash.lstrip("/") name = name.replace("/", " ") widget.set_name(name) return widget
def get_text(my, path, last_path=None, highlight=True): if path.startswith("http"): if path.startswith("https://docs.google.com/document"): # NOTE: this is very specific to google docs if not path.endswith("?embedded=true"): path = "%s?embedded=true" % path is_html = True else: is_html = False import urllib2 response = urllib2.urlopen(path) html = response.read() fix = '''<meta content="text/html; charset=UTF-8" http-equiv="content-type">''' html = html.replace(fix, "") html = html.replace("&", "&") if is_html: xml = Xml() try: xml.read_string(html) except: my.doc_mode = "formatted" html = html.replace("&", "&") print print "WARNING: cannot parse as XML" print return html if my.doc_mode == "formatted": return xml.to_string().replace("&", "&") node = xml.get_node("html/body") text = xml.to_string(node) text = text.replace("<body", "<div style='margin: 0px'") text = text.replace("</body>", "</div>") text = text.replace("&", "&") else: text = html lines2 = [] lines = text.split("\n") for line in lines: tmp_line = line.strip() if tmp_line.startswith("<span"): tmp_line = tmp_line.replace("<span>", "") tmp_line = tmp_line.replace("</span>", "") tmp_line = tmp_line.replace("<span/>", "") elif tmp_line.startswith("<p "): continue elif tmp_line.startswith("</p>"): continue # FIXME: there has to be a function to do this tmp_line = tmp_line.replace(" ", "") tmp_line = tmp_line.replace(""", '"') tmp_line = tmp_line.replace("'", "'") lines2.append(tmp_line) text = "\n".join(lines2) #print 'text', text #import html2text #text = html2text.html2text(html) # clear out any remaining html tags import re text = re.sub('<[^<]+?>', '', text) else: f = open(path) lines = f.readlines() f.close() #f = open(path) #lines2 = f.readlines() #f.close() #diff = my.get_diff(lines, lines2) #text = "".join(diff) text = "".join(lines) # read last text if it exists if last_path and os.path.exists(last_path): last_file = open(last_path, 'r') last_text = last_file.read() last_file.close() else: last_text = None if last_text != None: lines = text.split("\n") lines2 = last_text.split("\n") diff = my.get_diff(lines2, lines) diff_text = "\n".join(diff) text = diff_text if highlight: search_type_obj = SearchType.get(my.search_type) color = search_type_obj.get_value("color") if not color: color = '#0F0' # assemble all the lines data = [] search = Search(my.search_type) sobjects = search.get_sobjects() for sobject in sobjects: search_key = sobject.get_search_key() value = sobject.get_value(my.column) lines = value.split("\n") for line in lines: line = line.strip() data.append([line, search_key]) for line, search_key in data: if not line: continue line = line.strip() text = text.replace( line, "<i style='color: %s; font-weight: bold; opacity: 1.0;' spt_search_key='%s' class='spt_document_item hand'>%s</i>" % (color, search_key, line)) return text
def execute(self): print "kwargs: ", self.kwargs options = {} class_name = "tactic.ui.panel.CustomLayoutWdg" widget_key = "" for key, value in self.kwargs.items(): if value == '': continue if key.startswith("xxx_option"): parts = key.split("|") option_key = parts[1] if option_key == "display_class": class_name = value elif option_key == "widget_key": widget_key = value else: options[option_key] = value elif key.startswith("option|"): parts = key.split("|") option_key = parts[1] options[option_key] = value print "options: ", options name = self.kwargs.get("name") description = self.kwargs.get("description") or " " if not name: raise Exception("No name provided") name = Common.clean_filesystem_name(name) login = Environment.get_user_name() view = "pages.%s.%s" % (login, name) # find if this user page already exists search = Search("config/widget_config") search.add_filter("category", "CustomLayoutWg") search.add_filter("view", view) config = search.get_sobject() if config: raise Exception("Page with name [%s] already exists" % name) option_xml = [] for key, value in options.items(): option_xml.append("<%s>%s</%s>" % (key, value, key)) option_str = "\n".join(option_xml) if widget_key: display_line = '''<display widget="%s">''' % widget_key else: display_line = '''<display class="%s">''' % class_name # all pages are custom layouts config_xml = '''<config> <%s> <html> <div style="margin: 20px"> <div style="font-size: 25px">%s</div> <div>%s</div> <hr/> <element> %s %s </display> </element> </div> </html> </%s> </config> ''' % (view, name, description, display_line, option_str, view) print "config_xml: ", config_xml xml = Xml() xml.read_string(config_xml) config_xml = xml.to_string() config = SearchType.create("config/widget_config") config.set_value("category", "CustomLayoutWdg") config.set_value("view", view) config.set_value("config", config_xml) config.commit()
config.set_value("config", config_xml) config.commit() if __name__ == '__main__': from pyasm.security import Batch Batch() from pyasm.common import Xml from pyasm.web import WebContainer WebContainer.get_buffer() panel = PanelWdg() html = panel.get_buffer_display() xml = Xml() xml.read_string(html) print xml.to_string()
def get_display(my): my.kwargs['search_key'] = 'prod/asset?project=sample3d&code=chr001' custom = """<?xml version='1.0' encoding='UTF-8'?> <custom> <html> <div> This is html <textarea class='spt_test spt_input' name='description'> </textarea> <input class='spt_input' type='text' name='title'/> <br/> <input class='spt_button1' type='button' value='Press Me'/> <input class='spt_button2' type='button' value='Press Me2'/> <input class='spt_button3' type='button' value='Calendar'/> <input class='spt_refresh' type='button' value='Refresh'/> <element> <display class='tactic.ui.widget.CalendarWdg'/> </element> Much simpler!!! <elemeent class='CalendarWdg' blah='adasf'/> </div> </html> <behavior class='spt_button1'>{ "type": "click_up", "cbjs_action": ''' app.mel('sphere'); //var top = bvr.src_el.getParent(".spt_panel"); //var values = spt.api.Utility.get_input_values(top); //console.log(values); ''' }</behavior> <behavior class='spt_button2'>{ "type": "click_up", "cbjs_action": "alert(bvr.kwargs.search_key);" }</behavior> <behavior class='spt_button3'>{ "type": "click_up", "cbjs_action": ''' spt.panel.load('main_body', bvr.class_name, bvr.kwargs); //spt.panel.load('main_body', 'tactic.ui.widget.CalendarWdg', bvr.kwargs); ''' }</behavior> <behavior class='spt_refresh'>{ "type": "click_up", "cbjs_action": ''' var top = bvr.src_el.getParent(".spt_panel"); spt.panel.refresh(top); ''' }</behavior> </custom> """ xml = Xml() xml.read_string(custom) top = DivWdg() my.set_as_panel(top) top.add_class("spt_panel") inner = DivWdg() top.add(inner) html_node = xml.get_node("custom/html") html = xml.to_string(html_node) inner.add(html) behaviors = xml.get_nodes("custom/behavior") for behavior in behaviors: css_class = Xml.get_attribute(behavior, 'class') value = Xml.get_node_value(behavior) value = eval(value) # add the kwargs to this so behaviors have access value['kwargs'] = my.kwargs value['class_name'] = Common.get_full_class_name(my) inner.add_behavior({ 'type': 'load', 'value': value, 'css_class': css_class, 'cbjs_action': ''' var el = bvr.src_el.getElement("."+bvr.css_class); if (!el) { alert("WARNING: element ["+bvr.css_clsss+"] does not exist"); } spt.behavior.add( el, bvr.value); ''' }) if my.kwargs.get("is_refresh"): return inner else: return top
def execute(self): web = WebContainer.get_web() # get command line options search_type = self.kwargs.get("search_type") assert search_type view = self.kwargs.get("view") if not view: view = get_template_view() # check if this is advanced mode mode = web.get_form_value("custom_mode") if not mode: mode = 'simple' if mode == 'xml': config_string = web.get_form_value("config_xml") # handle the "default" view view = DEFAULT_VIEW config = WidgetDbConfig.get_by_search_type(search_type, view) if not config: config = WidgetDbConfig.create(search_type, view) xml = Xml() xml.read_string(config_string) element_name = xml.get_value("element/@name") element_name = element_name.strip() assert element_name type = xml.get_value("element/@type") if not type: class_name = xml.get_value("element/display/@class") if not class_name: raise TacticException( "Either a type or class name needs to be defined in config xml." ) config.append_xml_element(element_name, config_string) config.commit_config() # create the required columns widget = config.get_display_widget(element_name) columns = widget.get_required_columns() if columns: print("WARNING: need to create columns: ", columns) self.info['element_name'] = element_name return type = web.get_form_value("custom_type") description = web.get_form_value("custom_description") if not description: description = "No descripton" title = web.get_form_value("custom_title") name = web.get_form_value("custom_name") name = name.strip() if not name: raise TacticException("No name specified") add_to_current_view = web.get_form_value("add_to_current_view") add_to_edit_view = web.get_form_value("add_to_edit_view") is_searchable = web.get_form_value("is_searchable") # create the column if type not in ['button', 'empty']: cmd = ColumnAddCmd(search_type, name, type) cmd.execute() # create the type class_name = None options = {} # this is actually element attrs element_options = {} edit_class_name = None edit_options = {} edit_attrs = {} element_type = type # Date Range is not used any more in the UI" if type == "Date Range": class_name = "GanttWdg" options["start_date_column"] = "%s_start_date" % name options["end_deate_column"] = "%s_end_date" % name elif type == "date": class_name = "DateWdg" #edit_class_name = "CalendarWdg" element_type = 'timestamp' edit_attrs['type'] = 'timestamp' edit_class_name = "" add_to_edit_view = True elif type == "foreign_key": class_name = "" edit_class_name = "SelectWdg" foreign_search_type = web.get_form_value( "foreign_key_search_select") edit_options["query"] = '%s|code|code' % foreign_search_type # turn on add to edit view add_to_edit_view = True elif type == "button": class_name = "tactic.ui.table.ButtonElementWdg" script = web.get_form_value("option_script_select") if script: options['script'] = script icon = web.get_form_value("option_icon_select") if icon: options['icon'] = icon edit_class_name = "" # This does not have a type element_type = None elif type == "empty": element_type = None pass elif type == "list": class_name = "" edit_class_name = "SelectWdg" list_values = web.get_form_value("list_values") edit_options['values'] = list_values add_to_edit_view = True element_options['type'] = element_type element_options['title'] = title # handle the "default" view view = DEFAULT_VIEW config = WidgetDbConfig.get_by_search_type(search_type, view) if not config: config = WidgetDbConfig.create(search_type, view) config.append_display_element(name, class_name, options=options, \ element_attrs=element_options) config.commit_config() # get the config file if add_to_current_view: config = WidgetDbConfig.get_by_search_type(search_type, view) if not config: # if it doesn't exist, the check to see, if there is a hard # coded view out there predefined_config = WidgetConfigView.get_by_search_type( search_type, view) xml = predefined_config.get_xml() # create a new db one config = WidgetDbConfig.create(search_type, view) if xml: config.set_value("config", xml.to_string()) config._init() config.append_display_element(name) config.commit_config() # TODO: Need to make this searchable using simple search ????? if is_searchable: element_options['searchable'] = 'true' # handle the "edit" if add_to_edit_view and view != "edit": config = WidgetDbConfig.get_by_search_type(search_type, "edit") if not config: config = WidgetDbConfig.create(search_type, "edit") config.append_display_element(name, edit_class_name, edit_options, element_attrs=edit_attrs) config.commit_config() """ # this sType has been deprecated sobject = SearchType.create("prod/custom_property") sobject.set_value("search_type", search_type) sobject.set_value("name", name) sobject.set_value("description", description) sobject.commit() """ # set some information self.description = "Added Property [%s] of type [%s] to [%s]" % \ (name, type, search_type) self.info['element_name'] = name
def _test_access_manager(my): # reset it Environment.get_security().reset_access_manager() access_manager = AccessManager() xml = Xml() xml.read_string(''' <rules> <rule group='sobject' key='corporate/budget' access='allow'/> <rule group='sobject' key='corporate/salary' access='allow'/> <rule group='sobject' key='prod/asset' access='edit'/> <rule group='sobject' search_type='sthpw/note' project='sample3d' access='edit'/> <group type='url' default='deny'> <rule key='/tactic/bar/Partner' access='view'/> <rule key='/tactic/bar/External' access='view'/> </group> <rule group='sobject' search_type='prod/layer' project='sample3d' access='view'/> <rule column='description' search_type='prod/shot' access='view' group='sobject_column'/> <group type='sobject_column' default='edit'> <rule key='prod/asset|director_notes' access='deny'/> <rule key='prod/asset|sensitive_data' access='deny'/> </group> <rule group='search_type' code='prod/asset' access='allow'/> <rule group='search_type' code='sthpw/note' project='unittest' access='edit'/> <rule group='search_type' code='unittest/person' project='unittest' access='allow'/> </rules> ''') access_manager.add_xml_rules(xml) # test sensitive sobject test = access_manager.get_access('sobject', 'corporate/budget') my.assertEquals(test, "allow") # test allowed sobject test = access_manager.get_access('sobject', 'prod/asset') my.assertEquals(test, "edit") test = access_manager.get_access('sobject', [{'search_type':'sthpw/note', 'project':'sample3d'}]) my.assertEquals(test, "edit") # test url test = access_manager.get_access('url', '/tactic/bar/Partner') my.assertEquals(test, "view") # test with access values ... a more typical usage test = access_manager.check_access('sobject','prod/asset','view') my.assertEquals(test, True) test = access_manager.check_access('sobject','corporate/budget','edit') my.assertEquals(test, True) test = access_manager.check_access('sobject_column', 'prod/asset|director_notes','deny') my.assertEquals(test, True) test = access_manager.check_access('sobject_column',{'search_type':'prod/shot','column':'description'},'edit') my.assertEquals(test, False) test = access_manager.check_access('sobject_column',{'search_type':'prod/shot','column':'description'},'view') my.assertEquals(test, True) test = access_manager.get_access('sobject', {'search_type':'sthpw/note', 'project':'sample3d'} ) my.assertEquals(test, "edit") test = access_manager.get_access('sobject', {'search_type':'sthpw/note'} ) my.assertEquals(test, None) test = access_manager.get_access('sobject', {'search_type':'prod/layer', 'project':'sample3d'} ) my.assertEquals(test, "view") test = access_manager.get_access('sobject', 'prod/layer' ) my.assertEquals(test, None) # security version 2 uses group = search_type asset = SearchType.create('prod/asset') asset.set_value('name','unit test obj') asset.commit(triggers=False) # replace the access manager with this Environment.get_security()._access_manager = access_manager test = access_manager.check_access('search_type',{'search_type':'prod/asset','project':'sample3d'},'delete') my.assertEquals(test, False) asset.delete() note = SearchType.create('sthpw/note') note.set_value('note','unit test note obj') note.set_value('project_code','unittest') note.commit(triggers=False) test = access_manager.get_access('search_type', [{'code':'sthpw/note', 'project':'unittest'}] ) my.assertEquals(test, 'edit') msg = '' # delete of unittest note should fail try: note.delete() except SObjectException, e: msg = 'delete error'
class Pipeline(SObject): '''Represents a pipeline of process and their relationships''' SEARCH_TYPE = "sthpw/pipeline" def __init__(self, search_type="sthpw/pipeline", columns=None, result=None, fast_data=None): super(Pipeline, self).__init__(search_type, columns, result, fast_data=fast_data) self.processes = [] self.recursive_processes = [] self.connects = {} self.pipeline_dict = {} #if columns != None: # putting no exception here to ensure that this can be put into # a select widget which no uses distinct for the value column xml_str = self.get_value("pipeline", no_exception=True) # don't cache a potential empty xml when Pipeline.create() is call if xml_str: self.set_pipeline(xml_str, cache=False) self.process_sobjects = None def get_defaults(self): '''The default, if not specified is to set the current project''' defaults = {} project_code = Project.get_project_code() defaults['project_code'] = project_code self.update_dependencies() return defaults def on_updateX(self): # initialize the triggers for the workflow """ event = "change|sthpw/pipeline" trigger = SearchType.create("sthpw/trigger") trigger.set_value("event", event) trigger.set_value("class_name", ProjectPipelineTrigger) trigger.set_value("mode", "same process,same transaction") Trigger.append_static_trigger(trigger, startup=startup) """ if self.SEARCH_TYPE == "config/pipeline": return code = self.get_value("code") search = Search("config/pipeline") search.add_filter("code", code) pipeline = search.get_sobject() if not pipeline: pipeline = SearchType.create("config/pipeline") items = self.data.items() for name, value in items: if name.startswith("__"): continue if name in ["id", "project_code"]: continue if not value: continue pipeline.set_value(name, value) pipeline.commit(triggers="none") def on_insertX(self): # Copy this to the config/pipeline table. Currently this table # is not being used, however, pipelines really should be defined # there. It is an unfortunate historical wart that pipelines # are stored in the sthpw database. In some future release of # TACTIC, the pipeline table in the sthpw database will be deprecated # This copy will ensure that over time, the impact of this move over # will be minimized if self.SEARCH_TYPE == "config/pipeline": return search = Search("config/pipeline") search.add_filter("code", self.get_code()) pipeline = search.get_sobject() if not pipeline: pipeline = SearchType.create("config/pipeline") for name, value in self.get_data().items(): if name.startswith("__"): continue if name in ["id", "project_code"]: continue if not value: continue pipeline.set_value(name, value) pipeline.commit(triggers="none") def on_deleteX(self): if self.SEARCH_TYPE == "config/pipeline": return search = Search("config/pipeline") search.add_filter("code", self.get_code()) pipeline = search.get_sobject() if pipeline: pipeline.delete() def update_dependencies(self): '''Function that should be run on insert/update. It's already automatically called during insert. On update, the caller needs to call this explicitly. It checks the search type this pipeline is associated with and if there is no pipeline code column, then update it. It updates the process table also.''' search_type = self.get_value('search_type') self.update_process_table(search_type=search_type) # don't do anything for task sType if search_type == 'sthpw/task': return if not search_type: return if ProdSetting.get_value_by_key('autofill_pipeline_code') != 'false': try: columns = SearchType.get_columns(search_type) if not 'pipeline_code' in columns: # add the pipeline code column from pyasm.command import ColumnAddCmd cmd = ColumnAddCmd(search_type, "pipeline_code", "varchar") cmd.execute() except SqlException as e: print("Error creating column [pipeline_code] for %" % search_type) pass # go through all of the sobjects and set all the empty ones # to the new pipeline search = Search(search_type) search.add_op("begin") search.add_filter("pipeline_code", "NULL", op='is', quoted=False) search.add_filter("pipeline_code", "") search.add_op("or") sobject_ids = search.get_sobject_ids() if sobject_ids: # this is much faster and memory efficient db_resource = SearchType.get_db_resource_by_search_type( search_type) sql = DbContainer.get(db_resource) tbl = search.get_table() sobject_ids = [str(x) for x in sobject_ids] pipeline_code = self.get_value("code") sql.do_update( '''UPDATE "%s" SET "pipeline_code" = '%s' WHERE id in (%s) ''' % (tbl, pipeline_code, ','.join(sobject_ids))) """ for sobject in sobjects: if not sobject.get_value("pipeline_code"): sobject.set_value("pipeline_code", self.get_value("code") ) sobject.commit(triggers=False) """ def update_process_table(self, search_type=None): ''' make sure to update process table''' template = self.get_template_pipeline() if template: if template.get_code() == self.get_code(): template_processes = [] else: template_processes = template.get_process_names() else: template_processes = [] process_names = self.get_process_names() pipeline_code = self.get_code() search = Search("config/process") search.add_filter("pipeline_code", pipeline_code) process_sobjs = search.get_sobjects() existing_names = SObject.get_values(process_sobjs, 'process') pipeline_has_updates = False count = 1 for process_name in process_names: exists = False for process_sobj in process_sobjs: # if it already exist, then update if process_sobj.get_value("process") == process_name: exists = True break if not exists: process_sobj = SearchType.create("config/process") # default to (main) for non-task status pipeline if search_type and search_type != 'sthpw/task': process_sobj.set_value('subcontext_options', '(main)') process_sobj.set_value("pipeline_code", pipeline_code) process_sobj.set_value("process", process_name) # copy information over from the template if process_name in template_processes: template_attrs = template.get_process_attrs(process_name) process = self.get_process(process_name) for name, value in template_attrs.items(): if name in ['xpos', 'ypos', 'name']: continue process.set_attribute(name, value) pipeline_has_updates = True search = Search("config/process") search.add_filter("process", process_name) # NEED ANOTHER FILTER for templates here search.add_filter("pipeline_code", "%/__TEMPLATE__", op="like") # copy certain values from the template template_process = search.get_sobject() for name, value in template_process.get_data().items(): if not value: continue if name in ['checkin_mode']: process_sobj.set_value(name, value) attrs = self.get_process_attrs(process_name) color = attrs.get('color') if color: process_sobj.set_value("color", color) process_sobj.set_value("sort_order", count) process_sobj.commit() count += 1 if pipeline_has_updates: self.set_value("pipeline", self.get_pipeline_xml().to_string()) self.commit() # delete obsolete obsolete = set(existing_names) - set(process_names) if obsolete: for obsolete_name in obsolete: for process_sobj in process_sobjs: if process_sobj.get_value("process") != obsolete_name: continue # FIXME: this node type is always None process_obj = self.get_process(obsolete_name) if process_obj: node_type = process_obj.get_type() try: from pyasm.command import CustomProcessConfig handler = CustomProcessConfig.get_delete_handler( node_type, {}) except Exception as e: handler = None if handler: handler.execute() # delete it process_sobj.delete() def get_name(self, long=False): '''this is the old function, kept for backward-compatibility''' #TODO: remove this function here return self.get_code() def set_pipeline(self, pipeline_xml, cache=True): '''set the pipeline externally''' # cache according to pipeline code, which will share the same xml object if self.is_insert(): cache = False search_key = self.get_search_key() xml_dict = Container.get("Pipeline:xml") if xml_dict == None: xml_dict = {} Container.put("Pipeline:xml", xml_dict) self.xml = xml_dict.get(search_key) if self.xml == None: self.xml = Xml() if cache: xml_dict[search_key] = self.xml if not pipeline_xml: pipeline_xml = "<pipeline/>" try: self.xml.read_string(pipeline_xml) except XmlException as e: self.xml.read_string("<pipeline/>") # clear these again when set externally self.processes = [] self.recursive_processes = [] # create the process and pipelines process_nodes = self.xml.get_nodes( "pipeline/process | pipeline/pipeline") for node in process_nodes: node_name = self.xml.get_node_name(node) process = Process(node) process.set_parent_pipeline_code(self.get_code()) self.processes.append(process) if node_name == "pipeline": name = Xml.get_attribute(node, "name") # prevent infinite loop if name == self.get_code(): continue child = Pipeline.get_by_code(name) if not child: continue self.pipeline_dict[name] = child process.set_child_pipeline(child) def get_pipeline_xml(self): return self.xml def to_string(self): return self.xml.to_string() def get_process(self, name): '''returns a Process object''' if type(name) not in types.StringTypes: name = name.get_name() # first try the top level for process in self.processes: if process.get_name() == name: return process # Then iterate. This may be slow processes = self.get_processes(recurse=True) for process in processes: if process.get_full_name() == name: return process return None #raise PipelineException( "Pipeline '%s' does not have process '%s'" % \ # (self.get_name(),name) ) def get_processes(self, recurse=False, type=None): '''returns all the Process objects in this pipeline''' if type and isinstance(type, basestring): types = [type] else: types = type if recurse: if self.recursive_processes: return self.recursive_processes else: # add child processes for process in self.processes: if types and process.get_type() not in types: continue self.recursive_processes.append(process) child_pipeline = process.get_child_pipeline() if not child_pipeline: continue child_processes = child_pipeline.get_processes( recurse=recurse) for x in child_processes: x.set_sub_pipeline_process(True) self.recursive_processes.extend(child_processes) return self.recursive_processes else: if types: ret_processes = [] for process in self.processes: if process.get_type() not in types: continue ret_processes.append(process) return ret_processes else: return self.processes def get_process_attrs(self, name): process = self.get_process(name) if process: return process.get_attributes() else: return {} def get_process_names(self, recurse=False, type=None): '''returns all the Process names in this pipeline''' if type and isinstance(type, basestring): types = [type] else: types = type processes = self.get_processes(recurse, type=types) if recurse: process_names = [] for process in processes: if types and process.get_type() not in types: continue if process.is_from_sub_pipeline(): process_names.append(process.get_full_name()) else: process_names.append(process.get_name()) return process_names else: return [process.get_name() for process in processes] def get_process_sobject(self, process): # search all processes and cache all of the sobject locally if self.process_sobjects == None: search = Search("config/process") search.add_filter("pipeline_code", self.get_code()) sobjects = search.get_sobjects() self.process_sobjects = {} for process_sobject in sobjects: # prevent changing variable process pcs = process_sobject.get("process") self.process_sobjects[pcs] = process_sobject process_sobject = self.process_sobjects.get(process) return process_sobject def get_process_sobjects(self): process_name = "dummy" self.get_process_sobject(process_name) return self.process_sobjects def get_index(self, name): index = 0 for process in self.processes: if process.get_name() == name: return index index += 1 def _get_connects(self, process="", direction='from'): if direction == "from": opposite = "to" else: opposite = "from" if not process: connect_nodes = self.xml.get_nodes("pipeline/connect") else: connect_nodes = self.xml.get_nodes(\ "pipeline/connect[@%s='%s' or @%s='*']" % (direction, process, direction)) connects = [] for node in connect_nodes: opposite_name = Xml.get_attribute(node, opposite) full_name = "%s/%s" % (self.get_name(), opposite_name) if process == opposite_name or process == full_name: continue connects.append(ProcessConnect(node)) return connects def get_input_processes(self, process, type=None, to_attr=None): connects = self._get_connects(process, direction='to') processes = [] for connect in connects: if to_attr: connect_to_attr = connect.get_attr("to_attr") if connect_to_attr != to_attr: continue from_connect = connect.get_from() process = self.get_process(from_connect) if process: if type and process.get_type() != type: continue processes.append(process) return processes def get_input_process_names(self, process, type=None, from_attr=None): input_processes = self.get_input_processes(process, type, from_attr) process_names = [x.get_name() for x in input_processes] return process_names def get_output_processes(self, process, type=None, from_attr=None): connects = self._get_connects(process, direction="from") if not connects: return [] processes = [] for connect in connects: # make sure there are no empty contexts to = connect.get_to() if from_attr: connect_from_attr = connect.get_attr("from_attr") if connect_from_attr != from_attr: continue to_pipeline = connect.get_to_pipeline() if to_pipeline: pipeline = Pipeline.get_by_code(to_pipeline) process = pipeline.get_process(to) if type and process.get_type() != type: continue if process: processes.append(process) else: process = self.get_process(to) if process: processes.append(process) return processes def get_output_process_names(self, process, type=None, from_attr=None): output_processes = self.get_output_processes(process, type, from_attr) process_names = [x.get_name() for x in output_processes] return process_names def get_output_contexts(self, process, show_process=False): connects = self._get_connects(process, direction="from") if not connects: if show_process: data = (None, process) else: data = process return [data] contexts = [] for connect in connects: # make sure there are no empty contexts context = connect.get_context() if not context: context = connect.get_to() if show_process: data = (connect.get_to(), context) else: data = context contexts.append(data) return contexts def get_input_contexts(self, process, show_process=False): connects = self._get_connects(process, direction='to') contexts = [] for connect in connects: # make sure there are no empty contexts context = connect.get_context() if not context: context = connect.get_from() if show_process: data = (connect.get_from(), context) else: data = context contexts.append(data) return contexts def get_group(self, process_name): process = self.get_process(process_name) return process.get_group() def get_input_connects(self, process): connects = self._get_connects(process, direction="to") if not connects: return [] else: return connects def get_output_connects(self, process): connects = self._get_connects(process, direction="from") if not connects: return [] else: return connects # DEPRECATED def get_forward_connects(self, process): connects = self._get_connects(process) process_names = [] for connect in connects: process_names.append(connect.get_to()) return process_names # DEPRECATED def get_backward_connects(self, process): connects = self._get_connects(process, direction='to') process_names = [] for connect in connects: process_names.append(connect.get_from()) return process_names def get_all_contexts(self): connects = self._get_connects() contexts = [] for connect in connects: context = connect.get_context() if context not in contexts: contexts.append(context) return contexts # # support for new pipeline methods # def get_input_snapshots(self, sobject, process_name, input_name, version='latest'): '''gets the snapshots of the input''' assert version in ['latest', 'current'] process_node = self.xml.get_node( "pipeline/process[@name='%s']/input[@name='%s']" % (process_name, input_name)) search_type = Xml.get_attribute(process_node, "search_type") context = Xml.get_attribute(process_node, "context") filter = Xml.get_attribute(process_node, "filter") # get the sobjects sobjects = sobject.get_all_children(search_type) # get the snapshots search = Search("sthpw/snapshot") search.add_filter('context', context) #if version == 'latest': # search.add_filter("is_latest", 1) #elif version == 'current': # search.add_filter("is_current", 1) # build filters for search_type, search_id combinations filters = [] for sobject in sobjects: filter = "(\"search_type\" = '%s' and \"search_id\" = %s)" % ( sobject.get_search_type(), sobject.get_id()) filters.append(filter) search.add_where("( %s )" % " or ".join(filters)) snapshots = search.get_sobjects() return snapshots # # Static methods # def create(name, desc, search_type, xml=None, code=None, color=None): '''will only create if it does not exist, otherwise it just updates''' if code: sobject = Pipeline.get_by_code(code) else: sobject = None if sobject == None: #sobject = Pipeline( Pipeline.SEARCH_TYPE ) sobject = SearchType.create(Pipeline.SEARCH_TYPE) else: return sobject if not xml: xml = Xml() xml.create_doc('pipeline') if isinstance(xml, basestring): xml_string = xml xml = Xml() xml.read_string(xml_string) sobject.set_value("pipeline", xml.get_xml()) sobject.set_pipeline(xml.to_string()) sobject.set_value('timestamp', Sql.get_default_timestamp_now(), quoted=False) if code: sobject.set_value('code', code.strip()) sobject.set_value('name', name.strip()) sobject.set_value('search_type', search_type) sobject.set_value('description', desc) if color: sobject.set_value("color", color) sobject.commit() process_names = sobject.get_process_names() for i, process_name in enumerate(process_names): process = SearchType.create("config/process") process.set_value("pipeline_code", sobject.get_code()) process.set_value("process", process_name) process.set_value("sort_order", i) process.set_value("subcontext_options", "(main)") process.commit() return sobject create = staticmethod(create) def get_by_code(cls, code, allow_default=False): '''it is fatal not to have a pipeline, so put a default''' if not code: return None # first look at project specific pipeline pipeline = Search.get_by_code("config/pipeline", code) if not pipeline: pipeline = super(Pipeline, cls).get_by_code(code) if not pipeline: if code == 'task': # Remap this to a default from projects settings task_code = ProjectSetting.get_by_key("task_pipeline") if not task_code: task_code = "task" # Create a default task pipeline pipeline = SearchType.create("sthpw/pipeline") pipeline.set_value("code", task_code) from pyasm.biz import Task xml = Task.get_default_task_xml() pipeline.set_value("pipeline", xml) pipeline.set_pipeline(xml) pipeline.set_value("search_type", "sthpw/task") elif code == 'approval': # Create a default task approval pipeline pipeline = SearchType.create("sthpw/pipeline") pipeline.set_value("code", "approval") from pyasm.biz import Task xml = Task.get_default_approval_xml() pipeline.set_value("pipeline", xml) pipeline.set_pipeline(xml) pipeline.set_value("search_type", "sthpw/task") elif code == 'dependency': pipeline = SearchType.create("sthpw/pipeline") pipeline.set_value("code", "dependency") from pyasm.biz import Task xml = Task.get_default_dependency_xml() pipeline.set_value("pipeline", xml) pipeline.set_pipeline(xml) pipeline.set_value("search_type", "sthpw/task") elif code == 'progress': pipeline = SearchType.create("sthpw/pipeline") pipeline.set_value("code", "progress") from pyasm.biz import Task xml = Task.get_default_dependency_xml() pipeline.set_value("pipeline", xml) pipeline.set_pipeline(xml) pipeline.set_value("search_type", "sthpw/task") elif code == 'milestone': pipeline = SearchType.create("sthpw/pipeline") pipeline.set_value("code", "milestone") from pyasm.biz import Task xml = Task.get_default_milestone_xml() pipeline.set_value("pipeline", xml) pipeline.set_pipeline(xml) pipeline.set_value("search_type", "sthpw/milestone") elif code == 'snapshot': pipeline = SearchType.create("sthpw/pipeline") pipeline.set_value("code", "snapshot") from pyasm.biz import Task xml = Task.get_default_snapshot_xml() pipeline.set_value("pipeline", xml) pipeline.set_pipeline(xml) pipeline.set_value("search_type", "sthpw/snapshot") if not pipeline and allow_default: search = Search(cls) search.add_filter('code', 'default') pipeline = search.get_sobject() if not pipeline: pipeline = cls.create('default', \ 'default pipeline', '') xml = pipeline.get_xml_value("pipeline") # create a default process for the table root = xml.get_root_node() element = xml.create_element("process") Xml.set_attribute(element, "name", "default_process") Xml.append_child(root, element) pipeline.set_value('pipeline', xml.get_xml()) pipeline.commit() # set the pipeline pipeline.set_pipeline(pipeline.get_value('pipeline')) Environment.add_warning("pipeline autogenerated", \ "[default] pipeline has just been created.") # Sometimes, a pipeline is instantiated without calling set_pipeline() # to be looked into if pipeline and not pipeline.get_processes(): pipeline.set_pipeline(pipeline.get_value('pipeline')) return pipeline get_by_code = classmethod(get_by_code) # DEPRECATED def get_by_name(name): ''' for backward-compatibility, name has been renamed as code ''' return Pipeline.get_by_code(name) get_by_name = staticmethod(get_by_name) def get_by_search_type(cls, search_type, project_code=''): # make sure this is a be search type assert search_type search_type_obj = SearchType.get(search_type) if not search_type_obj: return [] search_type = search_type_obj.get_base_key() cache_key = "%s|%s" % (search_type, project_code) # commenting out until we have a full implementation of # project pipelines """ search = Search("config/pipeline") if search_type: search.add_filter("search_type", search_type) search.add_project_filter(project_code) pipelines = cls.get_by_search(search, cache_key, is_multi=True) """ search = Search("sthpw/pipeline") if search_type: search.add_filter("search_type", search_type) search.add_project_filter(project_code) pipelines = cls.get_by_search(search, cache_key, is_multi=True) if not pipelines: return [] for pipe in pipelines: code = pipe.get_code() cls.cache_sobject('sthpw/pipeline|%s' % code, pipe) return pipelines get_by_search_type = classmethod(get_by_search_type) def get_process_name_dict(search_type, project_code='', is_group_restricted=False, sobject=None): '''get process names for pipelines with a particular search type''' pipes = [] if sobject: pipe_code = sobject.get_value('pipeline_code', no_exception=True) if pipe_code: pipe = Pipeline.get_by_code(pipe_code) if pipe: pipes = [pipe] if not pipes: pipes = Pipeline.get_by_search_type(search_type, project_code) process_name_dict = {} my_group_names = LoginGroup.get_group_names() if pipes: for pipe in pipes: visible_process_names = [] process_names = pipe.get_process_names(recurse=True) if is_group_restricted: for process_name in process_names: group_name = pipe.get_group(process_name) if group_name and group_name not in my_group_names: continue else: visible_process_names.append(process_name) else: visible_process_names.extend(process_names) process_name_dict[pipe.get_code()] = visible_process_names return process_name_dict get_process_name_dict = staticmethod(get_process_name_dict) def get_default(): return Pipeline.get_by_code("default") get_default = staticmethod(get_default) def get_process_select_data(search_type, extra_process=[], project_code='', is_filter=False, is_group_restricted=False, sobject=None): '''get a tuple of data used for the SelectWdg''' context_dict = Pipeline.get_process_name_dict(search_type, project_code, is_group_restricted, sobject=sobject) labels = [] values = [] keys = context_dict.keys() keys.sort() process_values = Common.sort_dict(context_dict) for idx, value in enumerate(process_values): key = keys[idx] labels.append('<< %s >>' % key) if is_filter: # add all the process names in this pipeline into the value values.append(','.join(value)) else: values.append('') # extra process may not be needed if extra_process: value.extend(extra_process) if len(context_dict) > 1 and idx < len(context_dict) - 1: value.append('') values.extend(value) labels.extend(value) return labels, values get_process_select_data = staticmethod(get_process_select_data) def get_by_sobject(sobject, allow_default=False): ''' get the pipeline of the sobject''' pipeline_name = '' if not sobject: return None if sobject.has_value("pipeline_code"): pipeline_name = sobject.get_value("pipeline_code") elif sobject.has_value("pipeline"): pipeline_name = sobject.get_value("pipeline") pipeline = Pipeline.get_by_code(pipeline_name, allow_default=allow_default) return pipeline get_by_sobject = staticmethod(get_by_sobject) def get_template_pipeline(cls, search_type=None): search = Search("sthpw/pipeline") search.add_filter("name", "VFX Processes") pipeline = search.get_sobject() return pipeline get_template_pipeline = classmethod(get_template_pipeline) def create_pipeline_xml(cls, statuses, process_types=[], process_xpos=[], process_ypos=[]): '''create regular pipeline with process_types, xpos, ypos or plain task status pipeline''' if not statuses: statuses = [] xml = [] xml.append('''<pipeline>''') if process_types: for i, status in enumerate(statuses): if status == '': continue process_type = process_types[i] if len(process_xpos) > i: xpos = process_xpos[i] else: xpos = None if len(process_ypos) > i: ypos = process_ypos[i] else: ypos = None if xpos and ypos: xml.append( ''' <process name="%s" type="%s" xpos="%s" ypos="%s"/>''' % (status, process_type, xpos, ypos)) else: xml.append(''' <process name="%s" type="%s"/>''' % (status, process_type)) else: for status in statuses: if status == '': continue xml.append(''' <process name="%s"/>''' % status) last_status = None for i, status in enumerate(statuses): if status == '': continue if i == 0 or last_status == None: last_status = status continue xml.append(''' <connect from="%s" to="%s"/>''' % (last_status, status)) last_status = status xml.append('''</pipeline>''') return "\n".join(xml) create_pipeline_xml = classmethod(create_pipeline_xml)
class PageNavContainerWdg(BaseRefreshWdg): def init(self): link = self.kwargs.get('link') hash = self.kwargs.get('hash') self.widget = None if link: from tactic.ui.panel import SideBarBookmarkMenuWdg personal = False if '.' in link: personal = True config = SideBarBookmarkMenuWdg.get_config("SideBarWdg", link, personal=personal) options = config.get_display_options(link) # this is vital for view saving element_name = link attr_dict = config.get_element_attributes(link) title = attr_dict.get('title') hash = "/tab/%s" % link config = ''' <config> <application> <element name="left_nav"> <display class="tactic.ui.panel.SideBarPanelWdg"> </display> </element> <element name="main_body"> <display class="tactic.ui.panel.HashPanelWdg"> <hash>%s</hash> <element_name>%s</element_name> <title>%s</title> </display> <web/> </element> </application> </config> ''' % (hash, element_name, title) elif hash: from tactic.ui.panel import HashPanelWdg self.widget = HashPanelWdg.get_widget_from_hash(hash, force_no_index=True) config = None else: security = Environment.get_security() start_link = security.get_start_link() if start_link: self.kwargs['link'] = start_link return self.init() # search for a defined welcome view search = Search("config/widget_config") search.add_filter("category", "top_layout") search.add_filter("view", "welcome") config_sobj = search.get_sobject() if config_sobj: config = config_sobj.get_value("config") else: config = WidgetSettings.get_value_by_key("top_layout") if not config: config = self.get_default_config() self.config_xml = Xml() self.config_xml.read_string(config) def get_default_config(self): use_sidebar = self.kwargs.get('use_sidebar') if use_sidebar==False: config = ''' <config> <application> <element name="main_body"> <display class="tactic.ui.startup.MainWdg"/> <web/> </element> </application> </config> ''' else: config = ''' <config> <application> <element name="left_nav"> <display class="tactic.ui.panel.SideBarPanelWdg"> <auto_size>True</auto_size> </display> </element> <element name="main_body"> <display class="tactic.ui.startup.MainWdg"/> <web/> </element> </application> </config> ''' return config def set_state(self, panel_name, widget_class, options, values): '''this is called by side_bar.js mostly''' # set the class name display_node = self.config_xml.get_node("config/application/element[@name='%s']/display" % (panel_name) ) self.config_xml.set_attribute(display_node, "class", widget_class) # remove all the old options #display_node = self.config_xml.get_node("config/application/element[@name='%s']/display" % panel_name ) for child_node in self.config_xml.get_children(display_node): self.config_xml.remove_child(display_node, child_node) # set the options for name, value in options.items(): node = self.config_xml.get_node("config/application/element[@name='%s']/display/%s" % (panel_name, name) ) if isinstance( value, basestring ): #print("WARNING: set application: skipping [%s] with value [%s]" % (name, value)) #continue element = self.config_xml.create_text_element(name, value) self.config_xml.append_child(display_node, element) elif isinstance( value, dict): # if it is a dictionary # TODO: run recursively.. supports 2 level only now sub_element = self.config_xml.create_element(name) self.config_xml.append_child(display_node, element) for name2, value2 in value.items(): if isinstance(value2, dict): sub_element2 = self.config_xml.create_element(name2) self.config_xml.append_child(sub_element, sub_element2) for name3, value3 in value2.items(): element = self.config_xml.create_text_element(name3, value3) self.config_xml.append_child(sub_element2, element) else: element = self.config_xml.create_text_element(name2, value2) self.config_xml.append_child(sub_element, element) # web value node value_node = self.config_xml.get_node("config/application/element[@name='%s']/web" % (panel_name) ) if value_node != None: for child_node in self.config_xml.get_children(value_node): self.config_xml.remove_child(value_node, child_node) else: # create it value_node = self.config_xml.create_element('web') element_node = self.config_xml.get_node("config/application/element[@name='%s']" % (panel_name) ) self.config_xml.append_child(element_node, value_node) # set the values for name, value in values.items(): node = self.config_xml.get_node("config/application/element[@name='%s']/web/%s" % (panel_name, name) ) if not isinstance(value, basestring): print("WARNING: set application: skipping [%s] with value [%s]" % (name, value)) continue element = self.config_xml.create_text_element(name, value) self.config_xml.append_child(value_node, element) WidgetSettings.set_key_values("top_layout", [self.config_xml.to_string()]) def get_state(self): return self.config_xml def get_side_bar_cache(self, left_nav_wdg): project = Project.get() project_code = project.get_code() # do it with sobject #key = "%s_side_bar" % project.get_code() #cache = Search.get("sthpw/widget_cache") #cache.add_filter("key", key) #sobject = cache.get_sobject() #value = sobject.get_value("cache") login = Environment.get_user_name() tmp_dir = "%s/cache/side_bar" % Environment.get_tmp_dir() filename = "%s__%s.html" % (project_code, login) path = "%s/%s" % (tmp_dir, filename) # use files import os if os.path.exists(path): f = open(path, "r") html = f.read() f.close() else: dirname = os.path.dirname(path) if not os.path.exists(dirname): os.makedirs(dirname) f = open(path, "w") html = left_nav_wdg.get_buffer_display() f.write(html) f.close() return html def get_display(self): is_admin_project = Project.get().is_admin() security = Environment.get_security() if is_admin_project and not security.check_access("builtin", "view_site_admin", "allow"): return Error403Wdg() # create the elements config = WidgetConfig.get(xml=self.config_xml, view="application") left_nav_handler = config.get_display_handler("left_nav") left_nav_options = config.get_display_options("left_nav") view_side_bar = None if left_nav_handler: left_nav_wdg = Common.create_from_class_path(left_nav_handler, [], left_nav_options) # caching side_bar_cache = self.get_side_bar_cache(left_nav_wdg) else: view_side_bar = False # create the main table core_table = Table() core_table.add_tbody() core_table.set_style("border: 0px; border-collapse: collapse; width: 100%;") # add a spacer row #spacer_tr = core_table.add_row() #spacer_tr.add_style("width: 100%") #td = core_table.add_cell() #td.set_style("min-height: 1px; height: 1px;") #core_table.add_cell() #core_table.add_cell() # determine if the side bar is visible if view_side_bar == None: view_side_bar = security.check_access("builtin", "view_side_bar", "allow", default='allow') # add the main cells tr, td = core_table.add_row_cell() td.add_style("padding: 0px") td.add_style("margin: 0px") # add the main resizable table from tactic.ui.container import ResizableTableWdg main_table = ResizableTableWdg() main_table.set_keep_table_size() main_table.add_style("width: 100%") td.add(main_table) left_nav_td = main_table.add_cell() if view_side_bar: left_nav_td.add_class("spt_panel") left_nav_td.add_style("padding: 0px") main_body_td = main_table.add_cell(resize=False) main_body_td.add_style("padding: 10px") main_body_td.set_style( "width: 100%; vertical-align: top; text-align: center; padding-top: 3px" ) if view_side_bar: left_nav_td.set_style( "vertical-align: top" ) # create the left navigation panel left_nav_div = DivWdg() left_nav_td.add(left_nav_div) left_nav_div.set_id("side_bar" ) # add the detail to the panel left_nav_div.add_attr("spt_class_name", left_nav_handler) for name, value in left_nav_options.items(): left_nav_div.add_attr("spt_%s" % name, value) left_nav_div.add_style("max_width: 185px") left_nav_div.add_style("width: 185px") left_nav_div.add_style("text-align: right") left_nav_div.add_style("vertical-align: top") left_nav_div.add_style("overflow: hidden") left_nav_div.add_class("spt_resizable") side_bar_inner = DivWdg() left_nav_div.add(side_bar_inner) #side_bar_inner.add_style("padding-left: 1px") side_bar_inner.add_style("width: 100%") # add side bar to nav side_bar_inner.add(side_bar_cache) left_nav_div.add_style("border-style: solid") left_nav_div.add_style("border-width: 0px 1px 0px 0px") #left_nav_div.add_color("border-color", "border") left_nav_div.add_color("border-color", "border", -10) web = WebContainer.get_web() browser = web.get_browser() if browser in ['Qt','Webkit']: min_width = "1px" else: min_width = "0px" left_nav_div.add_behavior( { 'type': 'listen', 'event_name': 'side_bar|hide_now', 'min_width': min_width, 'cbjs_action': ''' var size = bvr.src_el.getSize(); bvr.src_el.setAttribute("spt_size", size.x); bvr.src_el.setStyle("width", bvr.min_width); ''' } ) left_nav_div.add_behavior( { 'type': 'listen', 'event_name': 'side_bar|hide', 'min_width': min_width, 'cbjs_action': ''' var size = bvr.src_el.getSize(); bvr.src_el.setAttribute("spt_size", size.x); new Fx.Tween(bvr.src_el, {duration:'short'}).start('width', bvr.min_width); ''' } ) left_nav_div.add_behavior( { 'type': 'listen', 'event_name': 'side_bar|show', 'min_width': min_width, 'cbjs_action': ''' var width = bvr.src_el.getAttribute("spt_size"); if (!width) { width = 185; } if (parseInt(width) < 5) { width = 185; } //bvr.src_el.setStyle("width", width + "px"); new Fx.Tween(bvr.src_el, {duration:'short'}).start('width', bvr.min_width, width+"px"); ''' } ) left_nav_div.add_behavior( { 'type': 'listen', 'event_name': 'side_bar|toggle', 'cbjs_action': ''' var size = bvr.src_el.getSize(); if (size.x < 5) { spt.named_events.fire_event("side_bar|show", {} ); } else { spt.named_events.fire_event("side_bar|hide", {} ); } ''' } ) # create the main body panel palette = WebContainer.get_palette() color = palette.color("background2") main_body_rounded = DivWdg() main_body_inner = main_body_rounded main_body_inner.add_style("min-height: 500px") # DEBREACATED """ # add a breadcrumb breadcrumb_wdg = DivWdg() # hide the breadcrumb breadcrumb_wdg.add_style("display", "none") Container.put("breadcrumb", breadcrumb_wdg) breadcrumb_wdg.set_id("breadcrumb") breadcrumb_wdg.add_style("text-align: left") breadcrumb_wdg.add_looks( "fnt_title_3" ) main_body_inner.add(breadcrumb_wdg) """ main_body_panel = DivWdg() main_body_panel.set_id("main_body") main_body_panel.add_class("spt_main_panel") main_body_inner.add(main_body_panel) tab = MainBodyTabWdg() main_body_panel.add(tab) # TEST: NEW LAYOUT if Config.get_value("install", "layout") == "fixed": main_body_panel.add_style("margin-top: 31px") main_body_rounded.add_color("background", "background") main_body_rounded.add_style("padding: 3px 0px 0px 0px") # add the content to the main body panel try: if self.widget: tab.add(self.widget) element_name = self.widget.get_name() else: main_body_handler = config.get_display_handler("main_body") main_body_options = config.get_display_options("main_body") element_name = main_body_options.get("element_name") title = main_body_options.get("title") main_body_content = Common.create_from_class_path(main_body_handler, [], main_body_options) # get the web values from top_layout main_body_values = config.get_web_options("main_body") web = WebContainer.get_web() if isinstance(main_body_values, dict): for name, value in main_body_values.items(): web.set_form_value(name, value) main_body_content.set_name(element_name) tab.add(main_body_content, element_name, title) self.set_as_panel(main_body_panel, class_name=main_body_handler, kwargs=main_body_options) main_body_panel.add_behavior( { 'type': 'load', 'element_name': element_name, 'cbjs_action': ''' if (spt.help) spt.help.set_view(bvr.element_name); ''' } ) except Exception as e: # handle an error in the drawing buffer = self.get_buffer_on_exception() error_wdg = self.handle_exception(e) main_body_content = DivWdg() main_body_content.add(error_wdg) main_body_content = main_body_content.get_buffer_display() tab.add(main_body_content, element_name, title) # add the main container container_div = DivWdg() container_div.set_style("width: 100%;") # NOTE: the td should not be the sliding element! So we create a div inside the TD to be the sliding element main_body_div = DivWdg() main_body_div.set_id("horizontal_main_body_slider") main_body_div.add(main_body_inner) """ # get the global drag_ghost_div drag_ghost_div = self.get_drag_ghost_div() drag_ghost_div.set_id( "drag_ghost_copy" ) drag_ghost_div.add_class( "SPT_PUW" ) # make it a Page Utility Widget (now processed client side) drag_ghost_div.set_z_start( 400 ) """ from page_header_wdg import PageHeaderWdg header_panel = DivWdg() header_panel.set_id("main_header") header_panel.add_attr("spt_class_name", "tactic.ui.app.PageHeaderWdg") header_wdg = PageHeaderWdg() header_panel.add(header_wdg) container_div.add( header_panel.get_buffer_display() ) main_body_dis = main_body_div.get_buffer_display() main_body_td.add(main_body_dis) container_div.add( core_table ) #container_div.add( drag_ghost_div ) is_admin = False security = Environment.get_security() if security.check_access("builtin", "view_site_admin", "allow"): is_admin = True if is_admin: from quick_box_wdg import QuickBoxWdg quick_box = QuickBoxWdg() container_div.add(quick_box) container_div.add_behavior( { 'type': 'load', 'cbjs_action': ''' spt.named_events.fire_event("close_admin_bar"); ''' } ) return container_div def handle_exception(self, e): '''The tab widget is a special widget concerning exceptions because it usually represents the outer skin of the content of the web page. The titles of the tab must be displayed in order for the site to remain functional in the case of an exception''' from pyasm.widget import ExceptionWdg widget = ExceptionWdg(e) self.error_wdg = Widget() self.error_wdg.add(widget) return self.error_wdg """ def get_drag_ghost_div(self): drag_ghost_div = HtmlElement.div() drag_ghost_div.set_attr( "id", "drag_ghost_copy" ) drag_ghost_div.set_attr( "element_copied", "_NONE_" ) drag_ghost_div.add_class( "REG_ID" ) drag_ghost_div.set_style("background: #393950; color: #c2c2c2; border: solid 1px black;' \ 'text-align: left; padding: 10px;', filter: alpha(opacity=60); " + "opacity: 0.6; position: absolute; display: none; left: 0px; top: 0px;" + " z-index: 110;" ) drag_ghost_div.add("Ghost Div") return drag_ghost_div """ def get_drag_div(self): drag_div = HtmlElement.div() drag_div.set_style( "position: absolute; left: 100px; top: 400px; min-width: 400px; width: 400px; " + "background-color: white; border: solid black;" ) drag_handle_div = HtmlElement.div() drag_handle_div.set_style( "cursor: default; background-color: gray; border-bottom: dotted black; " + "padding: 3px; font-family: sans-serif; font-weight: bold;" ) # NOTE: to specify behaviors on a widget, you can use the .add_behavior() method ... use one behavior # specification dictionary (hash) string per call to add_behavior (but you can add multiple # behaviors by making multiple calls to add_behavior) # drag_handle_div.add_behavior( "{type:'click', mouse_btn:'LMB', modkeys:'SHIFT', dst_el:'@.parentNode', " + "cbfn_action: spt.cb.change_color_action}" ) drag_handle_div.add_behavior( "{type:'drag', mouse_btn:'LMB', modkeys:'', src_el:'@.parentNode'}" ) drag_handle_div.add("Drag Handle -- click and drag me!") drag_div.add( drag_handle_div ) drag_div.add( "<p>This is a draggable div ... click on the drag handle above and drag this thing around!</p>" ) return drag_div
def _test_access_manager(my): # reset it Environment.get_security().reset_access_manager() access_manager = Environment.get_security().get_access_manager() xml = Xml() xml.read_string(''' <rules> <rule group='sobject' key='corporate/budget' access='allow'/> <rule group='sobject' key='corporate/salary' access='allow'/> <rule group='sobject' key='prod/asset' access='edit'/> <rule group='sobject' search_type='sthpw/note' project='sample3d' access='edit'/> <group type='url' default='deny'> <rule key='/tactic/bar/Partner' access='view'/> <rule key='/tactic/bar/External' access='view'/> </group> <rule group='sobject' search_type='prod/layer' project='sample3d' access='view'/> <rule column='description' search_type='prod/shot' access='view' group='sobject_column'/> <group type='sobject_column' default='edit'> <rule key='prod/asset|director_notes' access='deny'/> <rule key='prod/asset|sensitive_data' access='deny'/> </group> <rule group='search_type' code='prod/asset' access='allow'/> <rule group='search_type' code='sthpw/note' project='unittest' access='edit'/> <rule group='search_type' code='unittest/person' project='unittest' access='allow'/> <rule group='builtin' key='view_site_admin' access='allow'/> <rule group='builtin' key='export_all_csv' project='unittest' access='allow'/> <rule group='builtin' key='import_csv' access='allow'/> <rule group='builtin' key='retire_delete' project='*' access='allow'/> <rule group='builtin' key='view_side_bar' access='allow'/> </rules> ''') access_manager.add_xml_rules(xml) # try mixing in a 2nd login_group rule with a project override, mimmicking a # login_group with project_code. but project group is special it doesn't get the usual # project_override treatment xml2 = Xml() xml2.read_string(''' <rules> <rule group="project" code="sample3d" access="allow"/> <rule group="project" code="unittest" access="allow"/> <rule group='builtin' key='view_side_bar' project='sample3d' access='allow'/> </rules> ''') access_manager.add_xml_rules(xml2) access_manager.print_rules('project') test = access_manager.check_access('builtin', 'view_site_admin', 'allow') my.assertEquals(test, True) Project.set_project('sample3d') test = access_manager.check_access('builtin', 'export_all_csv', 'allow') my.assertEquals(test, False) # old way of checking project test = access_manager.check_access('project', 'sample3d', 'allow') my.assertEquals(test, True) Project.set_project('unittest') # old way should work as well test = access_manager.check_access('builtin', 'export_all_csv', 'allow') my.assertEquals(test, True) # default to the system's hardcoded deny for builtin test = access_manager.check_access('builtin', 'export_all_csv', 'allow', default='deny') my.assertEquals(test, True) # this is the new way to control per project csv export keys = [{ 'key': 'export_all_csv', 'project': 'unittest' }, { 'key': 'export_all_csv', 'project': '*' }] test = access_manager.check_access('builtin', keys, 'allow') my.assertEquals(test, True) keys = [{ 'key': 'import_csv', 'project': '*' }, { 'key': 'import_csv', 'project': Project.get_project_code() }] test = access_manager.check_access('builtin', keys, 'allow') my.assertEquals(test, True) test = access_manager.check_access('builtin', 'view_side_bar', 'allow') my.assertEquals(test, True) key = {"project": 'unittest', 'key': 'view_side_bar'} key1 = {"project": 'sample3d', 'key': 'view_side_bar'} key2 = {"project": "*", 'key': 'view_side_bar'} keys = [key, key2] test = access_manager.check_access('builtin', keys, 'allow') my.assertEquals(test, True) keys = [key1, key2] test = access_manager.check_access('builtin', keys, 'allow') my.assertEquals(test, True) test = access_manager.check_access('builtin', 'retire_delete', 'allow') my.assertEquals(test, True) # test sensitive sobject test = access_manager.get_access('sobject', 'corporate/budget') my.assertEquals(test, "allow") # test allowed sobject test = access_manager.get_access('sobject', 'prod/asset') my.assertEquals(test, "edit") test = access_manager.get_access('sobject', [{ 'search_type': 'sthpw/note', 'project': 'sample3d' }]) my.assertEquals(test, "edit") # test url test = access_manager.get_access('url', '/tactic/bar/Partner') my.assertEquals(test, "view") # test with access values ... a more typical usage test = access_manager.check_access('sobject', 'prod/asset', 'view') my.assertEquals(test, True) test = access_manager.check_access('sobject', 'corporate/budget', 'edit') my.assertEquals(test, True) test = access_manager.check_access('sobject_column', 'prod/asset|director_notes', 'deny') my.assertEquals(test, True) test = access_manager.check_access('sobject_column', { 'search_type': 'prod/shot', 'column': 'description' }, 'edit') my.assertEquals(test, False) test = access_manager.check_access('sobject_column', { 'search_type': 'prod/shot', 'column': 'description' }, 'view') my.assertEquals(test, True) test = access_manager.get_access('sobject', { 'search_type': 'sthpw/note', 'project': 'sample3d' }) my.assertEquals(test, "edit") test = access_manager.get_access('sobject', {'search_type': 'sthpw/note'}) my.assertEquals(test, None) test = access_manager.get_access('sobject', { 'search_type': 'prod/layer', 'project': 'sample3d' }) my.assertEquals(test, "view") test = access_manager.get_access('sobject', 'prod/layer') my.assertEquals(test, None) Project.set_project('sample3d') # security version 2 uses group = search_type asset = SearchType.create('prod/asset') asset.set_value('name', 'unit test obj') asset.commit(triggers=False) # replace the access manager with this Environment.get_security()._access_manager = access_manager test = access_manager.check_access('search_type', { 'search_type': 'prod/asset', 'project': 'sample3d' }, 'delete') my.assertEquals(test, False) asset.delete() note = SearchType.create('sthpw/note') note.set_value('note', 'unit test note obj') note.set_value('project_code', 'unittest') note.commit(triggers=False) test = access_manager.get_access('search_type', [{ 'code': 'sthpw/note', 'project': 'unittest' }]) my.assertEquals(test, 'edit') msg = '' # delete of unittest note should fail try: note.delete() except SObjectException, e: msg = 'delete error'
def _test_search_filter(my): # NOTE: this unittest is flawed because it relies on project # that may not exist my.security.set_admin(False) # exclude sample3d tasks and include unittest tasks only rules = """ <rules> <rule value='sample3d' search_type='sthpw/task' column='project_code' op='!=' group='search_filter'/> <rule value='unittest' search_type='sthpw/task' column='project_code' group='search_filter'/> </rules> """ xml = Xml() xml.read_string(rules) access_manager = Environment.get_security().get_access_manager() access_manager.add_xml_rules(xml) search = Search('sthpw/task') tasks = search.get_sobjects() project_codes = SObject.get_values(tasks,'project_code', unique=True) my.assertEquals(False, 'sample3d' in project_codes) my.assertEquals(True, 'unittest' in project_codes) # test list-based expression rules = """ <rules> <rule value='$PROJECT' search_type='sthpw/task' column='project_code' group='search_filter'/> <rule value="@GET(sthpw/login['login','EQ','unittest'].login)" search_type='sthpw/task' op='in' column='assigned' group='search_filter' project='*'/> </rules> """ xml = Xml() xml.read_string(rules) # reset it Environment.get_security().reset_access_manager() access_manager = Environment.get_security().get_access_manager() access_manager.add_xml_rules(xml) search = Search('sthpw/task') tasks = search.get_sobjects() # 3 tasks were created above for a person my.assertEquals(3, len(tasks)) assigned_codes = SObject.get_values(tasks,'assigned', unique=True) project_codes = SObject.get_values(tasks,'project_code', unique=True) my.assertEquals({'unittest_guy': 1,'unittest_gal': 1}, my.count(assigned_codes)) my.assertEquals(True, ['unittest'] == project_codes) rules = """ <rules> <rule group="project" code='sample3d' access='allow'/> <rule group="project" code='unittest' access='allow'/> <rule group="project" code='art' access='allow'/> <rule value='$PROJECT' search_type='sthpw/task' column='project_code' group='search_filter'/> <rule value='@GET(login.login)' search_type='sthpw/task' column='assigned' group='search_filter' project='*'/> </rules> """ xml = Xml() xml.read_string(rules) # reset it security = Environment.get_security() security.reset_access_manager() access_manager = security.get_access_manager() access_manager.add_xml_rules(xml) search = Search('sthpw/task') tasks = search.get_sobjects() # 2 tasks were created above for unittest_guy my.assertEquals(2, len(tasks)) assigned_codes = SObject.get_values(tasks,'assigned', unique=True) project_codes = SObject.get_values(tasks,'project_code', unique=True) my.assertEquals(True, ['unittest_guy'] == assigned_codes) my.assertEquals(True, ['unittest'] == project_codes) Project.set_project('sample3d') try: search = Search('sthpw/task') tasks = search.get_sobjects() my.assertEquals(1, len(tasks)) assigned_codes = SObject.get_values(tasks,'assigned', unique=True) project_codes = SObject.get_values(tasks,'project_code', unique=True) my.assertEquals(True, ['unittest_guy'] == assigned_codes) my.assertEquals(True, ['sample3d'] == project_codes) finally: Project.set_project('unittest') # project specific rule proj_rules = """ <rules> <rule group="project" code='sample3d' access='allow'/> <rule group="project" code='unittest' access='allow'/> <rule value='$PROJECT' search_type='sthpw/task' column='project_code' group='search_filter'/> <rule value='@GET(login.login)' search_type='sthpw/task' column='assigned' group='search_filter' project='unittest'/> <rule group="process" process="anim" access="allow"/> <rule group="process" process="comp" access="allow"/> </rules> """ xml = Xml() xml.read_string(proj_rules) # reset it Environment.get_security().reset_access_manager() access_manager = Environment.get_security().get_access_manager() access_manager.add_xml_rules(xml) project = Project.get_by_code('sample3d') if project: Project.set_project('sample3d') search = Search('sthpw/task') tasks = search.get_sobjects() assigned_codes = SObject.get_values(tasks,'assigned', unique=True) project_codes = SObject.get_values(tasks,'project_code', unique=True) # should fail since project is switched to sample3d.. and it should have more than just unittest my.assertEquals(False, ['unittest'] == assigned_codes) my.assertEquals(True, ['sample3d'] == project_codes) # unittest specific rule that uses negation !=, this takes care of NULL value automatically rules = """ <rules> <rule group="project" code='sample3d' access='allow'/> <rule value='5' search_type='sthpw/task' column='priority' op='!=' group='search_filter' project='sample3d'/> <rule group="process" process="anim" access="allow"/> <rule group="process" process="comp" access="allow"/> </rules> """ xml = Xml() xml.read_string(rules) # reset it Environment.get_security().reset_access_manager() access_manager = Environment.get_security().get_access_manager() access_manager.add_xml_rules(xml) Project.set_project('sample3d') search = Search('sthpw/task') tasks = search.get_sobjects() priorities = SObject.get_values(tasks,'priority', unique=True) #project_codes = SObject.get_values(tasks,'project_code', unique=True) for p in priorities: my.assertEquals(True, p != 5) try: Project.set_project('unittest') except SecurityException, e: # should get an SecurityException my.assertEquals('User [unittest_guy] is not permitted to view project [unittest]', e.__str__()) xml = Xml() xml.read_string(proj_rules) # reset it Environment.get_security().reset_access_manager() access_manager = Environment.get_security().get_access_manager() access_manager.add_xml_rules(xml)
def get_default_filter_config(my): custom_filter_view = my.kwargs.get('custom_filter_view') if not custom_filter_view: custom_filter_view='' config = [] config.append("<config>\n") config.append("<filter>\n") config.append(''' <element name='Keyword Search'> <display class='tactic.ui.filter.SObjectSearchFilterWdg'> <search_type>%s</search_type> <prefix>quick</prefix> </display> </element> ''' % my.search_type) config.append(''' <element name='Custom'> <display class='tactic.ui.filter.GeneralFilterWdg'> <prefix>custom</prefix> <search_type>%s</search_type> <mode>custom</mode> <custom_filter_view>%s</custom_filter_view> </display> </element> ''' % (my.search_type, custom_filter_view) ) config.append(''' <element name='Filter'> <display class='tactic.ui.filter.GeneralFilterWdg'> <prefix>main_body</prefix> <search_type>%s</search_type> <mode>sobject</mode> </display> </element> ''' % my.search_type) """ config.append(''' <element name='Filter2'> <display class='tactic.ui.filter.GeneralFilterWdg'> <prefix>filter2</prefix> <search_type>%s</search_type> <mode>sobject</mode> </display> </element> ''' % my.search_type) """ config.append(''' <element name='Parent'> <display class='tactic.ui.filter.GeneralFilterWdg'> <prefix>parent</prefix> <search_type>%s</search_type> <mode>parent</mode> </display> </element> ''' % my.search_type) config.append(''' <element name='Children'> <display class='tactic.ui.filter.GeneralFilterWdg'> <prefix>children</prefix> <search_type>%s</search_type> <mode>child</mode> </display> </element> ''' % my.search_type) """ config.append(''' <element name='Related'> <display class='tactic.ui.filter.GeneralFilterWdg'> <prefix>related</prefix> <search_type>%s</search_type> <mode>child</mode> </display> </element> ''' % my.search_type) """ config.append("</filter>\n") config.append("</config>\n") config = ''.join(config) config_xml = Xml() config_xml.read_string(config) config = WidgetConfig.get(xml=config_xml, view='filter') return config
def _test_access_manager(my): # reset it Environment.get_security().reset_access_manager() access_manager = AccessManager() xml = Xml() xml.read_string(''' <rules> <rule group='sobject' key='corporate/budget' access='allow'/> <rule group='sobject' key='corporate/salary' access='allow'/> <rule group='sobject' key='prod/asset' access='edit'/> <rule group='sobject' search_type='sthpw/note' project='sample3d' access='edit'/> <group type='url' default='deny'> <rule key='/tactic/bar/Partner' access='view'/> <rule key='/tactic/bar/External' access='view'/> </group> <rule group='sobject' search_type='prod/layer' project='sample3d' access='view'/> <rule column='description' search_type='prod/shot' access='view' group='sobject_column'/> <group type='sobject_column' default='edit'> <rule key='prod/asset|director_notes' access='deny'/> <rule key='prod/asset|sensitive_data' access='deny'/> </group> <rule group='search_type' code='prod/asset' access='allow'/> <rule group='search_type' code='sthpw/note' project='unittest' access='edit'/> <rule group='search_type' code='unittest/person' project='unittest' access='allow'/> <rule group='builtin' key='view_site_admin' access='allow'/> <rule group='builtin' key='export_all_csv' project='unittest' access='allow'/> <rule group='builtin' key='retire_delete' project='*' access='allow'/> </rules> ''') access_manager.add_xml_rules(xml) test = access_manager.check_access('builtin', 'view_site_admin','allow') my.assertEquals(test, True) test = access_manager.check_access('builtin', 'export_all_csv','allow') my.assertEquals(test, False) # this is the new way to control per project csv export keys = [{'key':'export_all_csv', 'project': 'unittest'}, {'key':'export_all_csv'}] test = access_manager.check_access('builtin', keys ,'allow') my.assertEquals(test, True) test = access_manager.check_access('builtin', 'retire_delete','allow') my.assertEquals(test, True) # test sensitive sobject test = access_manager.get_access('sobject', 'corporate/budget') my.assertEquals(test, "allow") # test allowed sobject test = access_manager.get_access('sobject', 'prod/asset') my.assertEquals(test, "edit") test = access_manager.get_access('sobject', [{'search_type':'sthpw/note', 'project':'sample3d'}]) my.assertEquals(test, "edit") # test url test = access_manager.get_access('url', '/tactic/bar/Partner') my.assertEquals(test, "view") # test with access values ... a more typical usage test = access_manager.check_access('sobject','prod/asset','view') my.assertEquals(test, True) test = access_manager.check_access('sobject','corporate/budget','edit') my.assertEquals(test, True) test = access_manager.check_access('sobject_column', 'prod/asset|director_notes','deny') my.assertEquals(test, True) test = access_manager.check_access('sobject_column',{'search_type':'prod/shot','column':'description'},'edit') my.assertEquals(test, False) test = access_manager.check_access('sobject_column',{'search_type':'prod/shot','column':'description'},'view') my.assertEquals(test, True) test = access_manager.get_access('sobject', {'search_type':'sthpw/note', 'project':'sample3d'} ) my.assertEquals(test, "edit") test = access_manager.get_access('sobject', {'search_type':'sthpw/note'} ) my.assertEquals(test, None) test = access_manager.get_access('sobject', {'search_type':'prod/layer', 'project':'sample3d'} ) my.assertEquals(test, "view") test = access_manager.get_access('sobject', 'prod/layer' ) my.assertEquals(test, None) # security version 2 uses group = search_type asset = SearchType.create('prod/asset') asset.set_value('name','unit test obj') asset.commit(triggers=False) # replace the access manager with this Environment.get_security()._access_manager = access_manager test = access_manager.check_access('search_type',{'search_type':'prod/asset','project':'sample3d'},'delete') my.assertEquals(test, False) asset.delete() note = SearchType.create('sthpw/note') note.set_value('note','unit test note obj') note.set_value('project_code','unittest') note.commit(triggers=False) test = access_manager.get_access('search_type', [{'code':'sthpw/note', 'project':'unittest'}] ) my.assertEquals(test, 'edit') msg = '' # delete of unittest note should fail try: note.delete() except SObjectException, e: msg = 'delete error'
def _test_access_manager(my): # reset it Environment.get_security().reset_access_manager() access_manager = AccessManager() xml = Xml() xml.read_string( """ <rules> <rule group='sobject' key='corporate/budget' access='allow'/> <rule group='sobject' key='corporate/salary' access='allow'/> <rule group='sobject' key='prod/asset' access='edit'/> <rule group='sobject' search_type='sthpw/note' project='sample3d' access='edit'/> <group type='url' default='deny'> <rule key='/tactic/bar/Partner' access='view'/> <rule key='/tactic/bar/External' access='view'/> </group> <rule group='sobject' search_type='prod/layer' project='sample3d' access='view'/> <rule column='description' search_type='prod/shot' access='view' group='sobject_column'/> <group type='sobject_column' default='edit'> <rule key='prod/asset|director_notes' access='deny'/> <rule key='prod/asset|sensitive_data' access='deny'/> </group> <rule group='search_type' code='prod/asset' access='allow'/> <rule group='search_type' code='sthpw/note' project='unittest' access='edit'/> <rule group='search_type' code='unittest/person' project='unittest' access='allow'/> <rule group='builtin' key='view_site_admin' access='allow'/> <rule group='builtin' key='export_all_csv' project='unittest' access='allow'/> <rule group='builtin' key='retire_delete' project='*' access='allow'/> </rules> """ ) access_manager.add_xml_rules(xml) test = access_manager.check_access("builtin", "view_site_admin", "allow") my.assertEquals(test, True) test = access_manager.check_access("builtin", "export_all_csv", "allow") my.assertEquals(test, False) # this is the new way to control per project csv export keys = [{"key": "export_all_csv", "project": "unittest"}, {"key": "export_all_csv"}] test = access_manager.check_access("builtin", keys, "allow") my.assertEquals(test, True) test = access_manager.check_access("builtin", "retire_delete", "allow") my.assertEquals(test, True) # test sensitive sobject test = access_manager.get_access("sobject", "corporate/budget") my.assertEquals(test, "allow") # test allowed sobject test = access_manager.get_access("sobject", "prod/asset") my.assertEquals(test, "edit") test = access_manager.get_access("sobject", [{"search_type": "sthpw/note", "project": "sample3d"}]) my.assertEquals(test, "edit") # test url test = access_manager.get_access("url", "/tactic/bar/Partner") my.assertEquals(test, "view") # test with access values ... a more typical usage test = access_manager.check_access("sobject", "prod/asset", "view") my.assertEquals(test, True) test = access_manager.check_access("sobject", "corporate/budget", "edit") my.assertEquals(test, True) test = access_manager.check_access("sobject_column", "prod/asset|director_notes", "deny") my.assertEquals(test, True) test = access_manager.check_access( "sobject_column", {"search_type": "prod/shot", "column": "description"}, "edit" ) my.assertEquals(test, False) test = access_manager.check_access( "sobject_column", {"search_type": "prod/shot", "column": "description"}, "view" ) my.assertEquals(test, True) test = access_manager.get_access("sobject", {"search_type": "sthpw/note", "project": "sample3d"}) my.assertEquals(test, "edit") test = access_manager.get_access("sobject", {"search_type": "sthpw/note"}) my.assertEquals(test, None) test = access_manager.get_access("sobject", {"search_type": "prod/layer", "project": "sample3d"}) my.assertEquals(test, "view") test = access_manager.get_access("sobject", "prod/layer") my.assertEquals(test, None) # security version 2 uses group = search_type asset = SearchType.create("prod/asset") asset.set_value("name", "unit test obj") asset.commit(triggers=False) # replace the access manager with this Environment.get_security()._access_manager = access_manager test = access_manager.check_access( "search_type", {"search_type": "prod/asset", "project": "sample3d"}, "delete" ) my.assertEquals(test, False) asset.delete() note = SearchType.create("sthpw/note") note.set_value("note", "unit test note obj") note.set_value("project_code", "unittest") note.commit(triggers=False) test = access_manager.get_access("search_type", [{"code": "sthpw/note", "project": "unittest"}]) my.assertEquals(test, "edit") msg = "" # delete of unittest note should fail try: note.delete() except SObjectException, e: msg = "delete error"
def get_display(my): top_wdg = DivWdg() top_wdg.add_style("color: black") top_wdg.add_style("width: 350px") top_wdg.add_style("margin-top: 10px") top_wdg.add_style("padding: 10px") top_wdg.add_border() title = DivWdg() title.add_style("color: black") title.add_style("margin-top: -22px") top_wdg.add(title) #if not my.name_string: # title.add('No database column') # return top_wdg title.add("Widget Definition") widget_types = { 'foreign_key': 'tactic.ui.table.ForeignKeyElementWdg', 'button': 'tactic.ui.table.ButtonElementWdg', 'expression': 'tactic.ui.table.ExpressionElementWdg' } web = WebContainer.get_web() config_string = web.get_form_value("config_xml") if not config_string: config_string = '<config/>' xml = Xml() xml.read_string(config_string) #print "config_string: ", config_string # get values from the config file element_name = xml.get_value('element/@name') config = WidgetConfig.get( view='element', xml='<config><element>%s</element></config>' % config_string) display_options = config.get_display_options(element_name) title = xml.get_value('element/@title') display_handler = xml.get_value('element/display/@class') if not display_handler: display_handler = 'tactic.ui.panel.TypeTableElementWdg' widget_name = xml.get_value('element/display/@widget') if not widget_name: widget_name = 'custom' custom_table = Table() custom_table.add_style("color: black") top_wdg.add(custom_table) name_text = DivWdg() name_text.add_style("color: black") name_text.add(element_name) custom_table.add_row() custom_table.add_cell("Name: ") custom_table.add_cell(name_text) # add title custom_table.add_row() title_wdg = TextWdg("custom_title") title_wdg.set_value(title) title_wdg.add_attr("size", "50") custom_table.add_cell("Title: ") custom_table.add_cell(title_wdg) # add description #custom_table.add_row() #description_wdg = TextAreaWdg("custom_description") #td = custom_table.add_cell( "Description: " ) #td.add_style("vertical-align: top") #custom_table.add_cell( description_wdg ) type_select = SelectWdg("custom_type") #type_select.add_empty_option("-- Select --") type_select.set_option( "values", "string|integer|float|boolean|currency|date|foreign_key|link|list|button|custom" ) type_select.set_option( "labels", "String(db)|Integer(db)|Float(db)|Boolean(db)|Currency(db)|Date(db)|Foreign Key|Link|List|Button|Custom" ) type_select.set_value(widget_name) #type_select.set_option("values", "string|integer|float|boolean|currency|date|link|list|foreign_key|button|empty") #type_select.set_option("labels", "String|Integer|Float|Boolean|Currency|Date|Link|List|Foreign Key|Button|Empty") custom_table.add_row() td = custom_table.add_cell("Widget Type: ") td.add_style("vertical-align: top") td = custom_table.add_cell(type_select) type_select.add_event( "onchange", "spt.CustomProject.property_type_select_cbk(this)") td.add(HtmlElement.br()) display_handler_text = TextWdg("display_handler") display_handler_text.add_attr("size", "50") display_handler_text.set_value(display_handler) td.add(display_handler_text) # extra info for foreign key custom_table.add_row() div = DivWdg() div.add_class("foreign_key_options") div.add_style("display: none") div.add_style("margin-top: 10px") div.add("Options") div.add(HtmlElement.br()) # extra info for foreign key custom_table.add_row() div = DivWdg() div.add_class("foreign_key_options") div.add_style("display: none") div.add_style("margin-top: 10px") div.add("Options") div.add(HtmlElement.br()) # TODO: this class should not be in prod!! from pyasm.prod.web import SearchTypeSelectWdg div.add("Relate to: ") search_type_select = SearchTypeSelectWdg( "foreign_key_search_select", mode=SearchTypeSelectWdg.CURRENT_PROJECT) div.add(search_type_select) td.add(div) # extra info for list custom_table.add_row() div = DivWdg() div.add_class("list_options") div.add_style("display: none") div.add_style("margin-top: 10px") div.add("Options") div.add(HtmlElement.br()) # TODO: this class should not be in prod!! from pyasm.prod.web import SearchTypeSelectWdg div.add("Values: ") search_type_text = TextWdg("list_values") div.add(search_type_text) td.add(div) # extra info for button custom_table.add_row() div = DivWdg() div.add_style("color: black") div.add_class("button_options") div.add_style("display: none") div.add_style("margin-top: 10px") #class_path = "tactic.ui.table.ButtonElementWdg" class_path = display_handler button = Common.create_from_class_path(class_path) args_keys = button.get_args_keys() div.add("Options") div.add(HtmlElement.br()) for key in args_keys.keys(): option_name_text = HiddenWdg("option_name") option_name_text.set_value(key) div.add(option_name_text) div.add("%s: " % key) div.add(" ") input = button.get_input_by_arg_key(key) value = display_options.get(key) if value: input.set_value(value) div.add(input) div.add(HtmlElement.br()) td.add(div) # is searchable checkbox #custom_table.add_row() #current_searchable_wdg = CheckboxWdg("is_searchable") #current_view_wdg.set_checked() #custom_table.add_cell("Searchable? ") #td = custom_table.add_cell(current_searchable_wdg) custom_table.close_tbody() return top_wdg
def add_xml_rules(my, xml): '''xml should be an XML object with the data in the form of <rules> <group type='sobject' default='<default>'> <rule key='<key>' access='<access>'/> </group> </rules> ''' if isinstance(xml, basestring): xmlx = Xml() xmlx.read_string(xml) xml = xmlx my.xml = xml # parse shorthand rules rule_nodes = xml.get_nodes("rules/rule") if not rule_nodes: return if my.project_codes == None: search = Search('sthpw/project') projects = search.get_sobjects() my.project_codes = [x.get_code() for x in projects] my.project_codes.append('*') for rule_node in rule_nodes: # initiate the project_code here for each loop project_code = '*' group_type = Xml.get_attribute( rule_node, "group" ) if not group_type: # category is the preferred name over group now # TODO: phase out the use of group completely group_type = Xml.get_attribute( rule_node, "category" ) # get an existing rule set or create a new one if my.groups.has_key(group_type): rules = my.groups[group_type] else: rules = {} my.groups[group_type] = rules # set the default, if specified group_default = xml.get_attribute( rule_node, "default" ) if group_default: rules['__DEFAULT__'] = group_default continue # generate the rule key #rule_key = xml.get_attribute(rule_node, 'key') attrs = xml.get_attributes(rule_node) attrs2 = {} count = 0 for name, value in attrs.items(): if name in ['access', 'group', 'category', 'project']: continue # have to turn everything into strings attrs2[str(name)] = str(value) count += 1 if count == 1 and attrs2.has_key('key'): # backwards compatibility rule_key = attrs2['key'] else: #rule_key = str(attrs2) rule_key = str(Common.get_dict_list(attrs2)) rule_project = xml.get_attribute(rule_node, 'project') if rule_project: project_code = rule_project # special treatment for search_filter to enable # project-specific search if group_type=='search_filter': attrs2['project'] = rule_project # if there is a value, then combine it with the key rule_value = xml.get_attribute(rule_node, 'value') if rule_value: rule_key = "%s||%s" % (rule_key, rule_value) # add a project code qualifier rule_keys = [] if project_code == '*' and group_type != 'search_filter': for code in my.project_codes: key = "%s?project=%s" % (rule_key, code) rule_keys.append(key) else: key= "%s?project=%s" % (rule_key, project_code) #key = str(key) # may need to stringify unicode string rule_keys.append(key) rule_access = xml.get_attribute(rule_node, 'access') #if rule_access == "": # raise AccessException("Cannot have empty 'access':\n%s" \ # % xml.to_string(rule_node) ) # if no key is specified, it is considered a DEFAULT if not rule_keys and not rule_value: rule_keys = ['__DEFAULT__'] for rule_key in rule_keys: # check if rule_access exists first, which doesn't for search_filter, # but it has to go into the rules regardless # if the rule already exists, take the highest one if rule_access and rules.has_key(rule_key): curr_access, cur_attrs = rules[rule_key] try: access_enum = my._get_access_enum(rule_access) if my._get_access_enum(curr_access) > access_enum: continue except: if group_type == "builtin": continue else: raise rules[rule_key] = rule_access, attrs2 #for rule, values in rules.items(): # print "rule: ", rule, values[0] # FIXME: this one doesn't support the multi-attr structure # convert this to a python data structure group_nodes = xml.get_nodes("rules/group") for group_node in group_nodes: group_type = Xml.get_attribute( group_node, "type" ) # get an existing rule set or create a new one if my.groups.has_key(group_type): rules = my.groups[group_type] else: rules = {} my.groups[group_type] = rules # set the default, if specified group_default = xml.get_attribute( group_node, "default" ) if group_default != "": rules['__DEFAULT__'] = group_default # get all of the rule nodes rule_nodes = Xml.get_children(group_node) for rule_node in rule_nodes: project_code='*' if Xml.get_node_name(rule_node) != 'rule': continue rule_key = xml.get_attribute(rule_node, 'key') rule_access = xml.get_attribute(rule_node, 'access') rule_project = xml.get_attribute(rule_node, 'project') if rule_project: project_code = rule_project if rule_access == "": raise AccessException("Cannot have empty 'access':\n%s" \ % xml.to_string(rule_node) ) rule_keys = [] attrs2 = {'key': rule_key} # add a project code qualifier if project_code == '*' and group_type != 'search_filter': for code in my.project_codes: key = "%s?project=%s" % (rule_key, code) rule_keys.append(key) else: key= "%s?project=%s" % (rule_key, project_code) rule_keys.append(key) for rule_key in rule_keys: rules[rule_key] = rule_access, attrs2
<process completion="20" color="#e9e386" name="In Progress"/> <process completion="20" color="#a96ccf" name="Waiting"/> <process completion="30" color="#a96ccf" name="Need Assistance"/> <process completion="80" color="#e84a4d" name="Review"/> <process completion="100" color="#a3d991" name="Approved"/> <connect to="Review" from="Need Assistance"/> <connect to="In Progress" from="Pending"/> <connect to="Pending" from="Assignment"/> <connect to="Need Assistance" from="Waiting"/> <connect to="Waiting" from="In Progress"/> <connect to="Approved" from="Review"/> </pipeline> ''' default_xml = Xml() default_xml.read_string(TASK_PIPELINE) OTHER_COLORS = { "Complete": "#a3d991", "Done": "#a3d991", "Final": "#a3d991", "Revise": "#e84a4d", "Ready": "#a3d991", "In_Progress":"#e9e386", } class Task(SObject): SEARCH_TYPE = "sthpw/task"
def execute(my): web = WebContainer.get_web() # get command line options search_type = my.kwargs.get("search_type") assert search_type view = my.kwargs.get("view") if not view: view = get_template_view() # check if this is advanced mode mode = web.get_form_value("custom_mode") if not mode: mode = 'simple' if mode == 'xml': config_string = web.get_form_value("config_xml") # handle the "default" view view = DEFAULT_VIEW config = WidgetDbConfig.get_by_search_type(search_type, view) if not config: config = WidgetDbConfig.create(search_type, view) xml = Xml() xml.read_string(config_string) element_name = xml.get_value("element/@name") element_name = element_name.strip() assert element_name type = xml.get_value("element/@type") if not type: class_name = xml.get_value("element/display/@class") if not class_name: raise TacticException("Either a type or class name needs to be defined in config xml.") config.append_xml_element(element_name,config_string) config.commit_config() # create the required columns widget = config.get_display_widget(element_name) columns = widget.get_required_columns() if columns: print "WARNING: need to create columns: ", columns my.info['element_name'] = element_name return type = web.get_form_value("custom_type") description = web.get_form_value("custom_description") if not description: description = "No descripton" title = web.get_form_value("custom_title") name = web.get_form_value("custom_name") name = name.strip() if not name: raise TacticException("No name specified") add_to_current_view = web.get_form_value("add_to_current_view") add_to_edit_view = web.get_form_value("add_to_edit_view") is_searchable = web.get_form_value("is_searchable") # create the column if type not in ['button', 'empty']: cmd = ColumnAddCmd(search_type, name, type) cmd.execute() # create the type class_name = None options = {} # this is actually element attrs element_options = {} edit_class_name = None edit_options = {} edit_attrs = {} element_type = type # Date Range is not used any more in the UI" if type == "Date Range": class_name = "GanttWdg" options["start_date_column"] = "%s_start_date" % name options["end_deate_column"] = "%s_end_date" % name elif type == "date": class_name = "DateWdg" #edit_class_name = "CalendarWdg" element_type = 'timestamp' edit_attrs['type'] = 'timestamp' edit_class_name = "" add_to_edit_view = True elif type == "foreign_key": class_name = "" edit_class_name = "SelectWdg" foreign_search_type = web.get_form_value("foreign_key_search_select") edit_options["query"] = '%s|code|code' % foreign_search_type # turn on add to edit view add_to_edit_view = True elif type == "button": class_name = "tactic.ui.table.ButtonElementWdg" script = web.get_form_value("option_script_select") if script: options['script'] = script icon = web.get_form_value("option_icon_select") if icon: options['icon'] = icon edit_class_name = "" # This does not have a type element_type = None elif type == "empty": element_type = None pass elif type == "list": class_name = "" edit_class_name = "SelectWdg" list_values = web.get_form_value("list_values") edit_options['values'] = list_values add_to_edit_view = True element_options['type'] = element_type element_options['title'] = title # handle the "default" view view = DEFAULT_VIEW config = WidgetDbConfig.get_by_search_type(search_type, view) if not config: config = WidgetDbConfig.create(search_type, view) config.append_display_element(name, class_name, options=options, \ element_attrs=element_options) config.commit_config() # get the config file if add_to_current_view: config = WidgetDbConfig.get_by_search_type(search_type, view) if not config: # if it doesn't exist, the check to see, if there is a hard # coded view out there predefined_config = WidgetConfigView.get_by_search_type(search_type, view) xml = predefined_config.get_xml() # create a new db one config = WidgetDbConfig.create(search_type, view) if xml: config.set_value("config", xml.to_string()) config._init() config.append_display_element(name) config.commit_config() # TODO: Need to make this searchable using simple search ????? if is_searchable: element_options['searchable'] = 'true' # handle the "edit" if add_to_edit_view and view != "edit": config = WidgetDbConfig.get_by_search_type(search_type, "edit") if not config: config = WidgetDbConfig.create(search_type, "edit") config.append_display_element(name, edit_class_name, edit_options,element_attrs=edit_attrs) config.commit_config() """ # this sType has been deprecated sobject = SearchType.create("prod/custom_property") sobject.set_value("search_type", search_type) sobject.set_value("name", name) sobject.set_value("description", description) sobject.commit() """ # set some information my.description = "Added Property [%s] of type [%s] to [%s]" % \ (name, type, search_type) my.info['element_name'] = name
def execute(self): web = WebContainer.get_web() alter_mode = self.kwargs.get("alter_mode") title = self.kwargs.get("title") config_mode = web.get_form_value("config_mode") view = web.get_form_value('view') constraint = web.get_form_value("config_constraint") data_type = '' if config_mode == "advanced" : config_string = web.get_form_value("config_xml") if config_string: xml = Xml() xml.read_string(config_string) node = xml.get_root_node() data_type = xml.get_attribute(node, "data_type") nullable = xml.get_attribute(node, "nullable") in ['true','True'] else: data_type = web.get_form_value("config_data_type") if data_type == 'Other...': data_type = web.get_form_value("config_data_type_custom") cb = CheckboxWdg("config_nullable") nullable = cb.is_checked() # if advanced is selected in the Widget Column view, data_type is '' # read from UI if not data_type and view == 'definition': data_type = web.get_form_value("config_data_type") if data_type == 'Other...': data_type = web.get_form_value("config_data_type_custom") cb = CheckboxWdg("config_nullable") nullable = cb.is_checked() column_name = web.get_form_value("column_name") search_type = web.get_form_value("target_search_type") if alter_mode == ManageSearchTypeDetailWdg.REMOVE_COLUMN: cmd = ColumnDropCmd(search_type, column_name) Command.execute_cmd(cmd) # delete widget config from definition view widget_config = WidgetDbConfig.get_by_search_type(search_type, 'definition') if widget_config: config = WidgetConfig.get('definition', xml=widget_config.get_xml_value('config')) config.remove_xml_element(column_name) new_xml = config.get_xml().to_string() widget_config.set_value("config", new_xml) widget_config.commit() # set cache to {} from pyasm.common import Container Container.put("WidgetConfigView:config_cache", {}) #Container.put("WidgetConfig:config_cache", {}) elif alter_mode == ManageSearchTypeDetailWdg.MODIFY_COLUMN: cmd = ColumnAlterCmd(search_type, column_name, data_type, nullable) Command.execute_cmd(cmd) element_options = {} element_options['type'] = data_type if title: element_options['title'] = title # handle the "default" view # update the widget config data type in the xml view = self.DEFAULT_VIEW config = WidgetDbConfig.get_by_search_type(search_type, view) if config: config.append_display_element(column_name, options={}, \ element_attrs=element_options) config.commit_config() elif alter_mode == ManageSearchTypeDetailWdg.ADD_COLUMN: cmd = ColumnAddCmd(search_type, column_name, data_type, nullable) Command.execute_cmd(cmd) if constraint: # add constraint from pyasm.command import ColumnAddIndexWdg cmd = ColumnAddIndexWdg() cmd.execute() else: # remove constraint pass
def get_widget_from_hash(cls, hash, return_none=False, force_no_index=False, kwargs={}): from pyasm.web import DivWdg if hash.startswith("//"): use_top = False hash = hash[1:] else: use_top = True import re p = re.compile("^/(\w+)") m = p.search(hash) if not m: if return_none: return None print "Cannot parse hash[%s]" % hash return DivWdg("Cannot parse hash [%s]" % hash) key = m.groups()[0] # guest user should never be able to see admin site if key != 'login': security = Environment.get_security() login = security.get_user_name() if login == "guest" and key == 'admin': from pyasm.widget import WebLoginWdg # HACK: if the guest access is full, the the outer form # is not defined ... force it in here. This is because the # top used it TopWdg and not TitleTopWdg div = DivWdg() div.add("<form id='form' name='form' method='post' enctype='multipart/form-data'>\n") web_login_wdg = WebLoginWdg().get_buffer_display() div.add(web_login_wdg) div.add("</form>\n") return div sobject = cls._get_predefined_url(key, hash) # look up the url if not sobject: search = Search("config/url") search.add_filter("url", "/%s/%%"%key, "like") search.add_filter("url", "/%s"%key) search.add_where("or") sobject = search.get_sobject() if not sobject: if return_none: return None return DivWdg("No Widget found for hash [%s]" % hash) config = sobject.get_value("widget") config = config.replace('&','&') url = sobject.get_value("url") url = url.strip() # update the config value with expressions options = Common.extract_dict(hash, url) for name, value in options.items(): config = config.replace("{%s}" % name, value) xml = Xml() xml.read_string(config) use_index, use_admin, use_sidebar = cls._get_flags(xml, sobject, force_no_index, kwargs) # add the admin bar security = Environment.get_security() is_admin = security.check_access("builtin", "view_site_admin", "allow") if is_admin and use_admin: # use admin from tactic.ui.app import PageNavContainerWdg top = PageNavContainerWdg( hash=hash, use_sidebar=use_sidebar ) return top.get_buffer_display() elif use_index: # check if there is an index search = Search("config/url") search.add_filter("url", "/index") index = search.get_sobject() # just use admin if no index page is found if not index: from tactic.ui.app import PageNavContainerWdg top = PageNavContainerWdg( hash=hash, use_sidebar=use_sidebar ) return top.get_buffer_display() config = index.get_value("widget") xml = Xml() xml.read_string(config) node = xml.get_node("element/display") options.update(xml.get_node_values_of_children(node)) class_name = xml.get_value("element/display/@class") if class_name: options['class_name'] = class_name # this passes the hash value to the index widget # which must handle it accordingly options['hash'] = hash top = cls.build_widget(options) return top.get_buffer_display() # process the options and then build the widget from the xml options = Common.extract_dict(hash, url) for name, value in kwargs.items(): options[name] = value node = xml.get_node("element/display") options.update(xml.get_node_values_of_children(node)) class_name = xml.get_value("element/display/@class") if class_name: options['class_name'] = class_name widget = cls.build_widget(options) name = hash.lstrip("/") name_array = name.split("/") if name_array: name_end = name_array[-1] name_end = name_end.replace("_", " ") widget.set_name(name_end) else: widget.set_name(name) return widget
def get_display(self): top_wdg = DivWdg() top_wdg.add_style("color: black") top_wdg.add_style("width: 350px") top_wdg.add_style("margin-top: 10px") top_wdg.add_style("padding: 10px") top_wdg.add_border() title = DivWdg() title.add_style("color: black") title.add_style("margin-top: -22px") top_wdg.add(title) #if not self.name_string: # title.add('No database column') # return top_wdg title.add("Widget Definition") widget_types = { 'foreign_key': 'tactic.ui.table.ForeignKeyElementWdg', 'button': 'tactic.ui.table.ButtonElementWdg', 'expression': 'tactic.ui.table.ExpressionElementWdg' } web = WebContainer.get_web() config_string = web.get_form_value("config_xml") if not config_string: config_string = '<config/>' xml = Xml() xml.read_string(config_string) #print "config_string: ", config_string # get values from the config file element_name = xml.get_value('element/@name') config = WidgetConfig.get(view='element',xml='<config><element>%s</element></config>' % config_string) display_options = config.get_display_options(element_name) title = xml.get_value('element/@title') display_handler = xml.get_value('element/display/@class') if not display_handler: display_handler = 'tactic.ui.panel.TypeTableElementWdg' widget_name = xml.get_value('element/display/@widget') if not widget_name: widget_name = 'custom' custom_table = Table() custom_table.add_style("color: black") top_wdg.add(custom_table) name_text = DivWdg() name_text.add_style("color: black") name_text.add(element_name) custom_table.add_row() custom_table.add_cell("Name: ") custom_table.add_cell(name_text) # add title custom_table.add_row() title_wdg = TextWdg("custom_title") title_wdg.set_value(title) title_wdg.add_attr("size", "50") custom_table.add_cell( "Title: " ) custom_table.add_cell( title_wdg ) # add description #custom_table.add_row() #description_wdg = TextAreaWdg("custom_description") #td = custom_table.add_cell( "Description: " ) #td.add_style("vertical-align: top") #custom_table.add_cell( description_wdg ) type_select = SelectWdg("custom_type") #type_select.add_empty_option("-- Select --") type_select.set_option("values", "string|integer|float|boolean|currency|date|foreign_key|link|list|button|custom") type_select.set_option("labels", "String(db)|Integer(db)|Float(db)|Boolean(db)|Currency(db)|Date(db)|Foreign Key|Link|List|Button|Custom") type_select.set_value(widget_name) #type_select.set_option("values", "string|integer|float|boolean|currency|date|link|list|foreign_key|button|empty") #type_select.set_option("labels", "String|Integer|Float|Boolean|Currency|Date|Link|List|Foreign Key|Button|Empty") custom_table.add_row() td = custom_table.add_cell("Widget Type: ") td.add_style("vertical-align: top") td = custom_table.add_cell(type_select) type_select.add_event("onchange", "spt.CustomProject.property_type_select_cbk(this)") td.add(HtmlElement.br()) display_handler_text = TextWdg("display_handler") display_handler_text.add_attr("size", "50") display_handler_text.set_value(display_handler) td.add(display_handler_text) # extra info for foreign key custom_table.add_row() div = DivWdg() div.add_class("foreign_key_options") div.add_style("display: none") div.add_style("margin-top: 10px") div.add("Options") div.add(HtmlElement.br()) # extra info for foreign key custom_table.add_row() div = DivWdg() div.add_class("foreign_key_options") div.add_style("display: none") div.add_style("margin-top: 10px") div.add("Options") div.add(HtmlElement.br()) # TODO: this class should not be in prod!! from pyasm.prod.web import SearchTypeSelectWdg div.add("Relate to: ") search_type_select = SearchTypeSelectWdg("foreign_key_search_select", mode=SearchTypeSelectWdg.CURRENT_PROJECT) div.add(search_type_select) td.add(div) # extra info for list custom_table.add_row() div = DivWdg() div.add_class("list_options") div.add_style("display: none") div.add_style("margin-top: 10px") div.add("Options") div.add(HtmlElement.br()) # TODO: this class should not be in prod!! from pyasm.prod.web import SearchTypeSelectWdg div.add("Values: ") search_type_text = TextWdg("list_values") div.add(search_type_text) td.add(div) # extra info for button custom_table.add_row() div = DivWdg() div.add_style("color: black") div.add_class("button_options") div.add_style("display: none") div.add_style("margin-top: 10px") #class_path = "tactic.ui.table.ButtonElementWdg" class_path = display_handler button = Common.create_from_class_path(class_path) args_keys = button.get_args_keys() div.add("Options") div.add(HtmlElement.br()) for key in args_keys.keys(): option_name_text = HiddenWdg("option_name") option_name_text.set_value(key) div.add(option_name_text) div.add("%s: " % key) div.add(" ") input = button.get_input_by_arg_key(key) value = display_options.get(key) if value: input.set_value(value) div.add(input) div.add(HtmlElement.br()) td.add(div) # is searchable checkbox #custom_table.add_row() #current_searchable_wdg = CheckboxWdg("is_searchable") #current_view_wdg.set_checked() #custom_table.add_cell("Searchable? ") #td = custom_table.add_cell(current_searchable_wdg) custom_table.close_tbody() return top_wdg
def get_text(self, path, last_path=None, highlight=True): if path.startswith("http"): if path.startswith("https://docs.google.com/document"): # NOTE: this is very specific to google docs if not path.endswith("?embedded=true"): path = "%s?embedded=true" % path is_html = True else: is_html = False import urllib2 response = urllib2.urlopen(path) html = response.read() fix = '''<meta content="text/html; charset=UTF-8" http-equiv="content-type">''' html = html.replace(fix, "") html = html.replace("&", "&") if is_html: xml = Xml() try: xml.read_string(html) except: self.doc_mode = "formatted" html = html.replace("&", "&") print print "WARNING: cannot parse as XML" print return html if self.doc_mode == "formatted": return xml.to_string().replace("&", "&") node = xml.get_node("html/body") text = xml.to_string(node) text = text.replace("<body", "<div style='margin: 0px'") text = text.replace("</body>", "</div>") text = text.replace("&", "&") else: text = html lines2 = [] lines = text.split("\n") for line in lines: tmp_line = line.strip() if tmp_line.startswith("<span"): tmp_line = tmp_line.replace("<span>", "") tmp_line = tmp_line.replace("</span>", "") tmp_line = tmp_line.replace("<span/>", "") elif tmp_line.startswith("<p "): continue elif tmp_line.startswith("</p>"): continue # FIXME: there has to be a function to do this tmp_line = tmp_line.replace(" ", "") tmp_line = tmp_line.replace(""", '"') tmp_line = tmp_line.replace("'", "'") lines2.append(tmp_line) text = "\n".join(lines2) #print 'text', text #import html2text #text = html2text.html2text(html) # clear out any remaining html tags import re text = re.sub('<[^<]+?>', '', text) else: f = open(path) lines = f.readlines() f.close() #f = open(path) #lines2 = f.readlines() #f.close() #diff = self.get_diff(lines, lines2) #text = "".join(diff) text = "".join(lines) # read last text if it exists if last_path and os.path.exists(last_path): last_file = open(last_path, 'r') last_text = last_file.read() last_file.close() else: last_text = None if last_text != None: lines = text.split("\n") lines2 = last_text.split("\n") diff = self.get_diff(lines2, lines) diff_text = "\n".join(diff) text = diff_text if highlight: search_type_obj = SearchType.get(self.search_type) color = search_type_obj.get_value("color") if not color: color = '#0F0' # assemble all the lines data = [] search = Search(self.search_type) sobjects = search.get_sobjects() for sobject in sobjects: search_key = sobject.get_search_key() value = sobject.get_value(self.column) lines = value.split("\n") for line in lines: line = line.strip() data.append( [line, search_key] ) for line, search_key in data: if not line: continue line = line.strip() text = text.replace(line, "<i style='color: %s; font-weight: bold; opacity: 1.0;' spt_search_key='%s' class='spt_document_item hand'>%s</i>" % (color, search_key, line)) return text
class SObjectDefaultConfig(Base): '''An artificial config file is made if none are found''' def __init__(self, search_type, view, config_base=None, mode="columns"): self.search_type = search_type if view: self.view = view else: self.view = config_base if not self.view: self.view = "table" # bit of protection ... : have been known to show up in view names self.view = self.view.replace(":", '_') #mode = "basic" self.xml = Xml() if mode == 'columns': self.handle_columns_mode() else: self.handle_basic_mode() def get_columns(self, required_only=False): if self.search_type == 'sthpw/virtual': return [] search_type_obj = SearchType.get(self.search_type) table = search_type_obj.get_table() from pyasm.biz import Project db_resource = Project.get_db_resource_by_search_type(self.search_type) database_name = db_resource.get_database() db = DbContainer.get(db_resource) # table may not exist try: all_columns = db.get_columns(table) columns = [] if required_only: nullables = db.get_column_nullables(table) for column in all_columns: null_ok = nullables.get(column) if not null_ok: columns.append(column) # if there are no required columns if not columns: columns = all_columns else: columns = all_columns except SqlException: Environment.add_warning( 'missing table', 'Table [%s] does not exist in database [%s]' % (table, database_name)) return [] return columns def handle_basic_mode(self): doc = self.xml.create_doc("config") root = self.xml.get_root_node() db_columns = self.get_columns() if "code" in db_columns: columns = ["preview", "code"] elif "name" in db_columns: columns = ["preview", "name"] elif "id" in db_columns: columns = ["preview", "id"] table = self.xml.create_element("table") Xml.append_child(root, table) for column in ["preview", "code"]: element = self.xml.create_element("element") Xml.set_attribute(element, "name", column) Xml.append_child(table, element) # create the edit edit = self.xml.create_element("edit") Xml.append_child(root, edit) for column in ["preview", "code"]: element = self.xml.create_element("element") Xml.set_attribute(element, "name", column) Xml.append_child(edit, element) # create the manual publish view publish = self.xml.create_element("publish") Xml.append_child(root, publish) element = self.xml.create_element("element") Xml.set_attribute(element, "name", "image") Xml.append_child(publish, element) dis_element = self.xml.create_element("display") Xml.set_attribute(dis_element, "class", "ThumbInputWdg") act_element = self.xml.create_element("action") Xml.set_attribute(act_element, "class", "NullAction") Xml.append_child(element, dis_element) Xml.append_child(element, act_element) element = self.xml.create_element("element") Xml.set_attribute(element, "name", "publish_files") Xml.append_child(publish, element) dis_element = self.xml.create_element("display") Xml.set_attribute(dis_element, "class", "UploadWdg") # add options option = self.xml.create_text_element('names', 'publish_icon|publish_main') Xml.append_child(dis_element, option) option = self.xml.create_text_element('required', 'false|true') Xml.append_child(dis_element, option) act_element = self.xml.create_element("action") Xml.set_attribute(act_element, "class", "MultiUploadAction") # add options option = self.xml.create_text_element('names', 'publish_icon|publish_main') Xml.append_child(act_element, option) option = self.xml.create_text_element('types', 'icon_main|main') Xml.append_child(act_element, option) Xml.append_child(element, dis_element) Xml.append_child(element, act_element) value = self.xml.to_string() self.xml = Xml() self.xml.read_string(value) def handle_columns_mode(self): doc = self.xml.create_doc("config") root = self.xml.get_root_node() columns = self.get_columns() if len(columns) == 1 and columns[0] == "id": columns = self.get_columns(required_only=False) # create the table # search is a special view for SearchWdg and it should not be created if self.view not in ['search', 'publish']: if self.view.find('@') != -1: table = self.xml.create_element('view', attrs={'name': self.view}) else: table = self.xml.create_element(self.view) self.xml.append_child(root, table) for column in columns: if column in ["_id", "id", "oid", "s_status"]: continue element = self.xml.create_element("element") Xml.set_attribute(element, "name", column) self.xml.append_child(table, element) # add history, input and output for the load view (designed for app loading) if self.view == 'load': element = self.xml.create_element("element") Xml.set_attribute(element, "name", "checkin") self.xml.append_child(table, element) for column in ['input', 'output']: element = self.xml.create_element("element") Xml.set_attribute(element, "name", column) Xml.set_attribute(element, "edit", "false") display_element = self.xml.create_element("display") Xml.set_attribute(display_element, "class", "tactic.ui.cgapp.LoaderElementWdg") self.xml.append_child(element, display_element) stype, key = SearchType.break_up_key(self.search_type) op1 = self.xml.create_text_element("search_type", stype) op2 = self.xml.create_text_element("mode", column) self.xml.append_child(display_element, op1) self.xml.append_child(display_element, op2) self.xml.append_child(table, element) value = self.xml.to_string() self.xml = Xml() self.xml.read_string(value) def get_type(self, element_name): xpath = "config/%s/element[@name='%s']/@type" % (self.view, element_name) type = self.xml.get_value(xpath) if not type: xpath = "config/%s/element[@name='%s']/@type" % ("definition", element_name) type = self.xml.get_value(xpath) return type def get_xml(self): return self.xml
def add_xml_rules(self, xml, project_code=None): '''xml should be an XML object with the data in the form of <rules> <group type='sobject' default='<default>'> <rule key='<key>' access='<access>'/> </group> </rules> ''' from pyasm.search import SObject if isinstance(xml, SObject): sobject = xml xml = sobject.get_xml_value("access_rules") if not project_code: project_code = sobject.get_value("project_code") project_override = project_code if isinstance(xml, basestring): xmlx = Xml() xmlx.read_string(xml) xml = xmlx # parse shorthand rules rule_nodes = xml.get_nodes("rules/rule") if not rule_nodes: return # store all of the project codes (this will only run once) if self.project_codes == None: search = Search('sthpw/project') projects = search.get_sobjects() self.project_codes = [x.get_code() for x in projects] self.project_codes.append('*') for rule_node in rule_nodes: # initiate the project_code here for each loop project_code = '*' group_type = Xml.get_attribute( rule_node, "group" ) if not group_type: # category is the preferred name over group now # TODO: phase out the use of group completely group_type = Xml.get_attribute( rule_node, "category" ) # get an existing rule set or create a new one if self.groups.has_key(group_type): rules = self.groups[group_type] else: rules = {} self.groups[group_type] = rules # set the default, if specified group_default = xml.get_attribute( rule_node, "default" ) if group_default: rules['__DEFAULT__'] = group_default continue # generate the rule key #rule_key = xml.get_attribute(rule_node, 'key') attrs = xml.get_attributes(rule_node) attrs2 = {} count = 0 for name, value in attrs.items(): if name in ['access', 'group', 'category', 'project']: continue # have to turn everything into strings attrs2[str(name)] = str(value) count += 1 if count == 1 and attrs2.has_key('key'): # backwards compatibility rule_key = attrs2['key'] else: #rule_key = str(attrs2) rule_key = str(Common.get_dict_list(attrs2)) if project_override: rule_project = project_override else: rule_project = xml.get_attribute(rule_node, 'project') if rule_project: project_code = rule_project # special treatment for search_filter to enable # project-specific search if group_type=='search_filter': attrs2['project'] = rule_project # if there is a value, then combine it with the key rule_value = xml.get_attribute(rule_node, 'value') if rule_value: rule_key = "%s||%s" % (rule_key, rule_value) # add a project code qualifier rule_keys = [] # project rule is special if group_type == 'project': key = str(rule_key) rule_keys.append(key) elif project_code == '*' and group_type != 'search_filter': for code in self.project_codes: key = "%s?project=%s" % (rule_key, code) rule_keys.append(key) else: key= "%s?project=%s" % (rule_key, project_code) #key = str(key) # may need to stringify unicode string rule_keys.append(key) #key= "%s?project=*" % (rule_key) #rule_keys.append(key) rule_access = xml.get_attribute(rule_node, 'access') #if rule_access == "": # raise AccessException("Cannot have empty 'access':\n%s" \ # % xml.to_string(rule_node) ) # if no key is specified, it is considered a DEFAULT if not rule_keys and not rule_value: rule_keys = ['__DEFAULT__'] for rule_key in rule_keys: # check if rule_access exists first, which doesn't for search_filter, # but it has to go into the rules regardless # if the rule already exists, take the highest one if rule_access and rules.has_key(rule_key): curr_access, cur_attrs = rules[rule_key] try: access_enum = self._get_access_enum(rule_access) if self._get_access_enum(curr_access) > access_enum: continue except: if group_type == "builtin": continue else: raise rules[rule_key] = rule_access, attrs2 # FIXME: this one doesn't support the multi-attr structure # convert this to a python data structure group_nodes = xml.get_nodes("rules/group") for group_node in group_nodes: group_type = Xml.get_attribute( group_node, "type" ) # get an existing rule set or create a new one if self.groups.has_key(group_type): rules = self.groups[group_type] else: rules = {} self.groups[group_type] = rules # set the default, if specified group_default = xml.get_attribute( group_node, "default" ) if group_default != "": rules['__DEFAULT__'] = group_default # get all of the rule nodes rule_nodes = Xml.get_children(group_node) for rule_node in rule_nodes: project_code='*' if Xml.get_node_name(rule_node) != 'rule': continue rule_key = xml.get_attribute(rule_node, 'key') rule_access = xml.get_attribute(rule_node, 'access') if project_override: rule_project = project_override else: rule_project = xml.get_attribute(rule_node, 'project') if rule_project: project_code = rule_project if rule_access == "": raise AccessException("Cannot have empty 'access':\n%s" \ % xml.to_string(rule_node) ) rule_keys = [] attrs2 = {'key': rule_key} # add a project code qualifier if project_code == '*' and group_type != 'search_filter': for code in self.project_codes: key = "%s?project=%s" % (rule_key, code) rule_keys.append(key) else: key= "%s?project=%s" % (rule_key, project_code) rule_keys.append(key) for rule_key in rule_keys: rules[rule_key] = rule_access, attrs2
APPROVAL_PIPELINE = ''' <pipeline type="serial"> <process completion="10" color="#8ad3e5" name="Pending"/> <process completion="10" color="#8ad3e5" name="Review"/> <process completion="50" color="#e84a4d" name="Revise"/> <process completion="100" color="#a3d991" name="Approve"/> <connect to="Review" from="Pending"/> <connect to="Revise" from="Review"/> <connect to="Approve" from="Review"/> </pipeline> ''' default_xml = Xml() default_xml.read_string(TASK_PIPELINE) OTHER_COLORS = { "Complete": "#a3d991", "Done": "#a3d991", "Final": "#a3d991", "Revise": "#e84a4d", "Ready": "#a3d991", "In_Progress":"#e9e386", } class Task(SObject): SEARCH_TYPE = "sthpw/task"
def get_default_filter_config(my): custom_filter_view = my.kwargs.get('custom_filter_view') if not custom_filter_view: custom_filter_view = '' config = [] config.append("<config>\n") config.append("<filter>\n") config.append(''' <element name='Combination'> <display class='tactic.ui.filter.SObjectSearchFilterWdg'> <search_type>%s</search_type> <prefix>quick</prefix> </display> </element> ''' % my.search_type) config.append(''' <element name='Custom'> <display class='tactic.ui.filter.GeneralFilterWdg'> <prefix>custom</prefix> <search_type>%s</search_type> <mode>custom</mode> <custom_filter_view>%s</custom_filter_view> </display> </element> ''' % (my.search_type, custom_filter_view)) config.append(''' <element name='Filter'> <display class='tactic.ui.filter.GeneralFilterWdg'> <prefix>main_body</prefix> <search_type>%s</search_type> <mode>sobject</mode> </display> </element> ''' % my.search_type) """ config.append(''' <element name='Filter2'> <display class='tactic.ui.filter.GeneralFilterWdg'> <prefix>filter2</prefix> <search_type>%s</search_type> <mode>sobject</mode> </display> </element> ''' % my.search_type) """ config.append(''' <element name='Parent'> <display class='tactic.ui.filter.GeneralFilterWdg'> <prefix>parent</prefix> <search_type>%s</search_type> <mode>parent</mode> </display> </element> ''' % my.search_type) config.append(''' <element name='Children'> <display class='tactic.ui.filter.GeneralFilterWdg'> <prefix>children</prefix> <search_type>%s</search_type> <mode>child</mode> </display> </element> ''' % my.search_type) """ config.append(''' <element name='Related'> <display class='tactic.ui.filter.GeneralFilterWdg'> <prefix>related</prefix> <search_type>%s</search_type> <mode>child</mode> </display> </element> ''' % my.search_type) """ config.append("</filter>\n") config.append("</config>\n") config = ''.join(config) config_xml = Xml() config_xml.read_string(config) config = WidgetConfig.get(xml=config_xml, view='filter') return config
def get_widget_from_hash(cls, hash, return_none=False, force_no_index=False, kwargs={}): from pyasm.web import DivWdg if hash.startswith("//"): use_top = False hash = hash[1:] else: use_top = True import re p = re.compile("^/(\w+)") m = p.search(hash) if not m: if return_none: return None print "Cannot parse hash[%s]" % hash return DivWdg("Cannot parse hash [%s]" % hash) key = m.groups()[0] # guest user should never be able to see admin site if key != 'login': security = Environment.get_security() login = security.get_user_name() if login == "guest" and key == 'admin': from pyasm.widget import Error403Wdg return Error403Wdg().get_buffer_display() sobject = cls._get_predefined_url(key, hash) # look up the url if not sobject: search = Search("config/url") search.add_filter("url", "/%s/%%"%key, "like") search.add_filter("url", "/%s"%key) search.add_where("or") sobject = search.get_sobject() if not sobject: if return_none: return None return DivWdg("No Widget found for hash [%s]" % hash) config = sobject.get_value("widget") config = config.replace('&','&') url = sobject.get_value("url") url = url.strip() # update the config value with expressions options = Common.extract_dict(hash, url) for name, value in options.items(): config = config.replace("{%s}" % name, value) xml = Xml() xml.read_string(config) use_index, use_admin, use_sidebar = cls._get_flags(xml, sobject, force_no_index, kwargs) if use_admin: # use admin from tactic.ui.app import PageNavContainerWdg top = PageNavContainerWdg( hash=hash, use_sidebar=use_sidebar ) return top.get_buffer_display() elif use_index: # check if there is an index search = Search("config/url") search.add_filter("url", "/index") index = search.get_sobject() # just use admin if no index page is found if not index: from tactic.ui.app import PageNavContainerWdg top = PageNavContainerWdg( hash=hash, use_sidebar=use_sidebar ) return top.get_buffer_display() config = index.get_value("widget") xml = Xml() xml.read_string(config) node = xml.get_node("element/display") options.update(xml.get_node_values_of_children(node)) class_name = xml.get_value("element/display/@class") if class_name: options['class_name'] = class_name # this passes the hash value to the index widget # which must handle it accordingly options['hash'] = hash top = cls.build_widget(options) return top.get_buffer_display() # process the options and then build the widget from the xml options = Common.extract_dict(hash, url) for name, value in kwargs.items(): options[name] = value node = xml.get_node("element/display") options.update(xml.get_node_values_of_children(node)) class_name = xml.get_value("element/display/@class") if class_name: options['class_name'] = class_name widget = cls.build_widget(options) name = hash.lstrip("/") name = name.replace("/", " ") widget.set_name(name) return widget