kbsintranett/kbs-intranett.code-snippets.php.txt

1065 lines
41 KiB
Text
Raw Permalink Normal View History

2026-06-01 08:46:48 +02:00
<?php
/**
* Disable admin bar for non-admins
*
* Turns off the WordPress admin bar for everyone except administrators.
*/
add_action( 'wp', function () {
if ( ! current_user_can( 'manage_options' ) ) {
show_admin_bar( false );
}
} );
/**
* Allow smilies
*
* Allows smiley conversion in obscure places.
*
* This is a sample snippet. Feel free to use it, edit it, or remove it.
*/
add_filter( 'widget_text', 'convert_smilies' );
add_filter( 'the_title', 'convert_smilies' );
add_filter( 'wp_title', 'convert_smilies' );
add_filter( 'get_bloginfo', 'convert_smilies' );
/**
* Begrens tilgang til utvalgte poster, sider, taksonomier og arkiv.
*/
/**
* Sjekker brukerens tilgang til innhold og omdirigerer ved behov
* Plasseres i functions.php i ditt tema eller i en egendefinert plugin
*/
add_action('template_redirect', 'sjekk_tilgang_til_innhold');
function sjekk_tilgang_til_innhold() {
$current_id = get_queried_object_id();
$allowed_ids = array(4144);
// Sjekk om brukeren er innlogget
if ((!is_user_logged_in()) && (is_singular() && !in_array($current_id, $allowed_ids))) {
// Omdirigerer ikke-innloggede brukere til innloggingssiden
wp_redirect(wp_login_url(get_permalink()));
exit;
}
// For innloggede brukere, sjekk tilgangsrettigheter
$current_user = wp_get_current_user();
$user_roles = $current_user->roles;
// Sjekk om brukeren har administrator-rettigheter
if (in_array('administrator', $user_roles) || in_array('kbs_admin', $user_roles)) {
return; // Administratorer har alltid tilgang
}
$forbidden_ids = array(3232,3234, 3547,3567);
$current_id = get_queried_object_id();
$access_denied = false;
// Sjekk om det er en post, side eller egendefinert post med spesifiserte IDer
if (is_singular() && in_array($current_id, $forbidden_ids)) {
$access_denied = true;
}
// Sjekk kategorier
if (is_singular()) {
$post_categories = wp_get_post_categories($current_id, array('fields' => 'slugs'));
// Sjekk om innlegget KUN er i kategorien 'forbudt' eller KUN i kategorien 'absolutt_ikke'
if (count($post_categories) === 1 &&
(in_array('administrasjonen-i-kbs', $post_categories) ||
in_array('absolutt_ikke', $post_categories))) {
$access_denied = true;
}
// Sjekk om innlegget har BÅDE 'forbudt' OG 'absolutt_ikke'
// (blokkerende kombinasjoner)
if (in_array('administrasjonen-i-kbs', $post_categories) &&
in_array('absolutt_ikke', $post_categories)) {
$access_denied = true;
}
}
// Sjekk om vi er på en arkivside for forbudte taksonomier
if (is_category('administrasjonen-i-kbs') || is_category('absolutt_ikke')) {
$access_denied = true;
}
// Omdirigering hvis tilgang er nektet
if ($access_denied) {
echo '<div style="text-align: center; padding: 50px;">
<p>Vennligst kontakt administratoren på nettstedet for å få tilgang til dette innholdet..
Du blir nå videresendt til forsiden.</p>
</div>';
// JavaScript for omdirigering etter 3 sekunder
echo '<script>
setTimeout(function() {
window.location.href = "' . home_url() . '";
}, 3000);
</script>';
exit;
}
}
/**
* Redirect etter innlogging
*/
function envide_custom_login_redirect( $url, $request, $user ) {
if ( $user && is_object( $user ) && is_a( $user, 'WP_User' ) ) {
$url = home_url( '/' );
}
return $url;
}
add_filter( 'login_redirect', 'envide_custom_login_redirect', 10, 3 );
/**
* Farger til tilpasser og Beaver Builder
*/
//Change the Customizer color palette presets
add_action('customize_controls_print_footer_scripts', function () {
?>
<script>
jQuery(document).ready(function($){
$('.wp-picker-container').iris({
mode: 'hsl',
controls: {
horiz: 'h', // square horizontal displays hue
vert: 's', // square vertical displays saturdation
strip: 'l' // slider displays lightness
},
palettes: ['#53AFE9', '#0069B3', '#C40426', '#546a76']
})
});
</script>
<?php
});
/**
* Gravity Wiz // Gravity Forms // Dynamically Populating User Role
*/
/**
* Gravity Wiz // Gravity Forms // Dynamically Populating User Role
* https://gravitywiz.com/dynamically-populating-user-role/
*
* Use this snippet in conjunction with Gravity Forms dynamic population
* functionality to populate the current users role into any form field.
*
* @version 1.0
* @author David Smith <david@gravitywiz.com>
* @license GPL-2.0+
* @link https://gravitywiz.com/
*/
add_filter( 'gform_field_value_user_role', 'gform_populate_user_role' );
function gform_populate_user_role( $value ) {
$user = wp_get_current_user();
$role = $user->roles;
return reset( $role );
}
/**
* Gjør spesifikke Gravity Forms-felt redigerbare kun for valgt leder eller administrator.
*/
/**
* Gravity Forms: Lås felter til kun nærmeste leder (felt 3) og administrator.
* - Gjelder skjema ID 21
* - Felt som kontrolleres: 40 (liste), 41 (checkbox), 37 (textarea), 44 (dato)
* - Seksjoner 36 og 39 har ikke egne input, men vi forsøker å disable innhold under disse også via JS.
*/
add_filter( 'gform_pre_render_21', 'kbs_lock_lederfelter' );
add_filter( 'gform_pre_validation_21', 'kbs_lock_lederfelter' );
function kbs_lock_lederfelter( $form ) {
$protected = array( 40, 41, 37, 44 ); // rene input-felt
$sections = array( 36, 39 ); // seksjoner (ingen egne inputs)
// Hvem er innlogget?
$current_user_id = get_current_user_id();
$is_admin = current_user_can( 'administrator' ) || current_user_can( 'manage_options' );
// Hent valgt leder-ID fra POST (på visning/validering), evt. fra eksisterende entry hvis tilgjengelig.
$leader_id = isset( $_POST['input_3'] ) ? absint( $_POST['input_3'] ) : null;
// Hvis vi viser et lagret oppslag (f.eks. via GravityView/entry edit), prøv å hente entry-verdi for felt 3:
if ( ! $leader_id ) {
if ( class_exists( 'GFFormsModel' ) && ( $lead = GFFormsModel::get_current_lead() ) ) {
$leader_id = isset( $lead['3'] ) ? absint( $lead['3'] ) : null;
}
}
$can_edit = $is_admin || ( $leader_id && (int) $leader_id === (int) $current_user_id );
// Merk feltene som "readonly" i markup (vi setter CSS-klasse; JS skrur av inputs).
if ( ! $can_edit ) {
foreach ( $form['fields'] as &$field ) {
if ( in_array( (int) $field->id, $protected, true ) ) {
$field->cssClass .= ' kbs-readonly-field';
}
if ( in_array( (int) $field->id, $sections, true ) ) {
$field->cssClass .= ' kbs-readonly-section';
}
}
// Legg til init-script som gjør alle inputs i disse feltene readonly/disabled
add_filter( 'gform_register_init_scripts_21', function( $form ) use ( $protected, $sections ) {
$ids = implode( ',', array_map( 'intval', array_merge( $protected, $sections ) ) );
$script = "
(function() {
var form = document.getElementById('gform_wrapper_{$form['id']}') || document.getElementById('gform_{$form['id']}');
if (!form) return;
var protectIds = '{$ids}'.split(',').map(function(x){return parseInt(x,10);});
protectIds.forEach(function(fid){
var rows = form.querySelectorAll('.gfield.gfield_id_'+fid+', #field_{$form['id']}_'+fid);
rows.forEach(function(row){
// Finn alle Inputs/Textareas/Selects i raden og lås dem
row.querySelectorAll('input, textarea, select, button').forEach(function(el){
// Ikke lås submit-knapper
if (el.type === 'submit' || el.classList.contains('gform_button')) return;
// For checkbox/radio/select/listefelt, bruk disabled; for tekstfelt, bruk readonly:
if (['checkbox','radio','select-one','select-multiple','button'].includes(el.type) || el.tagName==='SELECT') {
el.setAttribute('disabled','disabled');
} else {
el.setAttribute('readonly','readonly');
}
});
// Visuelt hint
row.classList.add('kbs-locked');
});
});
})();";
GFFormDisplay::add_init_script( $form['id'], 'kbs_lock', GFFormDisplay::ON_PAGE_RENDER, $script );
return $form;
}, 10, 1 );
}
return $form;
}
/**
* Serverside vern: Hvis bruker IKKE har lov, nullstill endringer i beskyttede felt før innsending prosesseres.
*/
add_action( 'gform_pre_submission_21', function( $form ) {
$protected = array( 40, 41, 37, 44 );
$current_user_id = get_current_user_id();
$is_admin = current_user_can( 'administrator' ) || current_user_can( 'manage_options' );
$leader_id = isset( $_POST['input_3'] ) ? absint( $_POST['input_3'] ) : null;
$can_edit = $is_admin || ( $leader_id && (int) $leader_id === (int) $current_user_id );
if ( $can_edit ) {
return;
}
// Dersom dette er en oppdatering av eksisterende entry, hent opprinnelige verdier og skriv dem tilbake.
$entry = class_exists( 'GFFormsModel' ) ? GFFormsModel::get_current_lead() : null;
foreach ( $protected as $fid ) {
// List (40) kan ha flere inputs (kolonner/rader); vi fjerner alle POST-verdier for sikkerhets skyld.
foreach ( $_POST as $key => $val ) {
if ( preg_match( '/^input_' . $fid . '(\\D|$)/', $key ) ) {
unset( $_POST[ $key ] );
}
}
// Sett tilbake opprinnelig verdi hvis vi har en entry (unngå at feltene blir blanket ut ved redigering).
if ( $entry && isset( $entry[ (string) $fid ] ) ) {
$_POST[ 'input_' . $fid ] = $entry[ (string) $fid ];
}
}
}, 10, 1 );
/**
* Litt enkel styling (valgfritt): grå ut låste felter.
*/
add_action( 'wp_head', function() {
echo '<style>
.gfield.kbs-locked { opacity: .8; }
.kbs-locked input[readonly], .kbs-locked textarea[readonly] { background: #f5f5f5; cursor: not-allowed; }
.kbs-locked select:disabled, .kbs-locked input:disabled { background: #f5f5f5; cursor: not-allowed; }
</style>';
} );
/**
* KBS App API Samlet
*/
/*
Plugin Name: KBS App API Samlet
Description: Alle endepunkter for Android-appen. V16 - Fix Class Not Found.
Version: 16
Author: KBS
*/
// =============================================================================
// --- GOOGLE CLIENT (Flyttet til toppen for å hindre "Class not found") ---
// =============================================================================
if (!class_exists('SimpleGoogleClient')) {
class SimpleGoogleClient {
private $key_file;
private $key_data;
public function __construct($path) {
if (!file_exists($path)) throw new Exception("Finner ikke nøkkelfil: " . $path);
$this->key_data = json_decode(file_get_contents($path), true);
if (!$this->key_data) throw new Exception("Ugyldig JSON i nøkkelfil");
}
public function getProjectId() {
return $this->key_data['project_id'] ?? null;
}
public function getAccessToken($scopes = ['https://www.googleapis.com/auth/calendar']) {
$header = base64_encode(json_encode(['alg' => 'RS256', 'typ' => 'JWT']));
$now = time();
$claim = base64_encode(json_encode([
'iss' => $this->key_data['client_email'],
'scope' => implode(' ', $scopes),
'aud' => 'https://oauth2.googleapis.com/token',
'exp' => $now + 3600, 'iat' => $now
]));
$sig_input = str_replace(['+', '/', '='], ['-', '_', ''], $header . "." . $claim);
$pk = $this->key_data['private_key'];
$signature = '';
if (!openssl_sign($sig_input, $signature, $pk, 'SHA256')) throw new Exception("Signering feilet");
$jwt = $sig_input . "." . str_replace(['+', '/', '='], ['-', '_', ''], base64_encode($signature));
$res = wp_remote_post('https://oauth2.googleapis.com/token', ['body' => ['grant_type' => 'urn:ietf:params:oauth:grant-type:jwt-bearer', 'assertion' => $jwt]]);
if (is_wp_error($res)) throw new Exception($res->get_error_message());
$data = json_decode(wp_remote_retrieve_body($res), true);
return $data['access_token'] ?? null;
}
}
}
// =============================================================================
// --- SIKKERHET (INTRANETT HARDENING) ---
// =============================================================================
add_filter('rest_authentication_errors', function ($result) {
if (!empty($result)) return $result;
$route = untrailingslashit($GLOBALS['wp']->query_vars['rest_route'] ?? '');
if (strpos($route, '/kbs/v1/login') !== false) return $result;
if (strpos($route, '/kbs/v1/lookup-id') !== false) return $result;
if (!is_user_logged_in()) {
return new WP_Error('rest_not_logged_in', 'Intranett: Krever innlogging.', ['status' => 401]);
}
return $result;
});
add_action('pre_comment_on_post', function($comment_post_ID) {
if (!is_user_logged_in()) {
wp_die('Sikkerhetsvarsel: Du må være logget inn for å kommentere.');
}
});
// =============================================================================
// --- KONFIGURASJON ---
// =============================================================================
function kbs_get_calendar_config() {
return [
'felles' => [
'name' => 'Felles',
'id' => 'c_23c125bd7c6d0366f16fbffb0442b19bb41a5baaaa3a5e9cf8ac9f8a0de23119@group.calendar.google.com',
'color' => '#0069B3'
],
'amu_hms' => [
'name' => 'AMU/HMS/Miljø',
'id' => 'kbservice.no_o2bmp5f9f540vedveit51optfo@group.calendar.google.com',
'color' => '#2E7D32'
],
'administrasjonen' => [
'name' => 'Administrasjonen',
'id' => 'c_19e4d6b3bfeff2ca7aeb44609abf1b7d930677c72ed4197d45737e62ed5230dd@group.calendar.google.com',
'color' => '#607D8B'
],
'service' => [
'name' => 'Serviceavdelingen',
'id' => 'c_a2268eec031a87c8c2cd9abc5e5f7d8dc9655ea3c2800fe189a2e838a7b806f8@group.calendar.google.com',
'color' => '#E65100'
],
'automasjon' => [
'name' => 'Automasjonsavdelingen',
'id' => 'c_273285bf75ff112f16f0ebc8cd91b36f3d4bc6239b26c85b2e10f4f62249db3d@group.calendar.google.com',
'color' => '#1B5E20'
],
'prosjekt' => [
'name' => 'Prosjektavdelingen',
'id' => 'c_2c09ebaf1badc240f317bdd1343dea6caf17e6d9469bcee440d205a675ebfd31@group.calendar.google.com',
'color' => '#7B1FA2'
]
];
}
function kbs_get_json_path() {
$root_dir = dirname($_SERVER['DOCUMENT_ROOT']);
// SJEKK AT DETTE FILNAVNET STEMMER:
return $root_dir . '/httpd.private/intranet-glogin-452614-bec002e385d7.json';
}
// =============================================================================
// --- API ROUTES ---
// =============================================================================
add_action('rest_api_init', function () {
// Auth & Brukere
register_rest_route('kbs/v1', '/login', array('methods' => 'POST', 'callback' => 'kbs_login_endpoint', 'permission_callback' => '__return_true'));
register_rest_route('kbs/v1', '/device/register', array('methods' => 'POST', 'callback' => 'kbs_register_device_endpoint', 'permission_callback' => 'kbs_check_auth'));
register_rest_route('kbs/v1', '/users', array('methods' => 'GET', 'callback' => 'kbs_get_users_list_endpoint', 'permission_callback' => 'kbs_check_auth'));
// Kalender
register_rest_route('kbs/v1', '/calendar/events', array('methods' => 'GET', 'callback' => 'kbs_get_calendar_events_endpoint', 'permission_callback' => '__return_true'));
register_rest_route('kbs/v1', '/calendar/create', array('methods' => 'POST', 'callback' => 'kbs_create_event_endpoint', 'permission_callback' => 'kbs_check_auth'));
register_rest_route('kbs/v1', '/calendar/update', array('methods' => 'POST', 'callback' => 'kbs_update_event_endpoint', 'permission_callback' => 'kbs_check_auth'));
register_rest_route('kbs/v1', '/calendar/delete', array('methods' => 'POST', 'callback' => 'kbs_delete_event_endpoint', 'permission_callback' => 'kbs_check_auth'));
// Skjema & Håndbok
register_rest_route('kbs/v1', '/forms', array('methods' => 'GET', 'callback' => 'kbs_get_active_forms_list', 'permission_callback' => 'kbs_check_auth'));
register_rest_route('kbs/v1', '/forms/(?P<id>\d+)', array('methods' => 'GET', 'callback' => 'kbs_get_single_form', 'permission_callback' => 'kbs_check_auth'));
register_rest_route('kbs/v1', '/handbook', array('methods' => 'GET', 'callback' => 'kbs_get_handbook_items_from_app_page', 'permission_callback' => 'kbs_check_auth'));
register_rest_route('kbs/v1', '/handbook/(?P<id>\d+)', array('methods' => 'GET', 'callback' => 'kbs_get_handbook_page_content', 'permission_callback' => 'kbs_check_auth'));
register_rest_route('kbs/v1', '/lookup-id', array('methods' => 'GET', 'callback' => 'kbs_lookup_url_to_id', 'permission_callback' => '__return_true'));
// Oppgaver (Tasks)
register_rest_route('kbs/v1', '/tasks', array('methods' => 'GET', 'callback' => 'kbs_get_tasks', 'permission_callback' => 'kbs_check_auth'));
register_rest_route('kbs/v1', '/tasks/sync', array('methods' => 'POST', 'callback' => 'kbs_sync_tasks', 'permission_callback' => 'kbs_check_auth'));
});
// =============================================================================
// --- HJELPEFUNKSJONER ---
// =============================================================================
function kbs_check_auth($request) {
if (is_user_logged_in()) return true;
$cookie = $request->get_header('cookie');
if ($cookie && defined('LOGGED_IN_COOKIE')) {
$cookies = [];
foreach (explode(';', $cookie) as $pair) {
$parts = explode('=', trim($pair), 2);
if (count($parts) == 2) $cookies[$parts[0]] = $parts[1];
}
if (isset($cookies[LOGGED_IN_COOKIE])) {
$uid = wp_validate_auth_cookie($cookies[LOGGED_IN_COOKIE], 'logged_in');
if ($uid) { wp_set_current_user($uid); return true; }
}
}
return false;
}
function kbs_user_can_access_calendar($slug, $mode = 'read') {
$user = wp_get_current_user();
$roles = (array) $user->roles;
if (in_array('administrator', $roles) || in_array('editor', $roles)) return true;
switch ($slug) {
case 'felles': return true;
case 'amu_hms': return in_array('amuhmsmiljogruppa', $roles);
case 'administrasjonen': return in_array('administrasjonen', $roles);
case 'service': return in_array('serviceavdelingen', $roles);
case 'automasjon': return in_array('automasjonsavdelingen', $roles);
case 'prosjekt': return in_array('prosjektavdelingen', $roles);
default: return false;
}
}
function kbs_get_writeable_calendars_for_user() {
$out = [];
foreach (kbs_get_calendar_config() as $slug => $conf) {
if (kbs_user_can_access_calendar($slug, 'write')) {
$out[] = $conf['name'];
}
}
return $out;
}
// =============================================================================
// --- PUSH VARSLING (FCM) ---
// =============================================================================
function kbs_get_fcm_tokens_for_calendar($slug) {
$args = ['meta_key' => 'android_fcm_token'];
$users = get_users($args);
$tokens = [];
$current_user_id = get_current_user_id();
foreach ($users as $user) {
if ($user->ID == $current_user_id) continue;
$original_user = wp_get_current_user();
wp_set_current_user($user->ID);
$has_access = kbs_user_can_access_calendar($slug, 'read');
wp_set_current_user($original_user->ID);
if ($has_access) {
$token = get_user_meta($user->ID, 'android_fcm_token', true);
if ($token) $tokens[] = $token;
}
}
return array_unique($tokens);
}
function kbs_get_all_fcm_tokens() {
$args = ['meta_key' => 'android_fcm_token'];
$users = get_users($args);
$tokens = [];
foreach ($users as $user) {
$t = get_user_meta($user->ID, 'android_fcm_token', true);
if ($t) $tokens[] = $t;
}
return array_unique($tokens);
}
function kbs_send_fcm_broadcast($title, $body, $data_payload = [], $tokens = null) {
if ($tokens === null) $tokens = kbs_get_all_fcm_tokens();
if (empty($tokens)) return;
try {
$client = new SimpleGoogleClient(kbs_get_json_path());
$access_token = $client->getAccessToken(['https://www.googleapis.com/auth/calendar', 'https://www.googleapis.com/auth/firebase.messaging']);
$project_id = $client->getProjectId();
if (!$project_id) { error_log("KBS Push: Fant ikke project_id"); return; }
$url = "https://fcm.googleapis.com/v1/projects/{$project_id}/messages:send";
foreach ($tokens as $token) {
$message = ['token' => $token];
if ($title && $body) {
$message['notification'] = ['title' => $title, 'body' => $body];
}
if (!empty($data_payload)) {
$message['data'] = $data_payload;
}
$payload = ['message' => $message];
wp_remote_post($url, [
'headers' => ['Authorization' => 'Bearer ' . $access_token, 'Content-Type' => 'application/json'],
'body' => json_encode($payload)
]);
}
} catch (Exception $e) {
error_log("KBS Push Broadcast Error: " . $e->getMessage());
}
}
function kbs_send_fcm_notification($title, $body, $tokens) {
kbs_send_fcm_broadcast($title, $body, ['force_refresh' => 'true', 'type' => 'calendar_update'], $tokens);
}
add_action('publish_post', 'kbs_notify_new_news', 10, 2);
function kbs_notify_new_news($ID, $post) {
if ($post->post_type !== 'post') return;
kbs_send_fcm_broadcast(
"Nyhet: " . $post->post_title,
"Les siste nytt på Intranettet nå.",
['type' => 'news_update', 'force_refresh' => 'true']
);
}
// =============================================================================
// --- ENDEPUNKTER ---
// =============================================================================
function kbs_login_endpoint($request) {
$params = $request->get_json_params();
$token = $params['token'] ?? '';
if (!$token) return new WP_Error('no_token', 'Mangler token', ['status'=>400]);
$email = '';
// --- GOOGLE REVIEW BYPASS START ---
// Hvis appen sender denne spesifikke koden, logger vi inn testbrukeren direkte
if ($token === 'KBS_REVIEW_BYPASS_2026_SECRET') {
$email = 'google-test@kbs.no'; // E-posten du lagde i trinn 1
} else {
// Standard Google-verifisering for vanlige brukere
$res = wp_remote_get('https://oauth2.googleapis.com/tokeninfo?id_token=' . $token);
if (is_wp_error($res)) return new WP_Error('google_error', 'Google feil', ['status'=>500]);
$body = json_decode(wp_remote_retrieve_body($res), true);
if (($body['email_verified'] ?? '') !== 'true') return new WP_Error('invalid_token', 'Ugyldig token', ['status'=>401]);
$email = $body['email'];
}
// --- GOOGLE REVIEW BYPASS END ---
$user = get_user_by('email', $email);
if (!$user) return new WP_Error('user_not_found', 'Ingen bruker funnet: ' . $email, ['status'=>401]);
wp_set_current_user($user->ID);
$cookie = wp_generate_auth_cookie($user->ID, time()+1209600, 'logged_in');
return new WP_REST_Response([
'success' => true,
'full_cookie' => LOGGED_IN_COOKIE . '=' . $cookie,
'role' => reset($user->roles),
'user_id' => $user->ID,
'first_name' => $user->first_name,
'last_name' => $user->last_name,
'stilling' => get_user_meta($user->ID, 'stilling', true),
'mobiltelefon' => get_user_meta($user->ID, 'mobiltelefon', true),
'writeable_calendars' => kbs_get_writeable_calendars_for_user()
], 200);
}
function kbs_register_device_endpoint($request) {
if (!is_user_logged_in()) kbs_check_auth($request);
$user_id = get_current_user_id();
if (!$user_id) return new WP_Error('not_logged_in', 'Bruker ikke logget inn', ['status' => 401]);
$params = $request->get_json_params();
$token = $params['fcm_token'] ?? '';
$platform = $params['platform'] ?? 'android';
if (empty($token)) return new WP_Error('missing_token', 'Mangler FCM token', ['status' => 400]);
update_user_meta($user_id, 'android_fcm_token', sanitize_text_field($token));
update_user_meta($user_id, 'last_device_platform', sanitize_text_field($platform));
return new WP_REST_Response(['success' => true, 'message' => 'Enhet registrert'], 200);
}
function kbs_get_users_list_endpoint($request) {
$users = get_users(['orderby' => 'display_name']);
$list = [];
foreach ($users as $user) {
$list[] = [
'id' => $user->ID,
'name' => $user->display_name,
'email' => $user->user_email,
'roles' => $user->roles
];
}
return $list;
}
function kbs_get_calendar_events_endpoint($request) {
if (!kbs_check_auth($request)) return [];
$config = kbs_get_calendar_config();
$all_events = [];
$client = null;
$token = null;
$current_user = wp_get_current_user();
$is_super_admin = in_array('administrator', (array)$current_user->roles);
foreach ($config as $slug => $conf) {
if (!kbs_user_can_access_calendar($slug, 'read')) continue;
$cal_id = $conf['id'];
$cal_display_name = $conf['name'];
$cal_color = $conf['color'] ?? '#0069B3';
$cache_key = 'kbs_cal_' . md5($cal_id);
$cached_events = get_transient($cache_key);
if ($cached_events === false) {
if ($client === null) {
try {
$client = new SimpleGoogleClient(kbs_get_json_path());
$token = $client->getAccessToken(['https://www.googleapis.com/auth/calendar']);
} catch (Exception $e) {
error_log('[KBS API] Auth feil: ' . $e->getMessage());
return [];
}
}
$url = "https://www.googleapis.com/calendar/v3/calendars/" . urlencode($cal_id) . "/events?" . http_build_query([
'singleEvents' => 'true',
'orderBy' => 'startTime',
'timeMin' => date('c', strtotime('-3 month')),
'maxResults' => 150
]);
$res = wp_remote_get($url, ['headers' => ['Authorization' => 'Bearer ' . $token]]);
if (is_wp_error($res) || wp_remote_retrieve_response_code($res) >= 400) {
error_log("[KBS API] Feil ved henting av kalender $slug: " . wp_remote_retrieve_response_code($res));
continue;
}
$data = json_decode(wp_remote_retrieve_body($res), true);
$parsed_items = [];
if (isset($data['items'])) {
foreach ($data['items'] as $item) {
$desc = $item['description'] ?? '';
$reminders = [];
if (preg_match('/#varsel:([\d,]+)/i', $desc, $m)) {
foreach(explode(',', $m[1]) as $p) $reminders[] = intval($p);
} else {
$reminders[] = 15;
}
$parsed_items[] = [
'id' => $item['id'],
'title' => $item['summary'] ?? '(Uten tittel)',
'start_date' => $item['start']['dateTime'] ?? $item['start']['date'] ?? '',
'end_date' => $item['end']['dateTime'] ?? $item['end']['date'] ?? '',
'description' => $desc,
'location' => $item['location'] ?? '',
'reminders' => $reminders,
'calendar_name' => $cal_display_name,
'calendar_color' => $cal_color,
'calendar_slug' => $slug
];
}
}
set_transient($cache_key, $parsed_items, 900);
$cached_events = $parsed_items;
}
foreach ($cached_events as $event) {
$desc = $event['description'];
$is_private = false;
$participants = [];
if (preg_match('/#deltakere:([^\s]+)/', $desc, $matches)) {
$is_private = true;
$participants = explode(',', strtolower($matches[1]));
}
if (!$is_private || $is_super_admin || in_array(strtolower($current_user->user_email), $participants)) {
$all_events[] = $event;
}
}
}
return $all_events;
}
function kbs_handle_event_save($params, $is_update) {
$cal_input = $params['calendar_type'] ?? 'felles';
$config = kbs_get_calendar_config();
$target_slug = '';
if (isset($config[$cal_input])) {
$target_slug = $cal_input;
} else {
foreach ($config as $slug => $conf) {
if ($conf['name'] === $cal_input) {
$target_slug = $slug;
break;
}
}
}
if (!$target_slug || !kbs_user_can_access_calendar($target_slug, 'write')) {
return new WP_Error('forbidden', 'Ingen tilgang', ['status'=>403]);
}
$cal_id = $config[$target_slug]['id'];
$title = sanitize_text_field($params['title']);
$desc = sanitize_textarea_field($params['description'] ?? '');
$loc = sanitize_text_field($params['location'] ?? '');
$reminders = $params['reminders'] ?? [];
if (!empty($reminders)) {
$desc = preg_replace('/#varsel:[\d,]+/', '', $desc);
$desc = trim($desc) . "\n\n#varsel:" . implode(',', $reminders);
}
$event_data = ['summary' => $title, 'description' => $desc, 'location' => $loc];
if (!empty($params['is_all_day'])) {
$d_end = new DateTime($params['end_time']);
if ($params['start_time'] == $params['end_time']) $d_end->modify('+1 day');
$event_data['start'] = ['date' => $params['start_time']];
$event_data['end'] = ['date' => $d_end->format('Y-m-d')];
} else {
$event_data['start'] = ['dateTime' => $params['start_time'] . ':00', 'timeZone' => 'Europe/Oslo'];
$event_data['end'] = ['dateTime' => $params['end_time'] . ':00', 'timeZone' => 'Europe/Oslo'];
}
if (!empty($params['recurrence']) && $params['recurrence'] !== 'NONE') {
$event_data['recurrence'] = [$params['recurrence']];
}
try {
$client = new SimpleGoogleClient(kbs_get_json_path());
$token = $client->getAccessToken(['https://www.googleapis.com/auth/calendar']);
$url = "https://www.googleapis.com/calendar/v3/calendars/" . urlencode($cal_id) . "/events";
$method = 'POST';
if ($is_update && !empty($params['id'])) {
$url .= "/" . $params['id'];
$method = 'PUT';
}
$res = wp_remote_request($url, [
'method' => $method,
'headers' => ['Authorization' => 'Bearer ' . $token, 'Content-Type' => 'application/json'],
'body' => json_encode($event_data)
]);
if (is_wp_error($res) || wp_remote_retrieve_response_code($res) >= 300) throw new Exception("Google feil");
delete_transient('kbs_cal_' . md5($cal_id));
$tokens = kbs_get_fcm_tokens_for_calendar($target_slug);
$action_text = $is_update ? "endret" : "opprettet";
kbs_send_fcm_notification(
"Kalender oppdatert",
"Hendelse i {$config[$target_slug]['name']} ble {$action_text}: {$title}",
$tokens
);
return new WP_REST_Response(['success' => true], 200);
} catch (Exception $e) { return new WP_Error('google_error', $e->getMessage(), ['status'=>500]); }
}
function kbs_create_event_endpoint($request) { return kbs_handle_event_save($request->get_json_params(), false); }
function kbs_update_event_endpoint($request) { return kbs_handle_event_save($request->get_json_params(), true); }
function kbs_delete_event_endpoint($request) {
$params = $request->get_json_params();
$cal_input = $params['calendar_type'] ?? 'felles';
$config = kbs_get_calendar_config();
$target_slug = '';
if (isset($config[$cal_input])) {
$target_slug = $cal_input;
} else {
foreach ($config as $slug => $conf) {
if ($conf['name'] === $cal_input) {
$target_slug = $slug;
break;
}
}
}
if (!$target_slug || !kbs_user_can_access_calendar($target_slug, 'write')) {
return new WP_Error('forbidden', 'Ingen tilgang', ['status'=>403]);
}
try {
$cal_id = $config[$target_slug]['id'];
$client = new SimpleGoogleClient(kbs_get_json_path());
$token = $client->getAccessToken(['https://www.googleapis.com/auth/calendar']);
$res = wp_remote_request("https://www.googleapis.com/calendar/v3/calendars/" . urlencode($cal_id) . "/events/" . $params['id'], [
'method' => 'DELETE', 'headers' => ['Authorization' => 'Bearer ' . $token]
]);
if (is_wp_error($res) || wp_remote_retrieve_response_code($res) >= 300) throw new Exception("Sletting feilet");
delete_transient('kbs_cal_' . md5($cal_id));
return new WP_REST_Response(['success' => true], 200);
} catch (Exception $e) { return new WP_Error('error', $e->getMessage(), ['status'=>500]); }
}
// --- SKJEMA/HÅNDBOK ---
function kbs_get_active_forms_list() { if (!class_exists('GFAPI')) return []; $forms = GFAPI::get_forms(); $out = []; foreach ($forms as $f) if (($f['is_active'] ?? false) && !($f['is_trash'] ?? false)) $out[] = array('id' => (int)$f['id'], 'title' => $f['title'], 'description' => $f['description'] ?? '', 'is_active' => true); return array_values($out); }
function kbs_get_single_form($data) { if (!class_exists('GFAPI')) return new WP_Error('gf_missing', 'Mangler GF', array('status' => 500)); return GFAPI::get_form($data['id']) ?: new WP_Error('404', 'Ikke funnet', array('status' => 404)); }
function kbs_get_handbook_items_from_app_page() {
$p = get_page_by_path('app/interninstruks-hms');
if(!$p) return [];
$c = get_children(['post_parent'=>$p->ID,'post_type'=>'page','post_status'=>'publish']);
$o=[];
foreach($c as $i) $o[]=['id'=>$i->ID,'title'=>$i->post_title,'desc'=>$i->post_excerpt?:'Les mer...','icon_type'=>'doc','url'=>get_permalink($i->ID),'menu_order'=>$i->menu_order];
usort($o, function($a, $b) { return strnatcasecmp($a['title'], $b['title']); });
return array_values($o);
}
function kbs_get_handbook_page_content($d) { $p=get_post($d['id']); return $p?['id'=>$p->ID,'title'=>$p->post_title,'content'=>apply_filters('the_content',$p->post_content)]:[]; }
function kbs_lookup_url_to_id($r) { $u=$r->get_param('url'); $id=url_to_postid($u); if($id)return['id'=>$id]; $s=basename(parse_url($u,PHP_URL_PATH)); global $wpdb; $id=$wpdb->get_var($wpdb->prepare("SELECT ID FROM $wpdb->posts WHERE post_name=%s AND post_status='publish' LIMIT 1",$s)); return['id'=>(int)$id]; }
// =============================================================================
// --- OPPGAVER (TASKS) LOGIKK ---
// =============================================================================
/**
* Hjelpefunksjon: Henter FCM-token for en spesifikk e-postadresse.
*/
function kbs_get_fcm_token_by_email($email) {
$user = get_user_by('email', $email);
if ($user) {
return get_user_meta($user->ID, 'android_fcm_token', true);
}
return false;
}
/**
* Endepunkt: Henter alle globale oppgaver.
*/
function kbs_get_tasks() {
$tasks_json = get_option('kbs_global_tasks', '[]');
$decoded = json_decode($tasks_json);
if ($decoded === null) $decoded = json_decode(stripslashes($tasks_json));
if ($decoded === null) $decoded = [];
return new WP_REST_Response($decoded, 200);
}
/**
* Endepunkt: Synkroniserer oppgaver og sender push-varsler ved endringer.
*/
function kbs_sync_tasks($request) {
$new_tasks = $request->get_json_params();
if (!is_array($new_tasks)) {
return new WP_Error('invalid_data', 'Data må være en array', array('status' => 400));
}
// 1. Hent gamle data for sammenligning
$old_tasks_json = get_option('kbs_global_tasks', '[]');
$old_tasks = json_decode($old_tasks_json, true) ?: [];
// 2. Sikkerhet: Ikke slett alt hvis det skjer en feil i appen
if (empty($new_tasks) && count($old_tasks) > 5) {
return new WP_REST_Response(['success' => false, 'message' => 'Ignorerte tømming'], 200);
}
// 3. Lagre nye data
update_option('kbs_global_tasks', json_encode($new_tasks, JSON_UNESCAPED_UNICODE));
// 4. Varsle om NYE oppgaver
$old_ids = array_column($old_tasks, 'id');
$current_user = wp_get_current_user();
foreach ($new_tasks as $task) {
// Sjekk om dette er en helt ny ID
if (!in_array($task['id'], $old_ids)) {
$title = $task['title'];
$creator = $task['createdByName'] ?? 'En kollega';
$assignees = $task['assigneeStatus'] ?? [];
foreach ($assignees as $email => $is_done) {
// Ikke varsle meg selv
if (strtolower($email) === strtolower($current_user->user_email)) continue;
$token = kbs_get_fcm_token_by_email($email);
if ($token) {
kbs_send_fcm_broadcast(
"Ny oppgave: $title",
"Tildelt av $creator",
['type' => 'tasks_update', 'force_refresh' => 'true'],
[$token]
);
}
}
}
}
// 5. Trigger bakgrunnsoppdatering (Silent update) for alle andre enheter
kbs_send_fcm_broadcast(
null,
null,
['type' => 'tasks_update', 'force_refresh' => 'true']
);
return new WP_REST_Response(array('success' => true), 200);
}
/**
* Snippet for lightbox utenfor Beaver Builder
*/
add_action('wp_footer', 'kbs_clean_lightbox_fix', 9999);
function kbs_clean_lightbox_fix() {
?>
<script>
(function($) {
$(window).on('load', function() {
// Finn bilder i innlegg som IKKE er i Beaver Builder
$('.fl-post-content img').each(function() {
if ($(this).closest('.fl-builder-content').length) return;
var $img = $(this), $link = $img.parent('a');
// Hvis bildet mangler lenke: Lag lenke til fullstørrelse
if (!$link.length) {
var fullSrc = $img.attr('src').replace(/-\d+x\d+(?=\.[a-zA-Z]+$)/, '');
$img.wrap('<a href="' + fullSrc + '" class="kbs-lb"></a>');
}
// Hvis bildet allerede har lenke til fil: Legg til klasse
else if ($link.attr('href').match(/\.(jpg|jpeg|png|gif|webp)$/i)) {
$link.addClass('kbs-lb');
}
});
// Aktiver Magnific Popup hvis tilgjengelig
if ($.fn.magnificPopup) {
$('.kbs-lb').magnificPopup({
type: 'image',
mainClass: 'mfp-with-zoom',
image: { titleSrc: function(item) { return item.el.find('img').attr('alt'); } },
zoom: { enabled: true, duration: 300 }
});
}
});
})(jQuery);
</script>
<?php
}
/**
* Ekstra roller
*/
function opprett_identifikator_roller() {
// Legg til rollen 'administrasjonen' uten noen rettigheter (tom array)
add_role(
'administrasjonen', // Intern ID (slug)
'Administrasjonen', // Navn som vises i admin
array() // Array for rettigheter - la denne være tom!
);
// Legg til rollen 'serviceavdelingen'
add_role(
'serviceavdelingen',
'Serviceavdelingen',
array()
);
// Legg til rollen 'automasjonsavdelingen'
add_role(
'automasjonsavdelingen',
'Automasjonsavdelingen',
array()
);
// Legg til rollen 'prosjektavdelingen'
add_role(
'prosjektavdelingen',
'Prosjektavdelingen',
array()
);
// Legg til rollen 'AMU_HMS_Miljø'
add_role(
'amuhmsmiljogruppa',
'AMU, HMS og Miljø',
array()
);
}
add_action('init', 'opprett_identifikator_roller');
/**
* Steng av XML-RPC
*/
// Deaktiver XML-RPC fullstendig
add_filter('xmlrpc_enabled', '__return_false');
// Fjern XML-RPC headeren fra HTTP responsen
add_filter('wp_headers', function($headers) {
unset($headers['X-Pingback']);
return $headers;
});
/**
* Intranet Performance
*/
// 1. Begrens WordPress Heartbeat (Ajax-kall)
// Endrer frekvensen fra 15s til 60s for å spare serveren.
add_filter( 'heartbeat_settings', function( $settings ) {
$settings['interval'] = 60;
return $settings;
});
// 2. Deaktiver tunge bakgrunnsprosesser i Gravity Forms hvis mulig
// (Kun hvis du opplever at skjemaer henger ved innsending)
// add_filter( 'gform_enable_background_processing', '__return_false' );