/** * @param mixed $handler_id The ID of the handler. * @param Array $args The argument list. * @param Array &$data The local request data. */ public function _handler_delete($handler_id, array $args, array &$data) { midcom::get('auth')->require_user_do('org.openpsa.user:manage', null, 'org_openpsa_user_interface'); $this->_group = new midcom_db_group($args[0]); if (array_key_exists('org_openpsa_user_deleteok', $_POST)) { $delete_succeeded = $this->_group->delete(); $prefix = midcom_core_context::get()->get_key(MIDCOM_CONTEXT_ANCHORPREFIX); if ($delete_succeeded) { // Update the index $indexer = midcom::get('indexer'); $indexer->delete($this->_group->guid); return new midcom_response_relocate(''); } else { // Failure, give a message midcom::get('uimessages')->add($this->_l10n->get('org.openpsa.user'), $this->_l10n->get("failed to delete group, reason") . ' ' . midcom_connection::get_error_string(), 'error'); return new midcom_response_relocate($prefix . 'group/' . $this->_group->guid . '/'); } } $data['view'] = midcom_helper_datamanager2_handler::get_view_controller($this, $this->_group); $data['group'] = $this->_group; org_openpsa_widgets_tree::add_head_elements(); $this->add_breadcrumb('groups/', $this->_l10n->get('groups')); $this->add_breadcrumb('', sprintf($this->_l10n_midcom->get('delete %s'), $this->_group->get_label())); $this->bind_view_to_object($this->_group); }
/** * Locates the root group */ static function find_root_group($name = '__org_openpsa_contacts') { static $root_groups = array(); //Check if we have already initialized if (!empty($root_groups[$name])) { return $root_groups[$name]; } $qb = midcom_db_group::new_query_builder(); $qb->add_constraint('owner', '=', 0); $qb->add_constraint('name', '=', $name); $results = $qb->execute(); if (is_array($results) && count($results) > 0) { foreach ($results as $group) { $root_groups[$name] = $group; } } else { debug_add("OpenPSA Contacts root group could not be found", MIDCOM_LOG_WARN); //Attempt to auto-initialize the group. midcom::get('auth')->request_sudo(); $grp = new midcom_db_group(); $grp->owner = 0; $grp->name = $name; $grp->official = midcom::get('i18n')->get_l10n('org.openpsa.contacts')->get($name); $ret = $grp->create(); midcom::get('auth')->drop_sudo(); if (!$ret) { throw new midcom_error("Could not auto-initialize the module, group creation failed: " . midcom_connection::get_error_string()); } $root_groups[$name] = $grp; } return $root_groups[$name]; }
/** * DM2 creation callback. */ function &dm2_create_callback(&$controller) { // Create a new group $this->_group = new midcom_db_group(); if (!$this->_group->create()) { debug_print_r('We operated on this object:', $this->_group); throw new midcom_error('Failed to create a new group. Last Midgard error was: ' . midcom_connection::get_error_string()); } return $this->_group; }
public function testCRUD() { midcom::get('auth')->request_sudo('midcom.core'); $group = new midcom_db_group(); $stat = $group->create(); $this->assertTrue($stat); $this->register_object($group); $group->refresh(); $this->assertEquals('Group #' . $group->id, $group->official); $this->assertEquals('Group #' . $group->id, $group->get_label()); $group->official = 'TEST GROUP ' . __CLASS__; $stat = $group->update(); $this->assertTrue($stat); $this->assertEquals('TEST GROUP ' . __CLASS__, $group->get_label()); $stat = $group->delete(); $this->assertTrue($stat); midcom::get('auth')->drop_sudo(); }
/** * @param mixed $handler_id The ID of the handler. * @param Array $args The argument list. * @param Array &$data The local request data. */ public function _handler_edit($handler_id, array $args, array &$data) { midcom::get('auth')->require_user_do('org.openpsa.user:manage', null, 'org_openpsa_user_interface'); $this->_group = new midcom_db_group($args[0]); $data['controller'] = $this->get_controller('simple', $this->_group); switch ($data['controller']->process_form()) { case 'save': midcom::get('uimessages')->add($this->_l10n->get('org.openpsa.user'), sprintf($this->_l10n->get('group %s saved'), $this->_group->get_label())); // Fall-through // Fall-through case 'cancel': return new midcom_response_relocate('group/' . $this->_group->guid . '/'); } $this->add_breadcrumb('groups/', $this->_l10n->get('groups')); $this->add_breadcrumb('', sprintf($this->_l10n_midcom->get('edit %s'), $this->_group->get_label())); org_openpsa_helpers::dm2_savecancel($this); $this->bind_view_to_object($this->_group); }
/** * @param mixed $handler_id The ID of the handler. * @param Array $args The argument list. * @param Array &$data The local request data. */ public function _handler_privileges($handler_id, array $args, array &$data) { midcom::get('auth')->require_user_do('org.openpsa.user:manage', null, 'org_openpsa_user_interface'); // Check if we get the group $this->_group = new midcom_db_group($args[0]); $this->_group->require_do('midgard:privileges'); $data['group'] = $this->_group; $data['acl_dm'] = $this->get_controller('simple', $this->_group); switch ($data['acl_dm']->process_form()) { case 'save': // Fall-through // Fall-through case 'cancel': return new midcom_response_relocate(midcom_core_context::get()->get_key(MIDCOM_CONTEXT_ANCHORPREFIX) . "group/" . $this->_group->guid . "/"); } midcom::get('head')->set_pagetitle($this->_group->official); org_openpsa_helpers::dm2_savecancel($this); $this->add_breadcrumb("group/{$this->_group->guid}/", $this->_group->name); $this->add_breadcrumb("", $this->_l10n->get('permissions')); }
/** * @param mixed $handler_id The ID of the handler. * @param Array $args The argument list. * @param Array &$data The local request data. */ public function _handler_create($handler_id, array $args, array &$data) { midcom::get('auth')->require_user_do('org.openpsa.user:manage', null, 'org_openpsa_user_interface'); if (count($args) > 0) { // Get the organization $this->_group = new midcom_db_group($args[0]); $this->_group->require_do('midgard:create'); midcom::get('head')->set_pagetitle($this->_group->official); $this->add_breadcrumb('group/' . $this->_group->guid . '/', $this->_group->get_label()); } $data['controller'] = $this->get_controller('create'); switch ($data['controller']->process_form()) { case 'save': midcom::get('uimessages')->add($this->_l10n->get('org.openpsa.user'), sprintf($this->_l10n->get('person %s created'), $this->_person->name)); $this->_master->create_account($this->_person, $data["controller"]->formmanager); return new midcom_response_relocate('view/' . $this->_person->guid . '/'); case 'cancel': return new midcom_response_relocate(''); } $this->add_breadcrumb('', sprintf($this->_l10n->get('create person'))); org_openpsa_helpers::dm2_savecancel($this); }
function list_groups($parent) { $str = ''; $qb_groups = midcom_db_group::new_query_builder(); $qb_groups->add_constraint('owner', '=', $parent); $groups = $qb_groups->execute(); if (count($groups) > 0) { $str = "<ul>\n"; foreach ($groups as $group) { $str .= "<li>"; $str .= '<input name="add_to_groups[' . $group->guid . ']" id="group_' . $group->guid . '" type="checkbox" value="' . $group->guid . '"> <label for="group_' . $group->guid . '">' . $group->official . '</label>'; $str .= "</li>\n"; $str .= list_groups($group->id); } $str .= "</ul>"; } return $str; }
/** * @param mixed $handler_id The ID of the handler. * @param Array $args The argument list. * @param Array &$data The local request data. */ public function _handler_view($handler_id, array $args, array &$data) { midcom::get('auth')->require_user_do('org.openpsa.user:access', null, 'org_openpsa_user_interface'); $this->_group = new midcom_db_group($args[0]); $data['group'] = $this->_group; $data['view'] = midcom_helper_datamanager2_handler::get_view_controller($this, $this->_group); org_openpsa_widgets_tree::add_head_elements(); org_openpsa_widgets_grid::add_head_elements(); $this->add_breadcrumb('groups/', $this->_l10n->get('groups')); $this->add_breadcrumb('', $this->_group->get_label()); $this->_view_toolbar->add_item(array(MIDCOM_TOOLBAR_URL => "group/edit/{$this->_group->guid}/", MIDCOM_TOOLBAR_LABEL => $this->_l10n_midcom->get("edit"), MIDCOM_TOOLBAR_ICON => 'stock-icons/16x16/edit.png', MIDCOM_TOOLBAR_ENABLED => $this->_group->can_do('midgard:update'), MIDCOM_TOOLBAR_ACCESSKEY => 'e')); $this->_view_toolbar->add_item(array(MIDCOM_TOOLBAR_URL => "group/delete/{$this->_group->guid}/", MIDCOM_TOOLBAR_LABEL => $this->_l10n_midcom->get("delete"), MIDCOM_TOOLBAR_ICON => 'stock-icons/16x16/trash.png', MIDCOM_TOOLBAR_ENABLED => $this->_group->can_do('midgard:delete'))); $this->_view_toolbar->add_item(array(MIDCOM_TOOLBAR_URL => "group/privileges/{$this->_group->guid}/", MIDCOM_TOOLBAR_LABEL => $this->_l10n->get("permissions"), MIDCOM_TOOLBAR_ICON => 'midgard.admin.asgard/permissions-16.png', MIDCOM_TOOLBAR_ENABLED => $this->_group->can_do('midgard:privileges'))); $this->_view_toolbar->add_item(array(MIDCOM_TOOLBAR_URL => "group/notifications/{$this->_group->guid}/", MIDCOM_TOOLBAR_LABEL => $this->_l10n->get("notification settings"), MIDCOM_TOOLBAR_ICON => 'stock-icons/16x16/stock-discussion.png', MIDCOM_TOOLBAR_ENABLED => $this->_group->can_do('midgard:update'))); $this->_view_toolbar->add_item(array(MIDCOM_TOOLBAR_URL => "create/{$this->_group->guid}/", MIDCOM_TOOLBAR_LABEL => $this->_l10n->get('create person'), MIDCOM_TOOLBAR_ICON => 'stock-icons/16x16/stock_person-new.png', MIDCOM_TOOLBAR_ENABLED => midcom::get('auth')->can_user_do('midgard:create', null, 'org_openpsa_contacts_person_dba'))); $this->bind_view_to_object($this->_group); }
$paid = ", " . midcom::get('i18n')->get_string('paid', 'org.openpsa.invoices') . ": " . strftime('%x', $invoice->paid); } else { if ($invoice->due < time()) { $paid = ", " . midcom::get('i18n')->get_string('not paid', 'org.openpsa.invoices'); } } ?> <li class="invoice" id="org_openpsa_relatedto_line_&(link['guid']);"> <span class="icon">&(data['icon']:h);</span> <span class="title">&(data['title']:h);</span> <ul class="metadata"> <?php // Customer if ($invoice->customer) { $customer = midcom_db_group::get_cached($invoice->customer); echo "<li>" . midcom::get('i18n')->get_string('customer', 'org.openpsa.invoices') . ": {$customer->official}</li>"; } echo "<li>" . midcom::get('i18n')->get_string('sum', 'org.openpsa.invoices') . ": " . org_openpsa_helpers::format_number($invoice->sum) . " (" . midcom::get('i18n')->get_string('due', 'org.openpsa.invoices') . ": " . strftime('%x', $invoice->due) . "{$paid})</li>"; ?> </ul> <div id="org_openpsa_relatedto_details_&(invoice.guid);" class="details hidden" style="display: none;"> </div> <?php //TODO: necessary JS stuff to load details (which should in turn include the invoice's own relatedtos) via AHAH echo org_openpsa_relatedto_handler_relatedto::render_line_controls($link, $data['other_obj']); ?> </li>
/** * Internal helper to check if the requested group belongs to the haystack * * @param int $id * @param int $owner */ public static function belongs_to($id, $owner) { do { if ($id === $owner) { return true; } $mc = midcom_db_group::new_collector('id', $id); $mc->set_limit(1); $keys = $mc->get_values('owner'); // Get the first array key foreach ($keys as $key) { if ($key === 0) { return false; } $id = $key; } } while ($mc->count() > 0); return false; }
public function _on_deleted() { $this->_invalidate_person_cache(); if (!midcom::get('auth')->request_sudo('midcom')) { return; } // Create an Activity Log entry for the membership addition try { $actor = midcom_db_person::get_cached($this->uid); $target = midcom_db_group::get_cached($this->gid); } catch (midcom_error $e) { $e->log(); return; } $activity = new midcom_helper_activitystream_activity_dba(); $activity->target = $target->guid; $activity->actor = $actor->id; $activity->verb = 'http://community-equity.org/schema/1.0/leave'; if (midcom::get('auth')->is_valid_user() && $actor->guid == midcom::get('auth')->user->guid) { $activity->summary = sprintf(midcom::get('i18n')->get_string('%s left group %s', 'midcom'), $actor->name, $target->official); } else { $activity->summary = sprintf(midcom::get('i18n')->get_string('%s was removed from group %s', 'midcom'), $actor->name, $target->official); } $activity->create(); midcom::get('auth')->drop_sudo(); }
/** * Internal helper for showing the groups recursively * * @param int $id * @param array &$data * @param int $level */ private function list_groups_for_select($id, &$data, $level) { $mc = midcom_db_group::new_collector('owner', (int) $id); $mc->add_value_property('name'); $mc->add_value_property('official'); $mc->add_value_property('id'); // Set the order $mc->add_order('metadata.score', 'DESC'); $mc->add_order('official'); $mc->add_order('name'); // Get the results $mc->execute(); $keys = $mc->list_keys(); // Hide empty groups if ($mc->count() === 0) { return; } $data['parent_id'] = $id; foreach ($keys as $guid => $array) { $group['guid'] = $guid; $group['id'] = $mc->get_subkey($guid, 'id'); $group['name'] = $mc->get_subkey($guid, 'name'); if ($title = $mc->get_subkey($guid, 'official')) { $group['title'] = $title; } else { $group['title'] = $group['name']; } if (!$group['title']) { $group['title'] = "#{$group['id']}"; } $group['level'] = $level; $data['groups_for_select'][] = $group; $level++; $this->list_groups_for_select($group['id'], $data, $level); $level--; } }
/** * Generates, loads and prepares the schema database. * * The operations are done on all available schemas within the DB. */ public function load_schemadb() { $schemadb = midcom_helper_datamanager2_schema::load_database($this->_config->get('schemadb_permissions')); // Populate additional assignee selector $additional_assignees = array('' => '', 'EVERYONE' => $this->_l10n->get('EVERYONE'), 'USERS' => $this->_l10n->get('USERS'), 'ANONYMOUS' => $this->_l10n->get('ANONYMOUS')); // List groups as potential assignees $qb = midcom_db_group::new_query_builder(); $groups = $qb->execute(); foreach ($groups as $group) { $additional_assignees["group:{$group->guid}"] = $group->get_label(); } $assignees = array(); // Populate all resources having existing privileges $existing_privileges = $this->_object->get_privileges(); foreach ($existing_privileges as $privilege) { if ($privilege->is_magic_assignee()) { // This is a magic assignee $label = midcom::get('i18n')->get_string($privilege->assignee, 'midgard.admin.asgard'); } else { //Inconsistent privilige base will mess here. Let's give a chance to remove ghosts $assignee = midcom::get('auth')->get_assignee($privilege->assignee); if (is_object($assignee)) { $label = $assignee->name; } else { $label = midcom::get('i18n')->get_string('ghost assignee for ' . $privilege->assignee, 'midgard.admin.asgard'); } } $assignees[$privilege->assignee] = $label; $key = str_replace(':', '_', $privilege->assignee); if (!isset($this->_row_labels[$key])) { $this->_row_labels[$key] = $label; } // This one is already an assignee, remove from 'Add assignee' options if (array_key_exists($privilege->assignee, $additional_assignees)) { unset($additional_assignees[$privilege->assignee]); } } asort($additional_assignees); // Add the 'Add assignees' choices to schema $schemadb['privileges']->fields['add_assignee']['type_config']['options'] = $additional_assignees; $header = ''; $header_items = array(); foreach ($assignees as $assignee => $label) { foreach ($this->_privileges as $privilege) { $privilege_components = explode(':', $privilege); if ($privilege_components[0] == 'midcom' || $privilege_components[0] == 'midgard') { // This is one of the core privileges, we handle it $privilege_label = $privilege; } else { // This is a component-specific privilege, call component to localize it $privilege_label = midcom::get('i18n')->get_string("privilege {$privilege_components[1]}", $privilege_components[0]); } if (!isset($header_items[$privilege_label])) { $header_items[$privilege_label] = " <th scope=\"col\" class=\"{$privilege_components[1]}\"><span>" . str_replace(' ', "\n", midcom::get('i18n')->get_string($privilege_label, 'midgard.admin.asgard')) . "</span></th>\n"; } $schemadb['privileges']->append_field(str_replace(':', '_', $assignee) . '_' . str_replace(':', '_', str_replace('.', '_', $privilege)), array('title' => $privilege_label, 'helptext' => sprintf(midcom::get('i18n')->get_string('sets privilege %s', 'midgard.admin.asgard'), $privilege), 'storage' => null, 'type' => 'privilege', 'type_config' => array('privilege_name' => $privilege, 'assignee' => $assignee), 'widget' => 'privilegeselection')); } } $header .= " <th align=\"left\" scope=\"col\" class=\"assignee_name\"><span> </span></th>\n"; foreach ($header_items as $key => $item) { $header .= $item; } $header .= " <th scope=\"col\" class=\"row_actions\"><span> </span></th>\n"; $this->_header = $header; return $schemadb; }
<?php $link =& $data['link']; $salesproject =& $data['other_obj']; $owner_card = org_openpsa_widgets_contact::get($salesproject->owner); $customer = midcom_db_group::get_cached($salesproject->customer); ?> <li class="salesproject" id="org_openpsa_relatedto_line_&(link['guid']);"> <span class="icon">&(data['icon']:h);</span> <span class="title">&(data['title']:h);</span> <ul class="metadata"> <?php // Owner echo "<li>" . midcom::get('i18n')->get_string('owner', 'midcom') . ": "; echo $owner_card->show_inline() . "</li>"; // Customer if ($customer) { echo "<li>" . midcom::get('i18n')->get_string('customer', 'org.openpsa.sales') . ": "; echo $customer->official; echo "</li>"; } ?> </ul> <div id="org_openpsa_relatedto_details_&(salesproject.guid);" class="details hidden" style="display: none;"> </div> <?php //TODO: necessary JS stuff to load details (which should in turn include the invoice's own relatedtos) via AHAH echo org_openpsa_relatedto_handler_relatedto::render_line_controls($link, $data['other_obj']); ?>
/** * * @param mixed $handler_id The ID of the handler. * @param array &$data The local request data. */ public function _show_generator($handler_id, array &$data) { midcom_show_style('sales_report-deliverable-start'); // Quick workaround to Bergies lazy determination of whether this is user's or everyone's report... if ($this->_request_data['query_data']['resource'] == 'user:'******'auth')->user->guid) { // My report $data['handler_id'] = 'deliverable_report'; } else { // Generic report $data['handler_id'] = 'sales_report'; } /*** Copied from sales/handler/deliverable/report.php ***/ midcom_show_style('sales_report-deliverable-header'); $invoices_node = midcom_helper_misc::find_node_by_component('org.openpsa.invoices'); $sums_per_person = array(); $sums_all = array('price' => 0, 'cost' => 0, 'profit' => 0); $odd = true; foreach ($data['invoices'] as $deliverable_guid => $invoices) { if (count($invoices) == 0) { // No invoices sent in this project, skip continue; } try { $deliverable = org_openpsa_sales_salesproject_deliverable_dba::get_cached($deliverable_guid); $product = org_openpsa_products_product_dba::get_cached($deliverable->product); $salesproject = org_openpsa_sales_salesproject_dba::get_cached($deliverable->salesproject); $customer = midcom_db_group::get_cached($salesproject->customer); } catch (midcom_error $e) { continue; } if (!array_key_exists($salesproject->owner, $sums_per_person)) { $sums_per_person[$salesproject->owner] = array('price' => 0, 'cost' => 0, 'profit' => 0); } // Calculate the price and cost from invoices $invoice_price = 0; $data['invoice_string'] = ''; $invoice_cycle_numbers = array(); foreach ($invoices as $invoice) { $invoice_price += $invoice->sum; $invoice_class = $invoice->get_status(); if ($invoices_node) { $invoice_label = "<a class=\"{$invoice_class}\" href=\"{$invoices_node[MIDCOM_NAV_FULLURL]}invoice/{$invoice->guid}/\">" . $invoice->get_label() . "</a>"; } else { $invoice_label = $invoice->get_label(); } if ($product->delivery == org_openpsa_products_product_dba::DELIVERY_SUBSCRIPTION) { $invoice_cycle_numbers[] = (int) $invoice->parameter('org.openpsa.sales', 'cycle_number'); } $data['invoice_string'] .= "<li class=\"{$invoice_class}\">{$invoice_label}</li>\n"; } if ($product->delivery == org_openpsa_products_product_dba::DELIVERY_SUBSCRIPTION) { // This is a subscription, it should be shown only if it is the first invoice if (!in_array(1, $invoice_cycle_numbers)) { continue; // This will skip to next deliverable } $scheduler = new org_openpsa_invoices_scheduler($deliverable); if ($deliverable->end == 0) { // Subscription doesn't have an end date, use specified amount of months for calculation $cycles = $scheduler->calculate_cycles($this->_config->get('subscription_profit_months')); $data['calculation_basis'] = sprintf($data['l10n']->get('%s cycles in %s months'), $cycles, $this->_config->get('subscription_profit_months')); } else { $cycles = $scheduler->calculate_cycles(); $data['calculation_basis'] = sprintf($data['l10n']->get('%s cycles, %s - %s'), $cycles, strftime('%x', $deliverable->start), strftime('%x', $deliverable->end)); } $price = $deliverable->price * $cycles; $cost = $deliverable->cost * $cycles; } else { // This is a single delivery, calculate cost as percentage as it may be invoiced in pieces if ($deliverable->price) { $cost_percentage = 100 / $deliverable->price * $invoice_price; $cost = $deliverable->cost / 100 * $cost_percentage; } else { $cost_percentage = 100; $cost = $deliverable->cost; } $price = $invoice_price; $data['calculation_basis'] = sprintf($data['l10n']->get('%s%% of %s'), round($cost_percentage), $deliverable->price); } // And now just count the profit $profit = $price - $cost; $data['customer'] = $customer; $data['salesproject'] = $salesproject; $data['deliverable'] = $deliverable; $data['price'] = $price; $sums_per_person[$salesproject->owner]['price'] += $price; $sums_all['price'] += $price; $data['cost'] = $cost; $sums_per_person[$salesproject->owner]['cost'] += $cost; $sums_all['cost'] += $cost; $data['profit'] = $profit; $sums_per_person[$salesproject->owner]['profit'] += $profit; $sums_all['profit'] += $profit; if ($odd) { $data['row_class'] = ''; $odd = false; } else { $data['row_class'] = ' class="even"'; $odd = true; } midcom_show_style('sales_report-deliverable-item'); } $data['sums_per_person'] = $sums_per_person; $data['sums_all'] = $sums_all; midcom_show_style('sales_report-deliverable-footer'); /*** /Copied from sales/handler/deliverable/report.php ***/ midcom_show_style('sales_report-deliverable-end'); }
function emailimport_find_group($email) { //TODO: find possible groups based on the persons email $qb = midcom_db_group::new_query_builder(); $qb->add_constraint('email', '=', $email); $results = $qb->execute(); if (!empty($results)) { //Exact matche(s) found, return first return $results[0]; } list($user, $domain) = explode('@', $email, 2); $qb = midcom_db_group::new_query_builder(); $qb->add_constraint('email', 'LIKE', "%@{$domain}"); $results = $qb->execute(); if (empty($results)) { return false; } //PONDER: What to return in case of multiple matches ?, now we always return first return $results[0]; }
$username = str_replace('ö', 'o', $username); $username = str_replace('Ä', 'a', $username); $username = str_replace('Ö', 'o', $username); $username = str_replace('Å', 'a', $username); $username = str_replace('å', 'a', $username); $person->username = $username; // Generate a random password and activation Hash $password = '******'; $length = 8; // Create a random password for ($i = 0; $i < $length; $i++) { $password .= chr(rand(97, 122)); } $person->password = $password; $person->create(); $group = new midcom_db_group(); $group->get_by_path('/kkp_admins/editoijat'); $member = new midcom_db_member(); $member->uid = $person->id; $member->gid = $group->id; $member->create(); $mail = new org_openpsa_mail(); $mail->from = '*****@*****.**'; $mail->subject = '[KKp-web] Tunnukset sivustolle'; $mail->body = "Hei\n\nTunnus: " . $username; $mail->body .= "\nSalasana: " . str_replace('**', '', $password); $mail->to = $person->email; $mail->send(); $status = 'Tunnukset lähetetty.'; } }