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

1064 lines
41 KiB
Text
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?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' );