# dispelled.ca — PHP Edition

A personal tech news aggregator and dashboard. Plain PHP 8, zero JavaScript
(except the TradingView stocks widget). No framework, no build step.

## Requirements

| Requirement | Notes |
|---|---|
| PHP 8.0 or newer | Namecheap Stellar includes PHP 8.x |
| `allow_url_fopen = On` | Required for RSS/API fetching — on by default on Stellar |
| Writable `cache/` directory | See setup step 3 below |
| Apache with mod_rewrite | Required for clean URLs — on by default on Stellar |

---

## Deploying to Namecheap Stellar

### 1. Upload the files

Upload the **contents** of this `dispelled-ca/` folder (not the folder itself)
to your hosting root, usually `public_html/`.

You can use:
- **cPanel File Manager** — Upload the ZIP, then Extract it inside `public_html/`
  (extract to the current folder, then move files up one level out of `dispelled-ca/`)
- **FTP/SFTP** (FileZilla, Cyberduck, etc.) — upload the folder's contents directly

Your final structure should look like:

```
public_html/
├── .htaccess
├── index.php
├── about.php
├── community.php
├── weather.php
├── ...
├── cache/           ← must exist and be writable
├── css/
├── img/
└── includes/
```

### 2. Create the cache directory

If it doesn't exist after uploading, create a `cache/` folder in your
`public_html/` root. The site stores RSS, weather, GitHub, and Reddit data
there to avoid fetching on every request.

### 3. Set cache directory permissions

In cPanel File Manager, right-click the `cache/` folder → **Change Permissions**
→ set to **755**.

Or via SSH:

```bash
chmod 755 ~/public_html/cache
```

### 4. Visit your site

Open your domain in a browser. The first load will be slightly slower while
caches fill (RSS feeds, weather, GitHub). Subsequent loads will be fast.

---

## Configuration

Edit `includes/config.php` to change settings:

```php
// BASE_PATH: leave '' for root install, or '/subdir' if in a subdirectory
define('BASE_PATH', '');

// Cache directory (relative to includes/)
define('CACHE_DIR', __DIR__ . '/../cache');

// Cache lifetimes in seconds
define('CACHE_TTL_FEED',    300); // 5 min  — RSS feeds
define('CACHE_TTL_WEATHER', 600); // 10 min — weather
```

### Installing in a subdirectory

If your site lives at `yourdomain.com/news/`:

1. Upload files to `public_html/news/`
2. Edit `includes/config.php` → change `BASE_PATH` to `'/news'`
3. Edit `.htaccess` → change `RewriteBase /` to `RewriteBase /news/`

---

## Pages

| URL | Description |
|---|---|
| `/` | Terminal — aggregated tech news from RSS feeds |
| `/weather` | Local weather (Ardill, SK) via Open-Meteo |
| `/community` | Reddit r/linux, r/programming, r/netsec, r/sysadmin, r/opensource |
| `/github` | Trending GitHub repos (past 7 days) |
| `/stocks` | Market overview via TradingView widget |
| `/links` | Curated link collection |
| `/cori` | Cori's personal links |
| `/j` | J's personal dashboard |
| `/about` | About page |
| `/agent` | Agent's Picks |
| `/source` | Browse the site's own PHP source code |

---

## Known Limitations

### Community page (Reddit)

Reddit's API sometimes blocks server-side requests depending on the IP
reputation of your hosting provider. If `/community` shows no posts:

- Delete `cache/reddit.json` and reload to force a fresh fetch
- If it consistently fails, your host's IP may be blocked by Reddit
- Workaround: fetch Reddit data client-side via JavaScript, or proxy
  through a service with a trusted IP

### GitHub API rate limits

The GitHub search API allows 10 unauthenticated requests per minute per IP.
The site caches results for 15 minutes so this is rarely an issue in practice.
If you hit rate limits, add a personal access token:

In `github.php`, find the `$ctx` definition and add to the headers:
`Authorization: Bearer YOUR_TOKEN`

### Weather location

The weather is hardcoded to Ardill, SK (latitude 49.78, longitude -104.57).
To change it, open `includes/weather.php` and update `$lat` and `$lon`.

---

## Customizing RSS sources

Edit the `$sources` array in `includes/rss.php` to add, remove, or swap feeds.
Each source needs an `id` (unique slug for caching), `label` (display name),
and `url` (RSS or Atom feed URL).

---

*Built with PHP 8, zero JavaScript, and a lot of CSS. — dispelled.ca*
