def action(self): if html.request.var("_action") != "discard": return if not html.check_transaction(): return if not self._may_discard_changes(): return # TODO: Remove once new changes mechanism has been implemented # Now remove all currently pending changes by simply restoring the last automatically # taken snapshot. Then activate the configuration. This should revert all pending changes. file_to_restore = self._get_last_wato_snapshot_file() if not file_to_restore: raise MKUserError(None, _('There is no WATO snapshot to be restored.')) msg = _("Discarded pending changes (Restored %s)") % file_to_restore # All sites and domains can be affected by a restore: Better restart everything. watolib.add_change("changes-discarded", msg, domains=watolib.ConfigDomain.enabled_domains(), need_restart=True) self._extract_snapshot(file_to_restore) cmk.gui.watolib.activate_changes.execute_activate_changes( [d.ident for d in watolib.ConfigDomain.enabled_domains()]) for site_id in cmk.gui.watolib.changes.activation_site_ids(): self.confirm_site_changes(site_id) html.header(self.title(), show_body_start=display_options.enabled(display_options.H), show_top_heading=display_options.enabled( display_options.T)) html.open_div(class_="wato") html.begin_context_buttons() home_button() html.end_context_buttons() html.message(_("Successfully discarded all pending changes.")) html.javascript("hide_changes_buttons();") html.footer() return False
def delete_job(self): job_id = html.request.var(self.delete_job_var) if not job_id: return job = GUIBackgroundJob(job_id=job_id) if not job.is_available(): return html.header("Deletion of job") c = html.confirm(_("Delete job %s%s?") % (job_id, self._get_extra_info(job))) if c and job.may_delete(): job.delete() self._did_delete_job = True html.message(_("Background job has been deleted"))
def _show_diagnose_output(self): if not html.request.var('_try'): html.message( _('You can diagnose the connection to a specific host using this dialog. ' 'You can either test whether your current configuration is still working ' 'or investigate in which ways a host can be reached. Simply configure the ' 'connection options you like to try on the right side of the screen and ' 'press the "Test" button. The results will be displayed here.' )) return if html.has_user_errors(): html.show_user_errors() return # TODO: Insert any vs_host valuespec validation # These tests can be called with invalid valuespec settings... # TODO: Replace hard coded icon paths with dynamic ones to old or new theme for ident, title in ModeDiagHost.diag_host_tests(): html.h3(title) html.open_table(class_=["data", "test"]) html.open_tr(class_=["data", "odd0"]) html.open_td(class_="icons") html.open_div() html.icon(title=None, icon="reload", id_="%s_img" % ident) html.open_a(href="") html.icon(title=_('Retry this test'), icon="reload", cssclass="retry", id_="%s_retry" % ident) html.close_a() html.close_div() html.close_td() html.open_td() html.div('', class_="log", id="%s_log" % ident) html.close_td() html.close_tr() html.close_table() html.javascript( 'cmk.host_diagnose.start_test(%s, %s, %s)' % (json.dumps(ident), json.dumps(self._hostname), json.dumps(html.transaction_manager.fresh_transid())))
def page(self): self._folder.show_breadcrump() if not self._folder.may("read"): html.message( html.render_icon("autherr", cssclass="authicon") + " " + self._folder.reason_why_may_not("read")) self._folder.show_locking_information() self._show_subfolders_of() if self._folder.may("read"): self._show_hosts() if not self._folder.has_hosts(): if self._folder.is_search_folder(): html.message(_("No matching hosts found.")) elif not self._folder.has_subfolders() and self._folder.may("write"): self._show_empty_folder_menu()
def stop_job(self): job_id = html.request.var(self.stop_job_var) if not job_id: return job = GUIBackgroundJob(job_id) if not job.is_available(): return html.header("Interuption of job") if self.confirm_dialog_opened() and not job.is_running(): html.message(_("No longer able to stop job. Background job just finished.")) return c = html.confirm(_("Stop job %s%s?") % (job_id, self._get_extra_info(job))) if c and job.may_stop(): job.stop() self._did_stop_job = True html.message(_("Background job has been stopped"))
def page(self): job_id = html.request.var("job_id") job = gui_background_job.GUIBackgroundJob(job_id) if not job.exists(): html.message(_("Background job info is not available")) return try: # Race condition, the job might get deleted during snapshot generation job_snapshot = job.get_status_snapshot() except Exception: html.message(_("Background job info is not available")) logger.error(traceback.format_exc()) return job_manager = gui_background_job.GUIBackgroundJobManager() job_manager.show_job_details_from_snapshot(job_snapshot) if job_snapshot.is_running(): html.immediate_browser_redirect(1, "")
def _end(self): if not self.rows and self.options["omit_if_empty"]: return if self.options["output_format"] == "csv": self._write_csv( csv_separator=html.request.var("csv_separator", ";")) return if self.title: if self.options["foldable"]: html.begin_foldable_container( treename="table", id_=self.id, isopen=True, indent=False, title=html.render_h3(self.title, class_=["treeangle", "title"])) else: html.open_h3() html.write(self.title) html.close_h3() if self.help: html.help(self.help) if not self.rows: html.div(self.empty_text, class_="info") return # Controls whether or not actions are available for a table rows, actions_enabled, actions_visible, search_term, user_opts = self._evaluate_user_opts( ) # Apply limit after search / sorting etc. num_rows_unlimited = len(rows) limit = self.limit if limit is not None: # only use rows up to the limit plus the fixed rows rows = [ rows[i] for i in range(num_rows_unlimited) if i < limit or rows[i][3] ] # Display corrected number of rows num_rows_unlimited -= len([r for r in rows if r[3]]) # Render header self._write_table(rows, actions_enabled, actions_visible, search_term) if self.title and self.options["foldable"]: html.end_foldable_container() if limit is not None and num_rows_unlimited > limit: html.message( _('This table is limited to show only %d of %d rows. ' 'Click <a href="%s">here</a> to disable the limitation.') % (limit, num_rows_unlimited, html.makeuri([('limit', 'none')]))) if actions_enabled: config.user.save_file("tableoptions", user_opts) return
def page(self): html.open_div(id_="ldap") html.open_table() html.open_tr() html.open_td() html.begin_form("connection", method="POST") html.prevent_password_auto_completion() vs = self._valuespec() vs.render_input("connection", self._connection_cfg) vs.set_focus("connection") html.button("_save", _("Save")) html.button("_test", _("Save & Test")) html.hidden_fields() html.end_form() html.close_td() html.open_td(style="padding-left:10px;vertical-align:top") html.h2(_('Diagnostics')) if not html.request.var('_test') or not self._connection_id: html.message( HTML('<p>%s</p><p>%s</p>' % ( _('You can verify the single parts of your ldap configuration using this ' 'dialog. Simply make your configuration in the form on the left side and ' 'hit the "Save & Test" button to execute the tests. After ' 'the page reload, you should see the results of the test here.' ), _('If you need help during configuration or experience problems, please refer ' 'to the <a target="_blank" ' 'href="https://checkmk.com/checkmk_multisite_ldap_integration.html">' 'LDAP Documentation</a>.')))) else: connection = userdb.get_connection(self._connection_id) for address in connection.servers(): html.h3("%s: %s" % (_('Server'), address)) with table_element('test', searchable=False) as table: for title, test_func in self._tests(): table.row() try: state, msg = test_func(connection, address) except Exception as e: state = False msg = _('Exception: %s') % html.render_text(e) logger.exception("error testing LDAP %s for %s", title, address) if state: img = html.render_icon("success", _('Success')) else: img = html.render_icon("failed", _("Failed")) table.cell(_("Test"), title) table.cell(_("State"), img) table.cell(_("Details"), msg) connection.disconnect() html.close_td() html.close_tr() html.close_table() html.close_div()
def do_log_ack(site, host_name, file_name): logs_to_ack = [] if not host_name and not file_name: # all logs on all hosts for this_site, this_host, logs in all_logs(): for int_filename in logs: file_display = form_file_to_ext(int_filename) logs_to_ack.append( (this_site, this_host, int_filename, file_display)) ack_msg = _('all logfiles on all hosts') elif host_name and not file_name: # all logs on one host for int_filename in logfiles_of_host(site, host_name): file_display = form_file_to_ext(int_filename) logs_to_ack.append((site, host_name, int_filename, file_display)) ack_msg = _('all logfiles of host %s') % html.render_text(host_name) elif host_name and file_name: # one log on one host int_filename = form_file_to_int(file_name) logs_to_ack = [(site, host_name, int_filename, form_file_to_ext(int_filename))] ack_msg = html.render_text( _('the log file %s on host %s') % (file_name, host_name)) else: for this_site, this_host, logs in all_logs(): file_display = form_file_to_ext(file_name) if file_name in logs: logs_to_ack.append( (this_site, this_host, file_name, file_display)) ack_msg = html.render_text(_('log file %s on all hosts') % file_name) html.header(_("Acknowledge %s") % ack_msg) html.begin_context_buttons() button_all_logfiles() if host_name: html.context_button(_("All Logfiles of Host"), html.makeuri([('file', '')])) if host_name and file_name: html.context_button(_("Back to Logfile"), html.makeuri([])) html.end_context_buttons() ack = html.request.var('_ack') if not html.confirm( _("Do you really want to acknowledge %s by <b>deleting</b> all stored messages?" ) % ack_msg): html.footer() return if not config.user.may("general.act"): html.h1(_('Permission denied'), class_=["error"]) html.div(_('You are not allowed to acknowledge %s') % ack_msg, class_=["error"]) html.footer() return # filter invalid values if ack != '1': raise MKUserError('_ack', _('Invalid value for ack parameter.')) for this_site, this_host, int_filename, display_name in logs_to_ack: try: acknowledge_logfile(this_site, this_host, int_filename, display_name) except Exception as e: html.show_error(_('The log file <tt>%s</tt> of host <tt>%s</tt> could not be deleted: %s.') % \ (display_name, this_host, e)) html.footer() return html.message('<b>%s</b><p>%s</p>' % (_('Acknowledged %s') % ack_msg, _('Acknowledged all messages in %s.') % ack_msg)) html.footer()
def show_file(site, host_name, file_name): int_filename = form_file_to_int(file_name) html.header(_("Logfiles of Host %s: %s") % (host_name, file_name)) html.begin_context_buttons() html.context_button(_("Services"), services_url(site, host_name), 'services') html.context_button(_("All Logfiles of Host"), html.makeuri([('file', '')])) button_all_logfiles() html.context_button(_("Analyze patterns"), analyse_url(site, host_name, file_name), 'analyze') if html.request.var('_hidecontext', 'no') == 'yes': hide_context_label = _('Show Context') hide_context_param = 'no' hide = True else: hide_context_label = _('Hide Context') hide_context_param = 'yes' hide = False try: log_chunks = parse_file(site, host_name, int_filename, hide) except Exception as e: if config.debug: raise html.end_context_buttons() html.show_error(_("Unable to show logfile: <b>%s</b>") % e) html.footer() return if log_chunks is None: html.end_context_buttons() html.show_error(_("The logfile does not exist.")) html.footer() return elif log_chunks == []: html.end_context_buttons() html.message(_("This logfile contains no unacknowledged messages.")) html.footer() return ack_button(site, host_name, int_filename) html.context_button(hide_context_label, html.makeuri([('_hidecontext', hide_context_param)])) html.end_context_buttons() html.open_div(id_="logwatch") for log in log_chunks: html.open_div(class_=["chunk"]) html.open_table(class_=["section"]) html.open_tr() html.td(form_level(log['level']), class_=form_level(log['level'])) html.td(form_datetime(log['datetime']), class_="date") html.close_tr() html.close_table() for line in log['lines']: html.open_p(class_=line['class']) html.icon_button( analyse_url(site, host_name, file_name, line['line']), _("Analyze this line"), "analyze") html.write_text(line['line'].replace(" ", " ").replace( "\1", "<br>")) html.close_p() html.close_div() html.close_div() html.footer()
def page_crashed(what): # Do not reveal crash context information to unauthenticated users or not permitted # users to prevent disclosure of internal information if not config.user.may("general.see_crash_reports"): html.header(_("Internal error")) html.show_error("<b>%s:</b> %s" % (_("Internal error"), sys.exc_info()[1])) html.p( _("An internal error occurred while processing your request. " "You can report this issue to your Check_MK administrator. " "Detailed information can be found in <tt>var/log/web.log</tt>.") ) html.footer() return if what == "check": site = html.request.var("site") host = html.request.var("host") service = html.request.var("service") tardata = get_crash_report_archive_as_string(site, host, service) else: tardata = create_gui_crash_report(what) info = get_crash_info(tardata) if what == "check": title = _("Crashed Check Reporting") else: title = _("Internal error") html.header(title) show_context_buttons(what, tardata) if html.request.has_var("_report") and html.check_transaction(): details = handle_report_form(tardata, what) else: details = {} if what == "gui": # Unify different string types from exception messages to a unicode string exc_value = sys.exc_info()[1] try: exc_txt = unicode(exc_value) except UnicodeDecodeError: exc_txt = str(exc_value).decode("utf-8") html.show_error("<b>%s:</b> %s" % (_("Internal error"), exc_txt)) html.p( _("An internal error occured while processing your request. " "You can report this issue to the Check_MK team to help " "fixing this issue. Please use the form below for reporting.")) if info: warn_about_local_files(info) show_report_form(what, details) show_crash_report(info) show_crash_report_details(info) else: report_url = html.makeuri( [("subject", "Check_MK Crash Report - " + get_version(what))], filename="mailto:" + get_crash_report_target(what)) html.message( _("This crash report is in a legacy format and can not be submitted " "automatically. Please download it manually and send it to <a href=\"%s\">%s</a>" ) % (report_url, get_crash_report_target(what))) show_old_dump_trace(tardata) show_agent_output(tardata) html.footer()
def _process_notify_message(msg): msg['id'] = utils.gen_id() msg['time'] = time.time() # construct the list of recipients recipients = [] if isinstance(msg['dest'], str): dest_what = msg['dest'] else: dest_what = msg['dest'][0] if dest_what == 'broadcast': recipients = config.multisite_users.keys() elif dest_what == 'online': recipients = userdb.get_online_user_ids() elif dest_what == 'list': recipients = msg['dest'][1] num_recipients = len(recipients) num_success = {} for method in msg['methods']: num_success[method] = 0 # Now loop all notitification methods to send the notifications errors = {} for user_id in recipients: for method in msg['methods']: try: handler = _notify_methods()[method]['handler'] handler(user_id, msg) num_success[method] = num_success[method] + 1 except MKInternalError as e: errors.setdefault(method, []).append((user_id, e)) message = _('The notification has been sent via<br>') message += "<table>" for method in msg['methods']: message += "<tr><td>%s</td><td>to %d of %d recipients</td></tr>" %\ (_notify_methods()[method]["title"], num_success[method], num_recipients) message += "</table>" message += _('<p>Sent notification to: %s</p>') % ', '.join(recipients) message += '<a href="%s">%s</a>' % (html.makeuri( []), _('Back to previous page')) html.message(HTML(message)) if errors: error_message = "" for method, method_errors in errors.items(): error_message += _( "Failed to send %s notifications to the following users:" ) % method table_rows = '' for user, exception in method_errors: table_rows += html.render_tr(html.render_td(html.render_tt(user))\ + html.render_td(exception)) error_message += html.render_table(table_rows) + html.render_br() html.show_error(HTML(error_message))
def _show_page_user_profile(change_pw): start_async_replication = False if not config.user.id: raise MKUserError(None, _('Not logged in.')) if not config.user.may('general.edit_profile') and not config.user.may( 'general.change_password'): raise MKAuthException( _("You are not allowed to edit your user profile.")) if not config.wato_enabled: raise MKAuthException( _('User profiles can not be edited (WATO is disabled).')) success = None if html.request.has_var('_save') and html.check_transaction(): users = userdb.load_users(lock=True) try: # Profile edit (user options like language etc.) if config.user.may('general.edit_profile'): if not change_pw: set_lang = html.get_checkbox('_set_lang') language = html.request.var('language') # Set the users language if requested if set_lang: if language == '': language = None # Set custom language users[config.user.id]['language'] = language config.user.language = language html.set_language_cookie(language) else: # Remove the customized language if 'language' in users[config.user.id]: del users[config.user.id]['language'] config.user.reset_language() # load the new language cmk.gui.i18n.localize(config.user.language) user = users.get(config.user.id) if config.user.may('general.edit_notifications' ) and user.get("notifications_enabled"): value = forms.get_input( watolib.get_vs_flexible_notifications(), "notification_method") users[config.user.id]["notification_method"] = value # Custom attributes if config.user.may('general.edit_user_attributes'): for name, attr in userdb.get_user_attributes(): if attr.user_editable(): if not attr.permission() or config.user.may( attr.permission()): vs = attr.valuespec() value = vs.from_html_vars('ua_' + name) vs.validate_value(value, "ua_" + name) users[config.user.id][name] = value # Change the password if requested password_changed = False if config.user.may('general.change_password'): cur_password = html.request.var('cur_password') password = html.request.var('password') password2 = html.request.var('password2', '') if change_pw: # Force change pw mode if not cur_password: raise MKUserError( "cur_password", _("You need to provide your current password.")) if not password: raise MKUserError( "password", _("You need to change your password.")) if cur_password == password: raise MKUserError( "password", _("The new password must differ from your current one." )) if cur_password and password: if userdb.hook_login(config.user.id, cur_password) in [None, False]: raise MKUserError("cur_password", _("Your old password is wrong.")) if password2 and password != password2: raise MKUserError( "password2", _("The both new passwords do not match.")) watolib.verify_password_policy(password) users[config.user.id]['password'] = hash_password(password) users[config.user.id]['last_pw_change'] = int(time.time()) if change_pw: # Has been changed, remove enforcement flag del users[config.user.id]['enforce_pw_change'] # Increase serial to invalidate old cookies if 'serial' not in users[config.user.id]: users[config.user.id]['serial'] = 1 else: users[config.user.id]['serial'] += 1 password_changed = True # Now, if in distributed environment where users can login to remote sites, # set the trigger for pushing the new auth information to the slave sites # asynchronous if config.user.authorized_login_sites(): start_async_replication = True userdb.save_users(users) if password_changed: # Set the new cookie to prevent logout for the current user login.set_auth_cookie(config.user.id) success = True except MKUserError as e: html.add_user_error(e.varname, e) else: users = userdb.load_users() watolib.init_wato_datastructures(with_wato_lock=True) # When in distributed setup, display the replication dialog instead of the normal # profile edit dialog after changing the password. if start_async_replication: user_profile_async_replication_page() return if change_pw: title = _("Change Password") else: title = _("Edit User Profile") html.header(title) # Rule based notifications: The user currently cannot simply call the according # WATO module due to WATO permission issues. So we cannot show this button # right now. if not change_pw: rulebased_notifications = watolib.load_configuration_settings().get( "enable_rulebased_notifications") if rulebased_notifications and config.user.may( 'general.edit_notifications'): html.begin_context_buttons() url = "wato.py?mode=user_notifications_p" html.context_button(_("Notifications"), url, "notifications") html.end_context_buttons() else: reason = html.request.var('reason') if reason == 'expired': html.p( _('Your password is too old, you need to choose a new password.' )) else: html.p( _('You are required to change your password before proceeding.' )) if success: html.reload_sidebar() if change_pw: html.message(_("Your password has been changed.")) raise HTTPRedirect(html.request.var('_origtarget', 'index.py')) else: html.message(_("Successfully updated user profile.")) # Ensure theme changes are applied without additional user interaction html.immediate_browser_redirect(0.5, html.makeuri([])) if html.has_user_errors(): html.show_user_errors() user = users.get(config.user.id) if user is None: html.show_warning(_("Sorry, your user account does not exist.")) html.footer() return # Returns true if an attribute is locked and should be read only. Is only # checked when modifying an existing user locked_attributes = userdb.locked_attributes(user.get('connector')) def is_locked(attr): return attr in locked_attributes html.begin_form("profile", method="POST") html.prevent_password_auto_completion() html.open_div(class_="wato") forms.header(_("Personal Settings")) if not change_pw: forms.section(_("Name"), simple=True) html.write_text(user.get("alias", config.user.id)) if config.user.may( 'general.change_password') and not is_locked('password'): forms.section(_("Current Password")) html.password_input('cur_password', autocomplete="new-password") forms.section(_("New Password")) html.password_input('password', autocomplete="new-password") forms.section(_("New Password Confirmation")) html.password_input('password2', autocomplete="new-password") if not change_pw and config.user.may('general.edit_profile'): select_language(user) # Let the user configure how he wants to be notified if not rulebased_notifications \ and config.user.may('general.edit_notifications') \ and user.get("notifications_enabled"): forms.section(_("Notifications")) html.help( _("Here you can configure how you want to be notified about host and service problems and " "other monitoring events.")) watolib.get_vs_flexible_notifications().render_input( "notification_method", user.get("notification_method")) if config.user.may('general.edit_user_attributes'): for name, attr in userdb.get_user_attributes(): if attr.user_editable(): vs = attr.valuespec() forms.section(_u(vs.title())) value = user.get(name, vs.default_value()) if not attr.permission() or config.user.may( attr.permission()): vs.render_input("ua_" + name, value) html.help(_u(vs.help())) else: html.write(vs.value_to_text(value)) # Save button forms.end() html.button("_save", _("Save")) html.close_div() html.hidden_fields() html.end_form() html.footer()
def page(self): html.message(_("(This module is not yet implemented)"))
def _show_configuration_variables(self, groups): search_form(_("Search for settings:")) search = self._search html.open_div(class_="filter_buttons") if self._show_only_modified: html.buttonlink(html.makeuri([], delvars=["show_only_modified"]), _("Show all settings")) else: html.buttonlink(html.makeuri([("show_only_modified", "1")]), _("Show only modified settings")) html.close_div() at_least_one_painted = False html.open_div(class_="globalvars") for group in sorted(groups, key=lambda g: g.sort_index()): header_is_painted = False # needed for omitting empty groups for config_variable_class in group.config_variables(): config_variable = config_variable_class() varname = config_variable.ident() valuespec = config_variable.valuespec() if not config_variable.domain().enabled(): continue if config_variable.domain( ) == watolib.ConfigDomainCore and varname not in self._default_values: if config.debug: raise MKGeneralException( "The configuration variable <tt>%s</tt> is unknown to " "your local Check_MK installation" % varname) else: continue if not config_variable.in_global_settings(): continue if self._show_only_modified and varname not in self._current_settings: continue help_text = valuespec.help() or '' title_text = valuespec.title() if search and search not in group.title().lower() \ and search not in config_variable.domain().ident.lower() \ and search not in varname \ and search not in help_text.lower() \ and search not in title_text.lower(): continue # skip variable when search is performed and nothing matches at_least_one_painted = True if not header_is_painted: # always open headers when searching forms.header(group.title(), isopen=search or self._show_only_modified) header_is_painted = True default_value = self._default_values[varname] edit_url = watolib.folder_preserving_link([ ("mode", self._edit_mode()), ("varname", varname), ("site", html.request.var("site", "")) ]) title = html.render_a(title_text, href=edit_url, class_="modified" if varname in self._current_settings else None, title=html.strip_tags(help_text)) if varname in self._current_settings: value = self._current_settings[varname] elif varname in self._global_settings: value = self._global_settings[varname] else: value = default_value try: to_text = valuespec.value_to_text(value) except Exception: logger.exception() to_text = html.render_error( _("Failed to render value: %r") % value) # Is this a simple (single) value or not? change styling in these cases... simple = True if '\n' in to_text or '<td>' in to_text: simple = False forms.section(title, simple=simple) if varname in self._current_settings: modified_cls = "modified" title = _("This option has been modified.") elif varname in self._global_settings: modified_cls = "modified globally" title = _( "This option has been modified in global settings.") else: modified_cls = None title = None if is_a_checkbox(valuespec): html.open_div( class_=["toggle_switch_container", modified_cls]) html.toggle_switch( enabled=value, help_txt=_("Immediately toggle this setting"), href=html.makeactionuri([("_action", "toggle"), ("_varname", varname)]), class_=modified_cls, title=title, ) html.close_div() else: html.a(HTML(to_text), href=edit_url, class_=modified_cls, title=title) if header_is_painted: forms.end() if not at_least_one_painted and search: html.message( _('Did not find any global setting matching your search.')) html.close_div()
def page_edit(cls): back_url = html.get_url_input("back", cls.list_url()) cls.load() cls.need_overriding_permission("edit") # Three possible modes: # "create" -> create completely new page # "clone" -> like new, but prefill form with values from existing page # "edit" -> edit existing page mode = html.request.var('mode', 'edit') if mode == "create": title = cls.phrase("create") page_dict = { "name": cls.default_name(), "topic": cls.default_topic(), } else: # Load existing page. visual from disk - and create a copy if 'load_user' is set page_name = html.request.var("load_name") if mode == "edit": title = cls.phrase("edit") owner_user_id = html.request.var("owner", config.user.id) if owner_user_id == config.user.id: page = cls.find_my_page(page_name) else: page = cls.find_foreign_page(owner_user_id, page_name) if page is None: raise MKUserError(None, _("The requested %s does not exist") % cls.phrase("title")) # TODO FIXME: Looks like a hack cls.remove_instance((owner_user_id, page_name)) # will be added later again else: # clone title = cls.phrase("clone") load_user = html.get_unicode_input("load_user") # FIXME: Change varname to "owner" try: page = cls.instance((load_user, page_name)) except KeyError: raise MKUserError(None, _("The requested %s does not exist") % cls.phrase("title")) page_dict = page.internal_representation() html.header(title) html.begin_context_buttons() html.context_button(_("Back"), back_url, "back") html.end_context_buttons() parameters, keys_by_topic = cls._collect_parameters(mode) vs = Dictionary( title=_("General Properties"), render='form', optional_keys=None, elements=parameters, headers=keys_by_topic, ) def validate(page_dict): owner_user_id = html.request.var("owner", config.user.id) page_name = page_dict["name"] if owner_user_id == config.user.id: page = cls.find_my_page(page_name) else: page = cls.find_foreign_page(owner_user_id, page_name) if page: raise MKUserError( "_p_name", _("You already have an element with the ID <b>%s</b>") % page_dict["name"]) new_page_dict = forms.edit_valuespec( vs, page_dict, validate=validate, focus="_p_title", method="POST") if new_page_dict is not None: # Take over keys from previous value that are specific to the page type # and not edited here. if mode in ("edit", "clone"): for key, value in page_dict.items(): new_page_dict.setdefault(key, value) owner = html.request.var("owner", config.user.id) new_page_dict["owner"] = owner new_page = cls(new_page_dict) cls.add_page(new_page) cls.save_user_instances(owner) if mode == "create": redirect_url = new_page.after_create_url() or back_url else: redirect_url = back_url html.immediate_browser_redirect(0.5, redirect_url) html.message(_('Your changes haven been saved.')) # Reload sidebar.TODO: This code logically belongs to PageRenderer. How # can we simply move it there? # TODO: This is not true for all cases. e.g. the BookmarkList is not # of type PageRenderer but has a dedicated sidebar snapin. Maybe # the best option would be to make a dedicated method to decide whether # or not to reload the sidebar. if new_page_dict.get("hidden") in [ None, False ] \ or new_page_dict.get("hidden") != page_dict.get("hidden"): html.reload_sidebar() else: html.show_localization_hint() html.footer() return