New /bulk-log page: paste emails (semicolon/comma/newline separated), choose method, outcome, contact date and a follow-up date, review the matches, then log the contact on every matching non-removed business. Reuses BusinessRepository.LogInteraction so status and next-contact date update the same way as the single-business form. Registers bulklogcontroller in the core controller whitelist. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>master
| @@ -0,0 +1,198 @@ | |||||
| <% | |||||
| Class BulkLogController_Class | |||||
| Private m_useLayout | |||||
| Private m_title | |||||
| Private Sub Class_Initialize() | |||||
| m_useLayout = True | |||||
| m_title = "Bulk log" | |||||
| End Sub | |||||
| Public Property Get useLayout | |||||
| useLayout = m_useLayout | |||||
| End Property | |||||
| Public Property Let useLayout(v) | |||||
| m_useLayout = v | |||||
| End Property | |||||
| Public Property Get Title | |||||
| Title = m_title | |||||
| End Property | |||||
| Public Property Let Title(v) | |||||
| m_title = v | |||||
| End Property | |||||
| '------------------------------------------------------------------------------------------------------------------- | |||||
| ' GET /bulk-log : paste emails + dates | |||||
| '------------------------------------------------------------------------------------------------------------------- | |||||
| Public Sub index() | |||||
| Dim csrf, fv | |||||
| csrf = Csrf_Token("bulklog") | |||||
| Set fv = Nothing | |||||
| RenderForm csrf, fv | |||||
| End Sub | |||||
| ' Shared by index and a failed preview: fv (unsaved values) is Nothing for a blank form. | |||||
| Private Sub RenderForm(csrf, fv) | |||||
| Dim today, emailsText, method, outcome, contactedAt, followUp, notes | |||||
| today = Date_Iso(Date) | |||||
| emailsText = "" : method = "email" : outcome = "" : contactedAt = today : followUp = "" : notes = "" | |||||
| If Not fv Is Nothing Then | |||||
| emailsText = fv("emails") : method = fv("method") : outcome = fv("outcome") | |||||
| contactedAt = fv("contacted_at") : followUp = fv("follow_up_date") : notes = fv("notes") | |||||
| End If | |||||
| %> | |||||
| <!--#include file="../views/BulkLog/index.asp" --> | |||||
| <% | |||||
| End Sub | |||||
| '------------------------------------------------------------------------------------------------------------------- | |||||
| ' POST /bulk-log/preview : show who matched before anything is written | |||||
| '------------------------------------------------------------------------------------------------------------------- | |||||
| Public Sub preview() | |||||
| Dim v, parsed, matches, i, found, k, missing, skipped, toLog, csrf, emailsJoined | |||||
| Set v = ReadForm() | |||||
| If Not v.Exists("emails") Then Exit Sub ' ReadForm already redirected | |||||
| Set parsed = BulkLog_ParseEmails(v("emails")) | |||||
| Set matches = BusinessRepository().FindByEmails(parsed("valid")) | |||||
| Set found = CreateObject("Scripting.Dictionary") | |||||
| Set toLog = CreateObject("Scripting.Dictionary") | |||||
| Set skipped = CreateObject("Scripting.Dictionary") | |||||
| For i = 0 To matches.Count - 1 | |||||
| found(LCase(matches(i)("email") & "")) = 1 | |||||
| If matches(i)("status") = "removed" Then | |||||
| skipped.Add skipped.Count, matches(i) | |||||
| Else | |||||
| toLog.Add toLog.Count, matches(i) | |||||
| End If | |||||
| Next | |||||
| Set missing = CreateObject("Scripting.Dictionary") | |||||
| For Each k In parsed("valid").Keys | |||||
| If Not found.Exists(k) Then missing.Add missing.Count, k | |||||
| Next | |||||
| emailsJoined = Join(parsed("valid").Keys, ";") | |||||
| csrf = Csrf_Token("bulklog") | |||||
| m_title = "Bulk log: review" | |||||
| %> | |||||
| <!--#include file="../views/BulkLog/preview.asp" --> | |||||
| <% | |||||
| End Sub | |||||
| '------------------------------------------------------------------------------------------------------------------- | |||||
| ' POST /bulk-log/run : log the contact on every matching, non-removed business | |||||
| '------------------------------------------------------------------------------------------------------------------- | |||||
| Public Sub run() | |||||
| Dim v, parsed, matches, i, n, contactedAt, followUp | |||||
| Set v = ReadForm() | |||||
| If Not v.Exists("emails") Then Exit Sub | |||||
| contactedAt = Date_Parse(v("contacted_at")) | |||||
| If IsNull(contactedAt) Then contactedAt = Date | |||||
| followUp = Date_Parse(v("follow_up_date")) | |||||
| Set parsed = BulkLog_ParseEmails(v("emails")) | |||||
| Set matches = BusinessRepository().FindByEmails(parsed("valid")) | |||||
| n = 0 | |||||
| For i = 0 To matches.Count - 1 | |||||
| If matches(i)("status") <> "removed" Then | |||||
| BusinessRepository().LogInteraction CLng(matches(i)("id")), v("method"), v("outcome"), v("notes"), _ | |||||
| contactedAt, followUp, False, Null | |||||
| n = n + 1 | |||||
| End If | |||||
| Next | |||||
| If n = 0 Then | |||||
| Flash().AddError "No businesses were updated." | |||||
| Response.Redirect "/bulk-log" | |||||
| End If | |||||
| If IsNull(followUp) Then | |||||
| Flash().Success = "Logged a contact on " & n & " business" & Iif_(n = 1, "", "es") & "." | |||||
| Else | |||||
| Flash().Success = "Logged a contact on " & n & " business" & Iif_(n = 1, "", "es") & ". Follow-up set for " & Date_Fmt(followUp) & "." | |||||
| End If | |||||
| Response.Redirect "/bulk-log" | |||||
| End Sub | |||||
| '------------------------------------------------------------------------------------------------------------------- | |||||
| ' Shared checks | |||||
| '------------------------------------------------------------------------------------------------------------------- | |||||
| ' Validates the CSRF token and the form fields; returns them as a Dictionary (empty after a redirect on error). | |||||
| Private Function ReadForm() | |||||
| Dim v, d, contactedAt, followUp, method, outcome | |||||
| Set v = CreateObject("Scripting.Dictionary") | |||||
| Set ReadForm = v | |||||
| If Not Csrf_Valid("bulklog", F("csrf")) Then | |||||
| Flash().AddError "Invalid form state. Please try again." | |||||
| Response.Redirect "/bulk-log" | |||||
| End If | |||||
| method = F("method") | |||||
| outcome = F("outcome") | |||||
| If Not Businesses_IsKey(Businesses_Methods(), method) Then | |||||
| Flash().AddError "Choose how you contacted them." | |||||
| Response.Redirect "/bulk-log" | |||||
| End If | |||||
| ' "Remove from list" is deliberately not offered in bulk. | |||||
| If outcome = "remove" Or Not Businesses_IsKey(Businesses_Outcomes(), outcome) Then outcome = "" | |||||
| If Len(F("contacted_at")) > 0 Then | |||||
| contactedAt = Date_Parse(F("contacted_at")) | |||||
| If IsNull(contactedAt) Then | |||||
| Flash().AddError "The contact date isn't a valid date." | |||||
| Response.Redirect "/bulk-log" | |||||
| End If | |||||
| If contactedAt > Date + 1 Then | |||||
| Flash().AddError "The contact date can't be in the future." | |||||
| Response.Redirect "/bulk-log" | |||||
| End If | |||||
| End If | |||||
| If Len(F("follow_up_date")) > 0 Then | |||||
| followUp = Date_Parse(F("follow_up_date")) | |||||
| If IsNull(followUp) Then | |||||
| Flash().AddError "The follow-up date isn't a valid date." | |||||
| Response.Redirect "/bulk-log" | |||||
| End If | |||||
| End If | |||||
| Set d = BulkLog_ParseEmails(F("emails")) | |||||
| If d("valid").Count = 0 Then | |||||
| Flash().AddError "Enter at least one valid email address." | |||||
| Response.Redirect "/bulk-log" | |||||
| End If | |||||
| If d("valid").Count > 500 Then | |||||
| Flash().AddError "That's more than 500 addresses; split them into smaller batches." | |||||
| Response.Redirect "/bulk-log" | |||||
| End If | |||||
| v("emails") = Left(F("emails"), 60000) | |||||
| v("method") = method | |||||
| v("outcome") = outcome | |||||
| v("contacted_at") = F("contacted_at") | |||||
| v("follow_up_date") = F("follow_up_date") | |||||
| v("notes") = Left(F("notes"), 10000) | |||||
| Set ReadForm = v | |||||
| End Function | |||||
| Private Function F(name) | |||||
| F = CStr(Request.Form(name)) | |||||
| End Function | |||||
| End Class | |||||
| Dim BulkLogController_Class__Singleton | |||||
| Function BulkLogController() | |||||
| If IsEmpty(BulkLogController_Class__Singleton) Then | |||||
| Set BulkLogController_Class__Singleton = New BulkLogController_Class | |||||
| End If | |||||
| Set BulkLogController = BulkLogController_Class__Singleton | |||||
| End Function | |||||
| %> | |||||
| @@ -15,5 +15,6 @@ | |||||
| <!--#include file="IndustriesController.asp" --> | <!--#include file="IndustriesController.asp" --> | ||||
| <!--#include file="AreasController.asp" --> | <!--#include file="AreasController.asp" --> | ||||
| <!--#include file="ImportController.asp" --> | <!--#include file="ImportController.asp" --> | ||||
| <!--#include file="BulkLogController.asp" --> | |||||
| <!--#include file="ExportController.asp" --> | <!--#include file="ExportController.asp" --> | ||||
| <!--#include file="DashboardController.asp" --> | <!--#include file="DashboardController.asp" --> | ||||
| @@ -58,6 +58,31 @@ Function Businesses_StatusClass(s) | |||||
| End Select | End Select | ||||
| End Function | End Function | ||||
| ' Splits pasted text like "a@x.com;b@y.com" (also commas, spaces, new lines) into addresses. | |||||
| ' Returns Dictionary("valid" -> Dictionary(lowercased email -> 1), "invalid" -> Dictionary(index -> text)). | |||||
| Function BulkLog_ParseEmails(text) | |||||
| Dim re, parts, p, valid, invalid, result | |||||
| Set valid = CreateObject("Scripting.Dictionary") | |||||
| Set invalid = CreateObject("Scripting.Dictionary") | |||||
| Set re = New RegExp | |||||
| re.Pattern = "^[^@\s;,]+@[^@\s;,]+\.[^@\s;,]+$" | |||||
| parts = Split(Replace(Replace(Replace(Replace(text & "", vbCrLf, ";"), vbLf, ";"), ",", ";"), vbTab, ";"), ";") | |||||
| For Each p In parts | |||||
| p = LCase(Trim(Replace(p, " ", ""))) | |||||
| If Len(p) > 0 And Len(p) <= 255 Then | |||||
| If re.Test(p) Then | |||||
| valid(p) = 1 | |||||
| Else | |||||
| invalid.Add invalid.Count, Left(p, 80) | |||||
| End If | |||||
| End If | |||||
| Next | |||||
| Set result = CreateObject("Scripting.Dictionary") | |||||
| Set result("valid") = valid | |||||
| Set result("invalid") = invalid | |||||
| Set BulkLog_ParseEmails = result | |||||
| End Function | |||||
| ' Query string for the list: current filters with optional page / sort overrides. | ' Query string for the list: current filters with optional page / sort overrides. | ||||
| Function Businesses_QS(f, page, sort, dir) | Function Businesses_QS(f, page, sort, dir) | ||||
| Dim k, s | Dim k, s | ||||
| @@ -172,6 +172,26 @@ Class BusinessRepository_Class | |||||
| End If | End If | ||||
| End Function | End Function | ||||
| ' Businesses whose email equals one of the keys of `emails` (a Dictionary of lowercased addresses). | |||||
| ' Returns a Dictionary of row Dictionaries (id, name, contact_name, email, city, state, status, next_action_date). | |||||
| Public Function FindByEmails(emails) | |||||
| Dim list, rs, k, d, i | |||||
| Set list = CreateObject("Scripting.Dictionary") | |||||
| For Each k In emails.Keys | |||||
| Set rs = Db_Query("SELECT id, name, contact_name, email, city, state, status, next_action_date FROM businesses WHERE email = ? ORDER BY name, id", Array(Db_PS(k))) | |||||
| Do While Not rs.EOF | |||||
| Set d = CreateObject("Scripting.Dictionary") | |||||
| For i = 0 To rs.Fields.Count - 1 | |||||
| d(rs.Fields(i).Name) = rs.Fields(i).Value | |||||
| Next | |||||
| list.Add list.Count, d | |||||
| rs.MoveNext | |||||
| Loop | |||||
| rs.Close | |||||
| Next | |||||
| Set FindByEmails = list | |||||
| End Function | |||||
| ' Returns the industry id for a name, creating the industry when it does not exist. Null for a blank name. | ' Returns the industry id for a name, creating the industry when it does not exist. Null for a blank name. | ||||
| Public Function FindOrCreateIndustry(name) | Public Function FindOrCreateIndustry(name) | ||||
| Dim rs | Dim rs | ||||
| @@ -0,0 +1,85 @@ | |||||
| <% Flash().ShowErrorsIfPresent : Flash().ShowSuccessIfPresent %> | |||||
| <div class="d-flex justify-content-between align-items-center mb-3"> | |||||
| <h1 class="h3 mb-0">Bulk log a contact</h1> | |||||
| </div> | |||||
| <form method="post" action="/bulk-log/preview" class="card shadow-sm" id="bulkForm"> | |||||
| <div class="card-body"> | |||||
| <input type="hidden" name="csrf" value="<%= H(csrf) %>" /> | |||||
| <div class="mb-3"> | |||||
| <label for="emails" class="form-label small mb-1">Email addresses</label> | |||||
| <textarea class="form-control" id="emails" name="emails" rows="4" required | |||||
| placeholder="jlkj@jkh.com;hjjkh@yoi.com"><%= H(emailsText) %></textarea> | |||||
| <div class="form-text">Separate addresses with semicolons, commas, spaces or new lines. Businesses with a matching email are updated; you review the matches before anything is saved.</div> | |||||
| </div> | |||||
| <div class="mb-3"> | |||||
| <div class="btn-group" role="group" aria-label="How"> | |||||
| <% Dim mth, oc | |||||
| For Each mth In Businesses_Methods() %> | |||||
| <input type="radio" class="btn-check" name="method" id="m_<%= mth(0) %>" value="<%= mth(0) %>" autocomplete="off"<% If mth(0) = method Then Response.Write " checked" %> /> | |||||
| <label class="btn btn-outline-primary btn-sm" for="m_<%= mth(0) %>"><%= H(mth(1)) %></label> | |||||
| <% Next %> | |||||
| </div> | |||||
| </div> | |||||
| <div class="row g-2 mb-2"> | |||||
| <div class="col-md-5"> | |||||
| <label for="outcome" class="form-label small mb-1">Outcome</label> | |||||
| <select class="form-select form-select-sm" id="outcome" name="outcome"> | |||||
| <option value="">—</option> | |||||
| <% For Each oc In Businesses_Outcomes() | |||||
| If oc(0) <> "remove" Then %> | |||||
| <option value="<%= oc(0) %>"<% If oc(0) = outcome Then Response.Write " selected" %>><%= H(oc(1)) %></option> | |||||
| <% End If | |||||
| Next %> | |||||
| </select> | |||||
| </div> | |||||
| <div class="col-6 col-md-3"> | |||||
| <label for="contacted_at" class="form-label small mb-1">Date contacted</label> | |||||
| <input type="date" class="form-control form-control-sm" id="contacted_at" name="contacted_at" value="<%= H(contactedAt) %>" max="<%= Date_Iso(Date + 1) %>" required /> | |||||
| </div> | |||||
| <div class="col-6 col-md-4"> | |||||
| <label for="follow_up_date" class="form-label small mb-1">Contact again on</label> | |||||
| <input type="date" class="form-control form-control-sm" id="follow_up_date" name="follow_up_date" value="<%= H(followUp) %>" /> | |||||
| </div> | |||||
| </div> | |||||
| <div class="mb-2 small"> | |||||
| <span class="text-muted">Quick:</span> | |||||
| <button type="button" class="btn btn-link btn-sm p-0 me-2" data-days="3">3 days</button> | |||||
| <button type="button" class="btn btn-link btn-sm p-0 me-2" data-days="7">1 week</button> | |||||
| <button type="button" class="btn btn-link btn-sm p-0 me-2" data-days="14">2 weeks</button> | |||||
| <button type="button" class="btn btn-link btn-sm p-0 me-2" data-days="30">1 month</button> | |||||
| <button type="button" class="btn btn-link btn-sm p-0 text-secondary" data-days="0">clear</button> | |||||
| </div> | |||||
| <div class="mb-3"> | |||||
| <label for="notes" class="form-label small mb-1">Notes (added to each business's history)</label> | |||||
| <textarea class="form-control form-control-sm" id="notes" name="notes" rows="3" maxlength="10000"><%= H(notes) %></textarea> | |||||
| </div> | |||||
| <button type="submit" class="btn btn-primary btn-sm"><i class="bi bi-search"></i> Find matches</button> | |||||
| </div> | |||||
| </form> | |||||
| <script> | |||||
| (function () { | |||||
| var contacted = document.getElementById('contacted_at'); | |||||
| var followUp = document.getElementById('follow_up_date'); | |||||
| function iso(d) { | |||||
| return d.getFullYear() + '-' + ('0' + (d.getMonth() + 1)).slice(-2) + '-' + ('0' + d.getDate()).slice(-2); | |||||
| } | |||||
| document.querySelectorAll('[data-days]').forEach(function (b) { | |||||
| b.addEventListener('click', function () { | |||||
| var days = parseInt(b.getAttribute('data-days'), 10); | |||||
| if (!days) { followUp.value = ''; return; } | |||||
| var base = contacted.value ? new Date(contacted.value + 'T00:00:00') : new Date(); | |||||
| base.setDate(base.getDate() + days); | |||||
| followUp.value = iso(base); | |||||
| }); | |||||
| }); | |||||
| })(); | |||||
| </script> | |||||
| @@ -0,0 +1,97 @@ | |||||
| <% Flash().ShowErrorsIfPresent : Flash().ShowSuccessIfPresent %> | |||||
| <div class="d-flex justify-content-between align-items-center mb-3"> | |||||
| <h1 class="h3 mb-0">Bulk log: review matches</h1> | |||||
| </div> | |||||
| <div class="card shadow-sm mb-3"> | |||||
| <div class="card-body"> | |||||
| <dl class="row mb-0 small"> | |||||
| <dt class="col-sm-3">How</dt> | |||||
| <dd class="col-sm-9"><%= H(Businesses_Label(Businesses_Methods(), v("method"))) %></dd> | |||||
| <dt class="col-sm-3">Outcome</dt> | |||||
| <dd class="col-sm-9"><% If Len(v("outcome")) > 0 Then Response.Write H(Businesses_Label(Businesses_Outcomes(), v("outcome"))) Else Response.Write "—" %></dd> | |||||
| <dt class="col-sm-3">Date contacted</dt> | |||||
| <dd class="col-sm-9"><%= Date_Fmt(Date_Parse(v("contacted_at"))) %></dd> | |||||
| <dt class="col-sm-3">Contact again on</dt> | |||||
| <dd class="col-sm-9"><% If Len(v("follow_up_date")) > 0 Then Response.Write Date_Fmt(Date_Parse(v("follow_up_date"))) Else Response.Write "— (no follow-up)" %></dd> | |||||
| <% If Len(v("notes")) > 0 Then %> | |||||
| <dt class="col-sm-3">Notes</dt> | |||||
| <dd class="col-sm-9"><%= Html_Nl2Br(v("notes")) %></dd> | |||||
| <% End If %> | |||||
| </dl> | |||||
| </div> | |||||
| </div> | |||||
| <div class="card shadow-sm mb-3"> | |||||
| <div class="card-body"> | |||||
| <h2 class="h5 mb-3">Will be updated <span class="text-muted fs-6">(<%= toLog.Count %>)</span></h2> | |||||
| <% If toLog.Count = 0 Then %> | |||||
| <p class="text-muted mb-0">No active businesses matched.</p> | |||||
| <% Else %> | |||||
| <div class="table-responsive"> | |||||
| <table class="table table-sm align-middle mb-0"> | |||||
| <thead class="table-light"><tr><th>Business</th><th>Email</th><th>City</th><th>Status</th><th>Current next contact</th></tr></thead> | |||||
| <tbody> | |||||
| <% Dim ti, row | |||||
| For ti = 0 To toLog.Count - 1 | |||||
| Set row = toLog(ti) %> | |||||
| <tr> | |||||
| <td><a href="/businesses/<%= row("id") %>" class="text-decoration-none"><%= H(row("name")) %></a></td> | |||||
| <td><%= H(row("email")) %></td> | |||||
| <td><%= H(row("city")) %></td> | |||||
| <td><span class="badge <%= Businesses_StatusClass(row("status")) %>"><%= H(Businesses_StatusLabel(row("status"))) %></span></td> | |||||
| <td><%= Date_Fmt(row("next_action_date")) %></td> | |||||
| </tr> | |||||
| <% Next %> | |||||
| </tbody> | |||||
| </table> | |||||
| </div> | |||||
| <% End If %> | |||||
| </div> | |||||
| </div> | |||||
| <% If skipped.Count > 0 Then %> | |||||
| <div class="alert alert-secondary"> | |||||
| <strong>Skipped (removed from the list):</strong> | |||||
| <% Dim si | |||||
| For si = 0 To skipped.Count - 1 %> | |||||
| <div><%= H(skipped(si)("name")) %> <<%= H(skipped(si)("email")) %>></div> | |||||
| <% Next %> | |||||
| </div> | |||||
| <% End If %> | |||||
| <% If missing.Count > 0 Or parsed("invalid").Count > 0 Then %> | |||||
| <div class="alert alert-warning"> | |||||
| <% If missing.Count > 0 Then %> | |||||
| <strong>No business has these emails (<%= missing.Count %>):</strong> | |||||
| <div class="small"><% Dim mi | |||||
| For mi = 0 To missing.Count - 1 | |||||
| If mi > 0 Then Response.Write ", " | |||||
| Response.Write H(missing(mi)) | |||||
| Next %></div> | |||||
| <% End If %> | |||||
| <% If parsed("invalid").Count > 0 Then %> | |||||
| <div class="<% If missing.Count > 0 Then Response.Write "mt-2" %>"><strong>Not valid email addresses (ignored):</strong></div> | |||||
| <div class="small"><% Dim ii | |||||
| For ii = 0 To parsed("invalid").Count - 1 | |||||
| If ii > 0 Then Response.Write ", " | |||||
| Response.Write H(parsed("invalid")(ii)) | |||||
| Next %></div> | |||||
| <% End If %> | |||||
| </div> | |||||
| <% End If %> | |||||
| <form method="post" action="/bulk-log/run" class="d-flex gap-2"> | |||||
| <input type="hidden" name="csrf" value="<%= H(csrf) %>" /> | |||||
| <input type="hidden" name="emails" value="<%= H(emailsJoined) %>" /> | |||||
| <input type="hidden" name="method" value="<%= H(v("method")) %>" /> | |||||
| <input type="hidden" name="outcome" value="<%= H(v("outcome")) %>" /> | |||||
| <input type="hidden" name="contacted_at" value="<%= H(v("contacted_at")) %>" /> | |||||
| <input type="hidden" name="follow_up_date" value="<%= H(v("follow_up_date")) %>" /> | |||||
| <input type="hidden" name="notes" value="<%= H(v("notes")) %>" /> | |||||
| <% If toLog.Count > 0 Then %> | |||||
| <button type="submit" class="btn btn-primary btn-sm"><i class="bi bi-check2"></i> Log contact on <%= toLog.Count %> business<% If toLog.Count <> 1 Then Response.Write "es" %></button> | |||||
| <% End If %> | |||||
| <a href="/bulk-log" class="btn btn-outline-secondary btn-sm">Back</a> | |||||
| </form> | |||||
| @@ -65,6 +65,9 @@ If Len(pageTitle) = 0 Then pageTitle = "Classic ASP Starter Template" | |||||
| <li class="nav-item"> | <li class="nav-item"> | ||||
| <a class="nav-link" href="/businesses">Businesses</a> | <a class="nav-link" href="/businesses">Businesses</a> | ||||
| </li> | </li> | ||||
| <li class="nav-item"> | |||||
| <a class="nav-link" href="/bulk-log">Bulk log</a> | |||||
| </li> | |||||
| <li class="nav-item"> | <li class="nav-item"> | ||||
| <a class="nav-link" href="/industries">Industries</a> | <a class="nav-link" href="/industries">Industries</a> | ||||
| </li> | </li> | ||||
| @@ -22,6 +22,7 @@ Class ControllerRegistry_Class | |||||
| RegisterController "areascontroller" | RegisterController "areascontroller" | ||||
| RegisterController "exportcontroller" | RegisterController "exportcontroller" | ||||
| RegisterController "dashboardcontroller" | RegisterController "dashboardcontroller" | ||||
| RegisterController "bulklogcontroller" | |||||
| End Sub | End Sub | ||||
| Private Sub Class_Terminate() | Private Sub Class_Terminate() | ||||
| @@ -21,6 +21,11 @@ | |||||
| router.AddRoute "POST", "/businesses/:id/remove", "BusinessesController", "Remove" | router.AddRoute "POST", "/businesses/:id/remove", "BusinessesController", "Remove" | ||||
| router.AddRoute "POST", "/businesses/:id/restore", "BusinessesController", "Restore" | router.AddRoute "POST", "/businesses/:id/restore", "BusinessesController", "Restore" | ||||
| ' Bulk log a contact by pasted email addresses | |||||
| router.AddRoute "GET", "/bulk-log", "BulkLogController", "Index" | |||||
| router.AddRoute "POST", "/bulk-log/preview", "BulkLogController", "Preview" | |||||
| router.AddRoute "POST", "/bulk-log/run", "BulkLogController", "Run" | |||||
| ' Industries | ' Industries | ||||
| router.AddRoute "GET", "/industries", "IndustriesController", "Index" | router.AddRoute "GET", "/industries", "IndustriesController", "Index" | ||||
| router.AddRoute "POST", "/industries/create", "IndustriesController", "Create" | router.AddRoute "POST", "/industries/create", "IndustriesController", "Create" | ||||
Powered by TurnKey Linux.