# CalDav Calendar Viewer

[![Pipeline](https://gitlab.com/kayman-mk/caldav-calendar-viewer/badges/main/pipeline.svg)](https://gitlab.com/kayman-mk/caldav-calendar-viewer/-/pipelines)
[![Latest Release](https://gitlab.com/kayman-mk/caldav-calendar-viewer/-/badges/release.svg)](https://gitlab.com/kayman-mk/caldav-calendar-viewer/-/releases)
[![PHP](https://img.shields.io/badge/PHP-8.0--8.3-777BB4?logo=php&logoColor=white)](https://www.php.net/)
[![WordPress](https://img.shields.io/badge/WordPress-5.6%2B-21759B?logo=wordpress&logoColor=white)](https://wordpress.org/)
[![License](https://img.shields.io/badge/license-GPL--2.0--or--later-blue)](https://www.gnu.org/licenses/gpl-2.0.html)

A WordPress plugin that displays events from iCal (.ics) feeds in a clean 7-day event list.
Supports multiple feeds with configurable username and password per feed.

## Features

- **Multiple Calendar Feeds** – Configure as many iCal feeds as you need, each with a unique ID.
- **iCal Feed Integration** – Fetches and parses any standard `.ics` calendar feed (RFC 5545).
- **Recurring Events** – Expands RRULE recurrences (DAILY, WEEKLY, MONTHLY, YEARLY) including
  BYDAY, BYMONTHDAY, INTERVAL, COUNT, UNTIL, and EXDATE.
- **DURATION Support** – Derives end time from `DURATION` when `DTEND` is absent.
- **Basic Authentication** – Supports username/password per feed for protected calendar endpoints.
- **Encrypted Credentials** – Passwords are stored encrypted (AES-256-CBC) in the database.
- **Caching** – Configurable cache lifetime to reduce external requests (defaults to 1 hour).
- **Cache Management** – View the number of cached feed entries and clear them with one click
  from the admin settings page.
- **Shortcode** – Display any feed anywhere via `[caldcavi_calendar id="my-feed"]`.
- **Async Loading** – Calendar events are loaded via AJAX, so the page renders immediately and
  events appear without a full-page reload.
- **Label Filtering** – Optionally filter events by one or more iCal `CATEGORIES` labels using
  the `label` attribute. When multiple labels are given, **all** must be present on the event.
  Prefix a label with `!` to **exclude** events that carry that category.
- **Clickable Events** – Events with a `URL` property in the iCal feed are rendered as links.
- **7-Day Window** – Always fetches and displays only the next 7 days of events.
- **Responsive Design** – Clean event list adapts to mobile screens.
- **Tooltips** – Hover over events to see their description.
- **Description Prefix Filtering** – Automatically remove lines from event descriptions that start
  with configurable prefixes (e.g. to strip internal metadata added by calendar apps).

## Installation

1. Download or clone this repository into your WordPress `wp-content/plugins/` directory:

   ```bash
   cd wp-content/plugins/
   git clone <repository-url> caldav-calendar-viewer
   ```

2. Activate the plugin via **Plugins → Installed Plugins** in the WordPress admin.
3. Navigate to **Settings → CalDav Calendar Viewer** to configure your feeds.

## Configuration

Go to **Settings → CalDav Calendar Viewer** in the WordPress admin panel.

### Adding a Feed

Click **+ Add Feed** and fill in:

| Field             | Description                                                                |
|-------------------|----------------------------------------------------------------------------|
| **Feed ID**       | Unique identifier used in the shortcode (lowercase, hyphens, underscores). |
| **iCal Feed URL** | Full URL to the `.ics` calendar feed.                                      |
| **Username**      | Username for Basic Auth (leave blank for public feeds).                    |
| **Password**      | Password for Basic Auth (stored encrypted, leave blank if unused).         |

You can add multiple feeds — each one gets its own ID.

### General Settings

| Setting                                  | Description                                                                                                                                                                                                |
|------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| **Cache Lifetime**                       | How long fetched data is cached in seconds (0 disables caching).                                                                                                                                           |
| **Description Line Prefixes to Remove**  | Newline-separated list of prefixes. Any line in an event's description that starts with one of these prefixes is stripped from the hover tooltip. Useful to hide internal metadata added by calendar apps. |

**Example prefixes** (one per line):

```text
X-APPLE-
Reminder:
```

With these configured, a description line like `X-APPLE-STRUCTURED-LOCATION;...` or
`Reminder: 15 minutes before` will be removed from the tooltip automatically.

### Cache Management

The settings page shows the number of calendar feeds currently cached. Use the **Clear Cache**
button to invalidate all cached responses and force fresh fetches on the next page load.

## Usage

### Basic Shortcode

Reference a configured feed by its ID:

```text
[caldcavi_calendar id="my-feed"]
```

### Shortcode Attributes

| Attribute | Required | Default | Description                                                                                                                                                                          |
|-----------|----------|---------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `id`      | **yes**  | —       | The feed ID configured in Settings → CalDav Calendar Viewer.                                                                                                                         |
| `label`   | no       | _(all)_ | Comma-separated list of iCal `CATEGORIES` values to filter by. **All** plain labels must be present; labels prefixed with `!` must **not** be present. Matching is case-insensitive. |

**Examples:**

```text
[caldcavi_calendar id="team-calendar"]
[caldcavi_calendar id="hr-events"]
[caldcavi_calendar id="team-calendar" label="Work"]
[caldcavi_calendar id="team-calendar" label="Work,Important"]
[caldcavi_calendar id="team-calendar" label="Work,!Cancelled"]
[caldcavi_calendar id="team-calendar" label="!Private"]
```

## Testing

Run the test suite locally:

```bash
composer install
vendor/bin/phpunit --testdox
```

Tests run without a WordPress installation — the bootstrap file provides lightweight stubs for all
required WP functions.

## Linting

The project uses **PHP_CodeSniffer** with the **WordPress Coding Standards** for PHP
and **markdownlint** for Markdown.

```bash
# PHP – check style
composer run lint

# PHP – auto-fix style violations
composer run lint:fix

# Markdown
npm run lint
```

### Pre-commit hook

A Git pre-commit hook (`.githooks/pre-commit`) automatically runs
**phpcbf** (auto-fix) and **phpcs** (verify) on every staged PHP file before a commit
is accepted. It enforces the WordPress Coding Standards and blocks commits that still
contain unfixable violations.

**One-time setup:**

```bash
composer install   # installs dependencies AND registers the hooks path automatically
```

`composer install` triggers the `post-install-cmd` script which runs
`git config core.hooksPath .githooks`, so the hook is active immediately.

Alternatively, register the hook manually:

```bash
composer run setup:hooks
```

**Run manually against all files:**

```bash
composer run lint:fix   # auto-fix with phpcbf
composer run lint       # report remaining issues with phpcs
```

### Releases

Push a version tag to automatically build and publish a release `.zip` on GitHub:

```bash
git tag v1.0.0
git push origin v1.0.0
```

The release workflow runs tests first, then packages only the runtime files (no tests, dev config,
or vendor directory) into `caldav-calendar-viewer-1.0.0.zip` and attaches it to a GitHub Release.

Users can download the `.zip` from the [Releases page](../../releases) and install via
**Plugins → Add New → Upload Plugin**.

### WordPress.org Deploy

When a version tag is pushed, the deployment workflow also publishes the plugin to the WordPress.org
SVN repository.

**Setup (one-time):**

1. Register at [WordPress.org](https://login.wordpress.org/register) and
   [submit the plugin for review](https://wordpress.org/plugins/developers/add/).
2. Once approved, add these **repository secrets** in GitHub under
   **Settings → Secrets and variables → Actions**:
   - `WORDPRESS_ORG_USERNAME` – your WordPress.org SVN username
   - `WORDPRESS_ORG_PASSWORD` – your WordPress.org SVN password
3. Place plugin directory images (banner, icon) in the `.wordpress-org/` folder.

The workflow automatically syncs `readme.txt` (with the correct Stable tag), all runtime files,
and `.wordpress-org/` assets to SVN.

## Requirements

- WordPress 5.6 or later
- PHP 8.0 or later
- OpenSSL PHP extension (recommended for password encryption; falls back to Base64)

## License

GPL-2.0-or-later
