<?php
// config/functions.php
// Veritabanı bağlantısını içeri alıyoruz
require_once 'db.php';
// Oturumu başlat (Eğer başlamadıysa)
if (session_status() === PHP_SESSION_NONE) {
session_start();
}
/**
* 1. GÜVENLİK FONKSİYONU (XSS Temizliği)
* Kullanıcıdan gelen her veriyi bu fonksiyonla temizleyeceğiz.
*/
function guvenlik($veri) {
$veri = trim($veri); // Baştaki sondaki boşlukları sil
$veri = stripslashes($veri); // Ters eğik çizgileri temizle
$veri = htmlspecialchars($veri); // HTML etiketlerini etkisiz hale getir (XSS koruması)
return $veri;
}
/**
* 2. DETAYLI LOGLAMA FONKSİYONU
* Sistemdeki her hareketi veritabanına yazar.
* @param int $kullanici_id İşlemi yapan kişi (Yoksa 0)
* @param string $islem_tipi giris, ekleme, silme vb.
* @param string $aciklama Okunabilir detay
* @param string $tablo Hangi tabloda işlem yapıldı?
* @param int $kayit_id Hangi kayıt üzerinde işlem yapıldı?
*/
function logKaydet($pdo, $kullanici_id, $islem_tipi, $aciklama, $tablo = null, $kayit_id = null, $eski_veri = null, $yeni_veri = null) {
$ip = $_SERVER['REMOTE_ADDR'];
$cihaz = $_SERVER['HTTP_USER_AGENT'];
// GÜVENLİK GÜNCELLEMESİ: islem_tipi ve aciklama XSS'e karşı temizlenmeli
$temiz_islem_tipi = guvenlik($islem_tipi);
$temiz_aciklama = guvenlik($aciklama);
$sql = "INSERT INTO sistem_loglari (kullanici_id, islem_tipi, aciklama, tablo_adi, kayit_id, eski_veri, yeni_veri, ip_adresi, cihaz_bilgisi)
VALUES (:kid, :tip, :acik, :tablo, :rid, :eski, :yeni, :ip, :cihaz)";
$stmt = $pdo->prepare($sql);
$stmt->execute([
':kid' => $kullanici_id,
':tip' => $temiz_islem_tipi,
':acik' => $temiz_aciklama,
':tablo' => $tablo,
':rid' => $kayit_id,
':eski' => $eski_veri ? json_encode($eski_veri, JSON_UNESCAPED_UNICODE) : null,
':yeni' => $yeni_veri ? json_encode($yeni_veri, JSON_UNESCAPED_UNICODE) : null,
':ip' => $ip,
':cihaz' => $cihaz
]);
}
/**
* 3. YETKİ KONTROLÜ
* Sayfanın başında çağrılır. Kullanıcının o sayfaya girme yetkisi var mı bakar.
* @param array $izin_verilen_roller Bu sayfayı görebilecek rollerin listesi
*/
function yetkiKontrol($izin_verilen_roller) {
// 1. Giriş yapılmış mı?
if (!isset($_SESSION['kullanici_id']) || !isset($_SESSION['rol'])) {
header("Location: ../index.php?hata=giris_yapilmali");
exit;
}
// 2. Rol yetkisi var mı?
// Eğer kullanıcının rolü, izin verilenler listesinde YOKSA at.
if (!in_array($_SESSION['rol'], $izin_verilen_roller)) {
// Log kaydı atalım: Yetkisiz erişim denemesi
global $pdo; // $pdo nesnesini globalden çek
logKaydet($pdo, $_SESSION['kullanici_id'], 'guvenlik_uyarisi', 'Yetkisiz sayfaya erişim denemesi: ' . $_SERVER['PHP_SELF']);
// Ana sayfasına yönlendir veya hata göster
header("Location: ../index.php?hata=yetkisiz_erisim");
exit;
}
}
/**
* 4. CSRF TOKEN OLUŞTURMA (Form Güvenliği)
* Formlara gizli input olarak ekleyeceğiz.
*/
function csrfTokenOlustur() {
if (empty($_SESSION['csrf_token'])) {
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
}
return $_SESSION['csrf_token'];
}
/**
* 5. CSRF KONTROL
* Form post edildiğinde çağrılır.
*/
function csrfKontrol($token) {
if (!isset($_SESSION['csrf_token']) || $token !== $_SESSION['csrf_token']) {
die("Güvenlik Uyarısı: CSRF doğrulaması başarısız. Lütfen sayfayı yenileyip tekrar deneyin.");
}
}
/**
* 6. HAFTALIK ÇALIŞMA SÜRESİ HESAPLA (Denkleştirme İçin)
* Bir personelin, verilen tarihteki haftasının toplam onaylı saatini getirir.
*/
function haftalikToplamSureGetir($pdo, $calisan_id, $referans_tarih) {
// Tarihin olduğu haftanın Pazartesi ve Pazar günlerini bul
$dt = new DateTime($referans_tarih);
// ISO-8601'e göre haftanın ilk günü (Pazartesi)
$dt->modify('this week monday');
$baslangic = $dt->format('Y-m-d');
$dt->modify('this week sunday');
$bitis = $dt->format('Y-m-d');
// O haftaki ONAYLANMIŞ mesaileri topla
$sql = "SELECT SUM(toplam_saat) as toplam
FROM mesai_hareketleri
WHERE calisan_id = ?
AND tarih BETWEEN ? AND ?
AND durum = 'onaylandi'";
$stmt = $pdo->prepare($sql);
$stmt->execute([$calisan_id, $baslangic, $bitis]);
return (float)$stmt->fetchColumn(); // Örn: 42.5
}
/**
* 7. NET İZİN GÜNÜ HESAPLA
* Pazar günlerini ve Resmi Tatilleri saymaz.
* @param PDO $pdo Veritabanı bağlantısı
* @param string $baslangic Y-m-d H:i
* @param string $bitis Y-m-d H:i
* @return float Hesaplanan net iş günü
*/
function netIzinGunSayisiHesapla($pdo, $baslangic, $bitis) {
// Saatleri sıfırla, sadece gün bazlı hesaplayalım
$start = new DateTime(substr($baslangic, 0, 10));
$end = new DateTime(substr($bitis, 0, 10));
// Bitiş günü de dahil edilmeli, o yüzden döngüde <= kullanacağız
// Ama DateTime loop'unda bitişi dahil etmek için 1 gün ileri alıp < yaparız veya interval kullanırız.
// En temizi: $end gününü de kontrol etmek.
$toplam_gun = 0;
// Resmi Tatilleri Çek (Performans için bir kere çekip array yapalım)
// Sadece bu aralıktaki tatilleri çek
$sql = "SELECT tarih FROM resmi_tatiller WHERE tarih BETWEEN ? AND ?";
$stmt = $pdo->prepare($sql);
$stmt->execute([$start->format('Y-m-d'), $end->format('Y-m-d')]);
$tatiller = $stmt->fetchAll(PDO::FETCH_COLUMN); // ['2025-01-01', '2025-04-23'] gibi döner
// Döngü: Başlangıçtan Bitişe kadar gün gün git
$current = clone $start;
while ($current <= $end) {
$curr_date = $current->format('Y-m-d');
$haftanin_gunu = $current->format('N'); // 1 (Pzt) - 7 (Paz)
// KURAL 1: Pazar günü mü? (7) -> Sayma
// KURAL 2: Resmi tatil mi? -> Sayma
if ($haftanin_gunu != 7 && !in_array($curr_date, $tatiller)) {
$toplam_gun++;
}
// Bir sonraki güne geç
$current->modify('+1 day');
}
// Eğer başlangıç ve bitiş aynı günse ve o gün tatil değilse 1 saymalı
if ($start == $end && $toplam_gun == 0) {
// Ama tatilse 0 kalmalı. Yukarıdaki döngü bunu halleder.
}
return $toplam_gun;
}
/**
* 8. TELEFON FORMATLAMA
* Girdiyi temizler ve +905XXXXXXXXX formatına çevirir.
*/
function telefonFormatla($tel) {
// Sadece rakamları al
$tel = preg_replace('/[^0-9]/', '', $tel);
// Boşsa boş dön
if (empty($tel)) return '';
// Uzunluğa göre düzenle
// 532... (10 hane) -> +90532...
if (strlen($tel) == 10) {
return '+90' . $tel;
}
// 0532... (11 hane) -> +90532...
if (strlen($tel) == 11 && substr($tel, 0, 1) == '0') {
return '+9' . $tel;
}
// 90532... (12 hane) -> +90532...
if (strlen($tel) == 12 && substr($tel, 0, 2) == '90') {
return '+' . $tel;
}
// Format uymuyorsa olduğu gibi (veya başına + ekleyerek) döndür
return '+' . $tel;
}
// config/functions.php (En alta ekleyin)
/**
* 9. HAFTALIK ONAYLI SÜRE HESAPLA
* Verilen tarihin ait olduğu haftadaki (Pzt-Paz) toplam onaylı saati döndürür.
*/
function haftalikOnayliSureGetir($pdo, $calisan_id, $referans_tarih) {
$date = new DateTime($referans_tarih);
// Haftanın günü (1:Pzt ... 7:Paz)
$gun = $date->format('N');
// Pazartesiye git
$pazartesi = clone $date;
$pazartesi->modify('-' . ($gun - 1) . ' days');
// Pazara git
$pazar = clone $pazartesi;
$pazar->modify('+6 days');
$bas = $pazartesi->format('Y-m-d');
$bit = $pazar->format('Y-m-d');
$sql = "SELECT SUM(toplam_saat) FROM mesai_hareketleri
WHERE calisan_id = ? AND durum = 'onaylandi'
AND tarih BETWEEN ? AND ?";
$stmt = $pdo->prepare($sql);
$stmt->execute([$calisan_id, $bas, $bit]);
return (float)$stmt->fetchColumn();
}
/**
* 10. TÜRKÇE TARİH FORMATLAMA
* Girdiyi alır, "18 Mayıs 2025" formatına çevirir.
* @param string $tarih (Y-m-d veya datetime)
* @param bool $sadece_ay_yil (True ise sadece 'Aralık 2025' döndürür)
*/
function tarihTurkce($tarih, $sadece_ay_yil = false) {
if (!$tarih) return '-';
$zaman = strtotime($tarih);
$aylar_en = [
'January', 'February', 'March', 'April', 'May', 'June',
'July', 'August', 'September', 'October', 'November', 'December'
];
$aylar_tr = [
'Ocak', 'Şubat', 'Mart', 'Nisan', 'Mayıs', 'Haziran',
'Temmuz', 'Ağustos', 'Eylül', 'Ekim', 'Kasım', 'Aralık'
];
// Format belirle
$format = $sadece_ay_yil ? 'F Y' : 'd F Y';
$ingilizce_tarih = date($format, $zaman);
// Değiştir ve döndür
return str_replace($aylar_en, $aylar_tr, $ingilizce_tarih);
}
/**
* 11. BİLDİRİM GÖNDER
* Belirli bir role veya kişiye bildirim atar.
*/
function bildirimGonder($pdo, $hedef_kullanici_id, $baslik, $mesaj, $link) {
$sql = "INSERT INTO bildirimler (kullanici_id, baslik, mesaj, link) VALUES (?, ?, ?, ?)";
$pdo->prepare($sql)->execute([$hedef_kullanici_id, $baslik, $mesaj, $link]);
}
/**
* 12. GRUP BİLDİRİMİ (Rol Bazlı)
* Örn: Tüm 'muhasebe' ve 'insan_kaynaklari' rollerine gönderir.
*/
function rolBildirimGonder($pdo, $roller_dizisi, $baslik, $mesaj, $link) {
// Rolleri virgülle ayırıp SQL'e sokalım ('muhasebe','insan_kaynaklari')
$in = str_repeat('?,', count($roller_dizisi) - 1) . '?';
$sql = "SELECT id FROM kullanicilar WHERE rol IN ($in) AND durum = 1";
$stmt = $pdo->prepare($sql);
$stmt->execute($roller_dizisi);
$kisiler = $stmt->fetchAll(PDO::FETCH_COLUMN);
foreach($kisiler as $k_id) {
bildirimGonder($pdo, $k_id, $baslik, $mesaj, $link);
}
}
/**
* 13. OKUNMAMIŞ BİLDİRİM SAYISI
* GÜVENLİK GÜNCELLEMESİ: SQL Injection'a karşı koruma eklendi
*/
function bildirimSayisi($pdo, $uid) {
$sql = "SELECT COUNT(*) FROM bildirimler WHERE kullanici_id = ? AND okundu = 0";
$stmt = $pdo->prepare($sql);
$stmt->execute([$uid]); // Parametre bağlama kullanıldı
return $stmt->fetchColumn();
}
/**
* 14. PERSONEL ANLIK DURUM SORGULA
* Bugünün tarihine göre personelin onaylı bir izni var mı bakar.
*/
function personelDurumu($pdo, $calisan_id) {
$bugun = date('Y-m-d');
// Bugün tarih aralığında olan ONAYLI izin var mı?
$sql = "SELECT COUNT(*) FROM izin_talepleri
WHERE calisan_id = ?
AND durum = 'onaylandi'
AND ? BETWEEN DATE(baslangic_tarihi) AND DATE(bitis_tarihi)";
$stmt = $pdo->prepare($sql);
$stmt->execute([$calisan_id, $bugun]);
$izinli_mi = $stmt->fetchColumn();
if ($izinli_mi > 0) {
return '<span class="badge bg-warning text-dark"><i class="fas fa-umbrella-beach"></i> İZİNLİ</span>';
} else {
return '<span class="badge bg-success"><i class="fas fa-briefcase"></i> MESAİDE</span>';
}
}
?>