chore: scaffold Outlook VSTO Add-in for EspoCRM
Initial repo scaffold for a Windows VSTO/COM Outlook Add-in (C#/.NET FW 4.8) that integrates Outlook desktop with EspoCRM via the existing MailRouter v1.2.0 endpoint (POST /api/v1/MailRouter/file). Includes: - Solution + 4 csproj projects (host, Core, UI, Tests) under src/ - VSTO host project stub (ThisAddIn.cs, requires VS 2022 + Office workload to fully resolve) - Core/UI/Tests as SDK-style net48 projects (NuGet: Polly, System.Text.Json, Serilog, CommunityToolkit.Mvvm, xUnit, Moq, FluentAssertions) - .gitea/workflows/build.yml for Windows runner CI (publish/sign placeholders) - docs/ARCHITECTURE.md (component diagram, threading model, per-feature flow) - docs/ONBOARDING.md (Hebrew lawyer-facing install guide) Server-side already in place: MailRouter v1.2.0 — see plan ~/.claude/plans/resilient-sauteeing-feather.md for the 6-week implementation plan. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,75 @@
|
||||
name: Build OutlookAddin
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
tags: ['v*']
|
||||
pull_request:
|
||||
branches: [main]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Restore NuGet
|
||||
run: nuget restore OutlookAddin.sln
|
||||
|
||||
- name: Build
|
||||
run: msbuild OutlookAddin.sln /t:Build /p:Configuration=Release /p:Platform="Any CPU" /m
|
||||
|
||||
- name: Test
|
||||
run: dotnet test src/OutlookAddin.Tests/OutlookAddin.Tests.csproj --configuration Release --no-build --logger "trx;LogFileName=test-results.trx"
|
||||
|
||||
- name: Upload test results
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: test-results
|
||||
path: '**/*.trx'
|
||||
|
||||
publish:
|
||||
needs: build
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Restore NuGet
|
||||
run: nuget restore OutlookAddin.sln
|
||||
|
||||
- name: Build Release
|
||||
run: msbuild OutlookAddin.sln /t:Build /p:Configuration=Release /p:Platform="Any CPU" /m
|
||||
|
||||
# Code signing — cert installed on the runner's CurrentUser\My store.
|
||||
# Thumbprint comes from a Gitea Actions secret CODESIGN_THUMBPRINT.
|
||||
- name: Sign assemblies
|
||||
run: |
|
||||
$thumb = "${{ secrets.CODESIGN_THUMBPRINT }}"
|
||||
signtool sign /sha1 $thumb /fd SHA256 /tr http://timestamp.digicert.com /td SHA256 `
|
||||
src/OutlookAddin/bin/Release/MarcusLaw.OutlookAddin.dll `
|
||||
src/OutlookAddin.Core/bin/Release/net48/MarcusLaw.OutlookAddin.Core.dll `
|
||||
src/OutlookAddin.UI/bin/Release/net48/MarcusLaw.OutlookAddin.UI.dll
|
||||
|
||||
# mage.exe creates the ClickOnce .application + .vsto manifests.
|
||||
# See docs/ARCHITECTURE.md → Deployment for the full command sequence.
|
||||
- name: Publish ClickOnce
|
||||
run: |
|
||||
# TODO: invoke mage.exe / `vsto-publish` script (placeholder)
|
||||
echo "ClickOnce publish — to be implemented in week 6"
|
||||
|
||||
- name: Upload to platform.dev
|
||||
env:
|
||||
SSH_KEY: ${{ secrets.PLATFORM_DEV_SSH_KEY }}
|
||||
run: |
|
||||
# TODO: rsync publish/ to platform.dev.marcus-law.co.il:/var/www/outlook-addin/
|
||||
echo "rsync — to be implemented in week 6"
|
||||
|
||||
- name: Notify Mattermost
|
||||
if: success()
|
||||
run: |
|
||||
# TODO: POST to MATTERMOST_WEBHOOK_RELEASES (from Infisical /mattermost)
|
||||
echo "Mattermost notification — to be implemented in week 6"
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
# Build output
|
||||
[Bb]in/
|
||||
[Oo]bj/
|
||||
[Dd]ebug/
|
||||
[Rr]elease/
|
||||
x64/
|
||||
x86/
|
||||
*.user
|
||||
*.suo
|
||||
*.userprefs
|
||||
*.sln.docstates
|
||||
|
||||
# VS / Rider / VSCode
|
||||
.vs/
|
||||
.idea/
|
||||
.vscode/
|
||||
*.swp
|
||||
|
||||
# NuGet
|
||||
packages/
|
||||
*.nupkg
|
||||
*.snupkg
|
||||
project.lock.json
|
||||
project.fragment.lock.json
|
||||
|
||||
# Publish output (ClickOnce / MSIX)
|
||||
publish/
|
||||
PublishProfiles/
|
||||
*.pubxml.user
|
||||
|
||||
# VSTO build output
|
||||
*_TemporaryKey.pfx
|
||||
*.vsto
|
||||
*.application
|
||||
*.deploy
|
||||
*.manifest
|
||||
|
||||
# Code signing — NEVER commit private keys
|
||||
*.pfx
|
||||
*.snk
|
||||
|
||||
# Settings / logs that should never be committed
|
||||
appsettings.local.json
|
||||
*.log
|
||||
logs/
|
||||
|
||||
# OS
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
desktop.ini
|
||||
|
||||
# Task Master (config may contain API keys)
|
||||
.taskmaster/config.json
|
||||
.taskmaster/state.json
|
||||
.taskmaster/reports/
|
||||
.taskmaster/templates/
|
||||
@@ -0,0 +1,39 @@
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.0.0.0
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OutlookAddin", "src\OutlookAddin\OutlookAddin.csproj", "{A0000000-0000-0000-0000-000000000001}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OutlookAddin.Core", "src\OutlookAddin.Core\OutlookAddin.Core.csproj", "{A0000000-0000-0000-0000-000000000002}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OutlookAddin.UI", "src\OutlookAddin.UI\OutlookAddin.UI.csproj", "{A0000000-0000-0000-0000-000000000003}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OutlookAddin.Tests", "src\OutlookAddin.Tests\OutlookAddin.Tests.csproj", "{A0000000-0000-0000-0000-000000000004}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{A0000000-0000-0000-0000-000000000001}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{A0000000-0000-0000-0000-000000000001}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{A0000000-0000-0000-0000-000000000001}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{A0000000-0000-0000-0000-000000000001}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{A0000000-0000-0000-0000-000000000002}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{A0000000-0000-0000-0000-000000000002}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{A0000000-0000-0000-0000-000000000002}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{A0000000-0000-0000-0000-000000000002}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{A0000000-0000-0000-0000-000000000003}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{A0000000-0000-0000-0000-000000000003}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{A0000000-0000-0000-0000-000000000003}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{A0000000-0000-0000-0000-000000000003}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{A0000000-0000-0000-0000-000000000004}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{A0000000-0000-0000-0000-000000000004}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{A0000000-0000-0000-0000-000000000004}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{A0000000-0000-0000-0000-000000000004}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
@@ -0,0 +1,46 @@
|
||||
# OutlookAddin — תוסף EspoCRM ל-Outlook Desktop
|
||||
|
||||
תוסף VSTO/COM ל-Outlook ל-Windows שמאפשר לעו"ד לתייק מיילים ל-EspoCRM ישירות מ-Outlook, בלי הגדרת BCC.
|
||||
|
||||
## פיצ'רים
|
||||
|
||||
1. **תיוק מייל קיים לתיק/לקוח** — Ribbon button "File to EspoCRM" עם דיאלוג חיפוש (Case/Contact/Lead/Account).
|
||||
2. **חיפוש לקוח/תיק על מייל נכנס** — Reading-pane sidebar שמזהה את השולח ומציג את התיקים הקיימים.
|
||||
3. **כתיבת מייל חדש מתוך תיק** — מ-Outlook (Ribbon ב-compose) או מ-EspoCRM (URL protocol `outlookaddin://compose?caseId=...`).
|
||||
4. **סנכרון אוטומטי של תיקיות נבחרות** — Auto-file למיילים שמגיעים לתיקיות שמוגדרות לניטור.
|
||||
|
||||
## דרישות
|
||||
|
||||
- Windows 10/11
|
||||
- Outlook desktop: Microsoft 365 / Office 2021 / Office 2019 (לא 2016)
|
||||
- .NET Framework 4.8 (מובנה ב-Win10/11)
|
||||
- API key ב-EspoCRM (מקבלים מאדמין)
|
||||
|
||||
## התקנה
|
||||
|
||||
ClickOnce — לחץ על הלינק [https://platform.dev.marcus-law.co.il/outlook-addin/setup.exe](https://platform.dev.marcus-law.co.il/outlook-addin/setup.exe) ועקוב אחר ההוראות. Outlook יעלה מחדש אוטומטית.
|
||||
|
||||
## ארכיטקטורה
|
||||
|
||||
- **Server-side:** [MailRouter v1.2.0](https://gitea.dev.marcus-law.co.il/espocrm-extensions/MailRouter) חושף `POST /api/v1/MailRouter/file`.
|
||||
- **Client-side:** ה-Add-in מדבר עם MailRouter (file) + EspoCRM REST API (search/lookup).
|
||||
- **Auth:** `Espo-Authorization: <base64(username:apiKey)>` per-user.
|
||||
|
||||
מבנה ה-Solution: ראה [`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md).
|
||||
|
||||
## פיתוח
|
||||
|
||||
```bash
|
||||
# ב-Windows machine עם VS 2022 + VSTO workload
|
||||
git clone https://gitea.dev.marcus-law.co.il/espocrm-extensions/OutlookAddin.git
|
||||
cd OutlookAddin
|
||||
nuget restore
|
||||
msbuild /t:Build /p:Configuration=Debug
|
||||
# F5 ב-VS מפעיל Outlook עם ה-Add-in טעון לדיבאג
|
||||
```
|
||||
|
||||
CI build על Windows runner ב-Gitea Actions — ראה [`.gitea/workflows/build.yml`](.gitea/workflows/build.yml).
|
||||
|
||||
## רישיון
|
||||
|
||||
Internal use only — Marcus-Law.
|
||||
@@ -0,0 +1,143 @@
|
||||
# OutlookAddin — Architecture
|
||||
|
||||
תוסף VSTO/COM ל-Outlook Desktop שמדבר עם EspoCRM. ראה גם [`resilient-sauteeing-feather.md`](../../.claude/plans/resilient-sauteeing-feather.md) לתכנית המלאה.
|
||||
|
||||
## Component diagram
|
||||
|
||||
```
|
||||
┌──────────────────────────────────────────────────────────────────────┐
|
||||
│ Outlook Desktop (Win, Office 2019/2021/M365) │
|
||||
│ │
|
||||
│ ┌────────────────────────────────────────────────────────────────┐ │
|
||||
│ │ MarcusLaw.OutlookAddin (.dll, VSTO host) │ │
|
||||
│ │ ├── Ribbon (Explorer + Inspector) │ │
|
||||
│ │ ├── CustomTaskPane (Reading-pane sidebar) │ │
|
||||
│ │ ├── EventHandlers (ItemSend, ItemAdd, SelectionChange) │ │
|
||||
│ │ └── UrlProtocol (outlookaddin:// listener via named pipe) │ │
|
||||
│ └────────────────────────────────────────────────────────────────┘ │
|
||||
│ │ uses ↓ │
|
||||
│ ┌────────────────────────────────────────────────────────────────┐ │
|
||||
│ │ MarcusLaw.OutlookAddin.UI (WPF dialogs + VMs) │ │
|
||||
│ │ FileToCaseDialog · SettingsDialog · ComposeFromCaseDialog │ │
|
||||
│ │ ReadingPaneView · LocalizationManager · RTL themes │ │
|
||||
│ └────────────────────────────────────────────────────────────────┘ │
|
||||
│ │ uses ↓ │
|
||||
│ ┌────────────────────────────────────────────────────────────────┐ │
|
||||
│ │ MarcusLaw.OutlookAddin.Core (pure .NET 4.8, Office-free) │ │
|
||||
│ │ EspoCrmClient (HttpClient + Polly) │ │
|
||||
│ │ FilingService · MatchingService · DiskRetryQueue │ │
|
||||
│ │ DpapiCredentialStore · SettingsManager · LoggerFactory │ │
|
||||
│ └────────────────────────────────────────────────────────────────┘ │
|
||||
└──────────────────────────┬───────────────────────────────────────────┘
|
||||
│ HTTPS
|
||||
↓
|
||||
┌────────────────────────────┐
|
||||
│ EspoCRM (PHP) │
|
||||
│ + MailRouter v1.2.0 │
|
||||
│ POST /MailRouter/file │
|
||||
│ GET /EmailAddress/search │
|
||||
│ GET /GlobalSearch │
|
||||
│ GET /Case, /Account, ... │
|
||||
└────────────────────────────┘
|
||||
```
|
||||
|
||||
## Threading model
|
||||
|
||||
Outlook COM is **STA**. The single Outlook UI thread must own every COM object reference. Background work (HTTP, base64 encoding) must run on the threadpool, and any callback that touches Outlook must marshal back via the captured `SynchronizationContext`.
|
||||
|
||||
**The golden rule:** never touch a COM object from inside a `Task.Run` continuation. Either:
|
||||
- Read primitive values (string, int, byte[]) from the COM object on the UI thread first, pass those to `Task.Run`, or
|
||||
- Use `await syncContext.PostAsync(() => mailItem.Categories = ...)` to marshal back.
|
||||
|
||||
`Globals.ThisAddIn.Application` is captured in `ThisAddIn_Startup` and exposed via `IOutlookAccess` wrapper.
|
||||
|
||||
## Per-feature flow
|
||||
|
||||
### File to EspoCRM (feature 1)
|
||||
|
||||
```
|
||||
User clicks Ribbon "File to EspoCRM"
|
||||
→ FileToCaseDialog opens (modal, WPF, RTL)
|
||||
→ User searches: GET /GlobalSearch?q=... debounced 250ms
|
||||
→ User picks target, dialog returns (parentType, parentId)
|
||||
→ FilingService.FileAsync(MailItem[], target) on threadpool:
|
||||
for each MailItem:
|
||||
- extract on STA: subject, from, to/cc/bcc, body, dateSent
|
||||
- extract PR_INTERNET_MESSAGE_ID via PropertyAccessor
|
||||
- for each Attachment: SaveAsFile → read bytes → base64 → delete temp
|
||||
- POST /MailRouter/file (Polly retries)
|
||||
- 200/201 → marshal back: MailItem.Categories += "Filed: <name>"
|
||||
- 409 → success (already filed)
|
||||
- 401 → modal "re-enter API key"
|
||||
- else → DiskRetryQueue
|
||||
→ Toast: "Filed 3 of 4 emails. 1 queued."
|
||||
```
|
||||
|
||||
### Reading-pane sidebar (feature 2)
|
||||
|
||||
```
|
||||
Explorer.SelectionChange → debounce 200ms
|
||||
→ ExplorerSelectionHandler grabs MailItem.SenderEmailAddress
|
||||
→ MatchingService.LookupAsync(sender):
|
||||
1. GET /EmailAddress/search?q=<sender>&entityType=Contact
|
||||
2. If 1 hit: GET /Account/<contactId.accountId>
|
||||
GET /Case?where[linkedWith]=contacts:<contactId>
|
||||
3. cache 10 min in MemoryCache
|
||||
→ ReadingPaneViewModel updates → WPF binding refreshes sidebar
|
||||
```
|
||||
|
||||
### Compose from Case (feature 3)
|
||||
|
||||
```
|
||||
A. Outlook → EspoCRM:
|
||||
Inspector Ribbon "Compose from Case"
|
||||
→ ComposeFromCaseDialog (filtered to Case)
|
||||
→ GET /Case/<id>?select=name,number,contactsIds,accountId
|
||||
→ set MailItem.To, .Subject, append HTMLBody footer
|
||||
→ stamp UserProperty EspoCaseId = caseId, GUID = guid
|
||||
|
||||
B. EspoCRM → Outlook:
|
||||
User clicks "Email this case" in EspoCRM UI
|
||||
→ outlookaddin://compose?caseId=<id>
|
||||
→ OS launches OutlookAddinProtocolHandler.exe
|
||||
→ handler writes to \\.\pipe\MarcusLaw.OutlookAddin
|
||||
→ Add-in pipe listener reproduces flow A
|
||||
|
||||
C. ItemSend:
|
||||
MailItem sent → handler reads UserProperty EspoCaseId
|
||||
→ watch Sent Items via Items.ItemAdd for matching GUID
|
||||
→ when found: FilingService.FileAsync(item, case)
|
||||
```
|
||||
|
||||
### Auto-file folders (feature 4)
|
||||
|
||||
```
|
||||
Settings → Folders tab: TreeView of mailbox folders, checkboxes
|
||||
Each watched: default parent, mode (auto-file / notify), threshold
|
||||
|
||||
FolderWatcher (singleton):
|
||||
- Loads (StoreId, FolderPath, EntryID) tuples from settings.json
|
||||
- For each: store Items reference in static field (CRITICAL — prevents GC)
|
||||
- Subscribe Items.ItemAdd
|
||||
|
||||
On ItemAdd:
|
||||
- Capture EntryID immediately
|
||||
- Check HashSet<EntryID> dedup
|
||||
- Queue work to STA-bound TaskScheduler
|
||||
- MatchingService.LookupAsync(sender)
|
||||
- 1 hit + auto-mode → FilingService.FileAsync
|
||||
- else → Categories += "Needs filing" + toast
|
||||
```
|
||||
|
||||
## Deployment
|
||||
|
||||
ClickOnce hosted at `https://platform.dev.marcus-law.co.il/outlook-addin/`. Auto-update on Outlook launch. Self-signed cert deployed to all 10 lawyer machines via GPO (Trusted Publishers + Trusted Root CA).
|
||||
|
||||
ראה [`resilient-sauteeing-feather.md`](../../.claude/plans/resilient-sauteeing-feather.md) § Deployment לפרטים מלאים.
|
||||
|
||||
## Repository conventions
|
||||
|
||||
- **Conventional commits** (`fix:`, `feat:`, `chore:`, `docs:`)
|
||||
- **Branches:** `main` (protected, CI green required), feature branches `feat/<short-name>`
|
||||
- **Versioning:** SemVer, ClickOnce auto-rebuilds on every push to `main`, formal releases tagged `v1.0.0`, `v1.1.0`, ...
|
||||
- **Task Master:** each issue/feature corresponds to a `task-master` task. תהליך פר-extension כפי שב-`~/espocrm-extensions/CLAUDE.md`.
|
||||
@@ -0,0 +1,64 @@
|
||||
# התקנת OutlookAddin — מדריך לעו"ד
|
||||
|
||||
## מה זה התוסף?
|
||||
|
||||
תוסף ל-Outlook שמאפשר לתייק מיילים ל-EspoCRM ישירות מתוך Outlook, בלי להעתיק/להדביק או להגדיר BCC.
|
||||
|
||||
## דרישות
|
||||
|
||||
- Windows 10 או Windows 11
|
||||
- Outlook desktop (Microsoft 365 / Office 2021 / Office 2019)
|
||||
- חיבור אינטרנט
|
||||
- API key אישי ב-EspoCRM (תקבל מהאדמין)
|
||||
|
||||
## התקנה (5 דקות)
|
||||
|
||||
1. סגור את Outlook לגמרי (`File → Exit`, או יציאה דרך System Tray).
|
||||
2. פתח את הלינק: **https://platform.dev.marcus-law.co.il/outlook-addin/setup.exe**
|
||||
3. לחץ "Install" בחלונית שתיפתח.
|
||||
4. ההתקנה תיגמר תוך 30 שניות. אם רואים שגיאת "Unknown Publisher" — פנה לאדמין (חסר אישור הצוות).
|
||||
5. פתח את Outlook. תראה ב-Ribbon תפריט חדש בשם "EspoCRM".
|
||||
|
||||
## הגדרה ראשונית
|
||||
|
||||
1. בפעם הראשונה ש-Outlook ייפתח, ייפתח חלון "ברוך הבא ל-EspoCRM Add-in".
|
||||
2. **כתובת השרת** — מולא מראש: `https://crm.prod.marcus-law.co.il`
|
||||
3. **שם משתמש** — כתובת המייל שלך (לדוגמה `chaim@marcus-law.co.il`).
|
||||
4. **API Key** — הדבק את ה-key שקיבלת מהאדמין (נראה כמו `a1b2c3d4e5f6...`).
|
||||
5. לחץ "Test Connection". סימן ✓ ירוק = הכל עובד.
|
||||
6. לחץ "Save & Continue".
|
||||
|
||||
## שימוש יומיומי
|
||||
|
||||
### תיוק מייל לתיק
|
||||
- בחר מייל אחד או יותר ברשימה.
|
||||
- לחץ "**File to EspoCRM**" ב-Ribbon.
|
||||
- חפש את שם התיק או הלקוח, בחר, לחץ "File".
|
||||
- המייל יקבל קטגוריה צבעונית "Filed: <שם>" בעמודת הצבע ב-Outlook.
|
||||
|
||||
### מי הלקוח?
|
||||
- כשפותחים מייל, סיידבר ימני יופיע עם פרטי הלקוח/התיק (אם קיים ב-EspoCRM).
|
||||
- לחץ "Open in EspoCRM" כדי לעבור ישירות לתיק.
|
||||
|
||||
### כתיבת מייל חדש מתוך תיק
|
||||
- ב-EspoCRM, פתח תיק → לחץ "Email this case".
|
||||
- Outlook ייפתח עם כתובת + נושא ממולאים אוטומטית.
|
||||
|
||||
### סנכרון אוטומטי של תיקייה
|
||||
- לחץ "**Settings**" ב-Ribbon → טאב "Folders".
|
||||
- סמן את התיקיות שאתה רוצה לנטר (לדוגמה Inbox/Smith Family).
|
||||
- בחר "Auto-file when confident" או "Notify only".
|
||||
- מיילים שייכנסו לתיקיות האלו יסומנו/יתויקו אוטומטית.
|
||||
|
||||
## בעיות נפוצות
|
||||
|
||||
| בעיה | פתרון |
|
||||
|---|---|
|
||||
| Ribbon "EspoCRM" לא מופיע | File → Options → Add-ins → לוודא ש-MarcusLaw.OutlookAddin פעיל |
|
||||
| "401 Unauthorized" | API key לא נכון או הוחלף — פנה לאדמין |
|
||||
| "EspoCRM unreachable" | בדוק חיבור לאינטרנט; המיילים יישמרו בתור ויתויקו כשהחיבור יחזור |
|
||||
| Outlook איטי אחרי התקנה | סגור וטען מחדש את Outlook |
|
||||
|
||||
## דיווח על בעיה
|
||||
|
||||
ב-Settings → טאב "Diagnostics" → לחץ "Send Diagnostic Logs". יווצר קובץ zip ב-`%TEMP%`. שלח אותו לאדמין.
|
||||
@@ -0,0 +1,23 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net48</TargetFramework>
|
||||
<RootNamespace>MarcusLaw.OutlookAddin.Core</RootNamespace>
|
||||
<AssemblyName>MarcusLaw.OutlookAddin.Core</AssemblyName>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<Nullable>enable</Nullable>
|
||||
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
|
||||
<GenerateAssemblyInfo>true</GenerateAssemblyInfo>
|
||||
<Company>Marcus-Law</Company>
|
||||
<Product>OutlookAddin</Product>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="System.Text.Json" Version="8.0.5" />
|
||||
<PackageReference Include="Polly" Version="8.4.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="8.0.1" />
|
||||
<PackageReference Include="Serilog" Version="4.0.1" />
|
||||
<PackageReference Include="Serilog.Sinks.File" Version="6.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,24 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net48</TargetFramework>
|
||||
<RootNamespace>MarcusLaw.OutlookAddin.Tests</RootNamespace>
|
||||
<AssemblyName>MarcusLaw.OutlookAddin.Tests</AssemblyName>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<Nullable>enable</Nullable>
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.11.1" />
|
||||
<PackageReference Include="xunit" Version="2.9.0" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.2" />
|
||||
<PackageReference Include="Moq" Version="4.20.72" />
|
||||
<PackageReference Include="FluentAssertions" Version="6.12.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\OutlookAddin.Core\OutlookAddin.Core.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,23 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net48</TargetFramework>
|
||||
<RootNamespace>MarcusLaw.OutlookAddin.UI</RootNamespace>
|
||||
<AssemblyName>MarcusLaw.OutlookAddin.UI</AssemblyName>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<Nullable>enable</Nullable>
|
||||
<UseWPF>true</UseWPF>
|
||||
<UseWindowsForms>true</UseWindowsForms>
|
||||
<Company>Marcus-Law</Company>
|
||||
<Product>OutlookAddin</Product>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.3.2" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\OutlookAddin.Core\OutlookAddin.Core.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,76 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
NOTE: VSTO host projects rely on the legacy MSBuild csproj format with the VSTO
|
||||
project type GUIDs. This stub is intentionally minimal — open the .sln in
|
||||
Visual Studio 2022 with the "Office/SharePoint development" workload installed,
|
||||
then right-click the project → "Reload" if VS prompts. VS will populate the
|
||||
Office references, manifest, and ribbon designer scaffolding automatically.
|
||||
|
||||
Project type GUIDs:
|
||||
{BAA0C2D2-18E2-41B9-852F-F413020CAA33} — VSTO Add-in
|
||||
{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} — C#
|
||||
-->
|
||||
<Project ToolsVersion="Current" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{A0000000-0000-0000-0000-000000000001}</ProjectGuid>
|
||||
<ProjectTypeGuids>{BAA0C2D2-18E2-41B9-852F-F413020CAA33};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
||||
<OutputType>Library</OutputType>
|
||||
<RootNamespace>MarcusLaw.OutlookAddin</RootNamespace>
|
||||
<AssemblyName>MarcusLaw.OutlookAddin</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
|
||||
<Company>Marcus-Law</Company>
|
||||
<Product>OutlookAddin</Product>
|
||||
<OfficeApplication>Outlook</OfficeApplication>
|
||||
<SignManifests>false</SignManifests>
|
||||
<AssemblyOriginatorKeyFile>
|
||||
</AssemblyOriginatorKeyFile>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Drawing" />
|
||||
<Reference Include="System.Windows.Forms" />
|
||||
<Reference Include="System.Xml" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="PresentationCore" />
|
||||
<Reference Include="PresentationFramework" />
|
||||
<Reference Include="WindowsBase" />
|
||||
<Reference Include="WindowsFormsIntegration" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="ThisAddIn.cs" />
|
||||
<None Include="ThisAddIn.Designer.xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\OutlookAddin.Core\OutlookAddin.Core.csproj">
|
||||
<Project>{A0000000-0000-0000-0000-000000000002}</Project>
|
||||
<Name>OutlookAddin.Core</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\OutlookAddin.UI\OutlookAddin.UI.csproj">
|
||||
<Project>{A0000000-0000-0000-0000-000000000003}</Project>
|
||||
<Name>OutlookAddin.UI</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<Import Project="$(VSToolsPath)\OfficeTools\Microsoft.VisualStudio.Tools.Office.targets" Condition="'$(VSToolsPath)' != ''" />
|
||||
</Project>
|
||||
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
VSTO ThisAddIn designer XML.
|
||||
|
||||
Placeholder — Visual Studio 2022 regenerates this file from the VSTO host
|
||||
template when the project is first opened on a machine with the
|
||||
"Office/SharePoint development" workload installed. Do NOT hand-edit; it will
|
||||
be overwritten.
|
||||
-->
|
||||
<root />
|
||||
@@ -0,0 +1,47 @@
|
||||
using System;
|
||||
using Outlook = Microsoft.Office.Interop.Outlook;
|
||||
using Office = Microsoft.Office.Core;
|
||||
|
||||
namespace MarcusLaw.OutlookAddin
|
||||
{
|
||||
public partial class ThisAddIn
|
||||
{
|
||||
private void ThisAddIn_Startup(object sender, EventArgs e)
|
||||
{
|
||||
// Composition root — wires up:
|
||||
// - Serilog logger (LoggerFactory.Init)
|
||||
// - SettingsManager (load %APPDATA%\MarcusLaw\OutlookAddin\settings.json)
|
||||
// - DpapiCredentialStore (load API key)
|
||||
// - EspoCrmClient (HttpClient + Polly + auth header)
|
||||
// - FilingService, MatchingService, DiskRetryQueue
|
||||
// - Ribbon callbacks
|
||||
// - CustomTaskPane for reading-pane sidebar
|
||||
// - FolderWatcher (re-subscribe to watched folders from settings)
|
||||
// - ItemSendHandler (auto-file on send when EspoCaseId UserProperty present)
|
||||
// - URL protocol named-pipe listener
|
||||
//
|
||||
// See ARCHITECTURE.md for sequencing constraints.
|
||||
}
|
||||
|
||||
private void ThisAddIn_Shutdown(object sender, EventArgs e)
|
||||
{
|
||||
// Flush logger, dispose HttpClient, unsubscribe events.
|
||||
// Note: in Outlook 2013+ this event is NOT raised reliably — do NOT
|
||||
// rely on it for critical persistence. Save state eagerly.
|
||||
}
|
||||
|
||||
#region VSTO generated code
|
||||
protected override Office.IRibbonExtensibility CreateRibbonExtensibilityObject()
|
||||
{
|
||||
// Returns the merged Explorer + Inspector ribbon (see Ribbon/ folder).
|
||||
return base.CreateRibbonExtensibilityObject();
|
||||
}
|
||||
|
||||
private void InternalStartup()
|
||||
{
|
||||
this.Startup += new EventHandler(ThisAddIn_Startup);
|
||||
this.Shutdown += new EventHandler(ThisAddIn_Shutdown);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user