Files
shopdb/search.asp
cproudlock b0c60ebbd5 Fix search routing for PCs and WinRM remote update script
- search.asp: Route to displaypc.asp for PCs (machinetypeid 33-43 or
  machinetypeid 1 with hostname), displaymachine.asp for equipment
- search.asp: Add hostname search capability for PCs
- Update-ShopfloorPCs-Remote.ps1: Fix hashtable conversion bug that caused
  empty API errors - pass $result directly instead of PSObject.Properties

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-04 17:18:45 -05:00

1111 lines
43 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"/>
<meta name="description" content=""/>
<meta name="author" content=""/>
<!-- loader-->
<link href="assets/css/pace.min.css" rel="stylesheet"/>
<!--favicon-->
<link rel="icon" href="assets/images/favicon.ico" type="image/x-icon">
<!-- simplebar CSS-->
<link href="assets/plugins/simplebar/css/simplebar.css" rel="stylesheet"/>
<!-- Bootstrap core CSS-->
<link href="assets/css/bootstrap.min.css" rel="stylesheet"/>
<!-- animate CSS-->
<link href="assets/css/animat2e.css" rel="stylesheet" type="text/css"/>
<!-- Icons CSS-->
<link href="assets/css/icons.css" rel="stylesheet" type="text/css"/>
<!-- Sidebar CSS-->
<link href="assets/css/sidebar-menu.css" rel="stylesheet"/>
<!-- Custom Style-->
<link href="assets/css/app-style.css" rel="stylesheet"/>
<!--#include file="./includes/sql.asp"-->
<!--#include file="./includes/validation.asp"-->
<!--#include file="./includes/encoding.asp"-->
<!--#include file="./includes/error_handler.asp"-->
<!--#include file="./includes/db_helpers.asp"-->
</head>
<%
' Initialize error handling
Call InitializeErrorHandling("search.asp")
theme = Request.Cookies("theme")
IF theme = "" THEN
theme="bg-theme1"
END IF
' Get and validate search parameter
search = Request.Querystring("search")
search = Replace(search, "+", " ")
search = Trim(search)
' Keep a display version with spaces for user-facing messages
Dim searchDisplay
searchDisplay = search
' Get highlight parameter for shared links
Dim highlightId, isShared
highlightId = Request.QueryString("highlight")
isShared = (Request.QueryString("shared") = "1")
' Basic validation - prevent empty searches
If search = "" Then
Call HandleValidationError("default.asp", "REQUIRED_FIELD")
End If
' Length validation - prevent excessively long inputs
If Len(search) > 200 Then
Call HandleValidationError("default.asp", "INVALID_INPUT")
End If
' ------------------------------- Search For Machine Number or Hostname ----------------------------------------------------------
' Also search by hostname for PCs, and get machinetypeid from models table to determine PC vs Equipment
strSQL = "SELECT m.machineid, m.hostname, mo.machinetypeid FROM machines m " & _
"LEFT JOIN models mo ON m.modelnumberid = mo.modelnumberid " & _
"WHERE (m.machinenumber = ? OR m.alias LIKE ? OR m.hostname = ?) AND m.isactive = 1"
Set rs = ExecuteParameterizedQuery(objConn, strSQL, Array(search, "%" & search & "%", search))
If Not rs.EOF Then
machineid = rs("machineid")
Dim searchMachTypeId, searchIsPC, searchRedirectPage, searchHostname
searchMachTypeId = 0
If Not IsNull(rs("machinetypeid")) Then searchMachTypeId = CLng(rs("machinetypeid"))
searchHostname = rs("hostname") & ""
' PC if machinetypeid 33-43, OR if machinetypeid is 1 (default) and has a hostname
searchIsPC = (searchMachTypeId >= 33 And searchMachTypeId <= 43) Or (searchMachTypeId = 1 And searchHostname <> "")
rs.Close
Set rs = Nothing
Call CleanupResources()
' Route to appropriate detail page based on PC vs Equipment
If searchIsPC Then
Response.Redirect "./displaypc.asp?machineid=" & machineid
Else
Response.Redirect "./displaymachine.asp?machineid=" & machineid
End If
Response.End
End If
rs.Close
Set rs = Nothing
'----------------------------- Search for Applications directly ----------------------------------------------------------------
' If only one application matches, redirect directly to it
' Normalize search by removing common punctuation for better matching
Dim normalizedSearch
normalizedSearch = search
normalizedSearch = Replace(normalizedSearch, "-", " ")
normalizedSearch = Replace(normalizedSearch, "_", " ")
normalizedSearch = Replace(normalizedSearch, " ", " ") ' Double spaces to single
' Search using REPLACE to normalize app names in the same way
strSQL = "SELECT appid FROM applications WHERE " & _
"REPLACE(REPLACE(REPLACE(appname, '-', ' '), '_', ' '), ' ', ' ') LIKE ? " & _
"AND isactive = 1"
Set rs = ExecuteParameterizedQuery(objConn, strSQL, Array("%" & normalizedSearch & "%"))
Dim appMatchCount, firstAppId
appMatchCount = 0
If Not rs.EOF Then
firstAppId = rs("appid")
appMatchCount = 1
rs.MoveNext
If Not rs.EOF Then
appMatchCount = 2 ' More than one match
End If
End If
rs.Close
Set rs = Nothing
' Disabled auto-redirect - always show results in search.asp for consistency
' If appMatchCount = 1 Then
' Call CleanupResources()
' Response.Redirect "./displaytopic.asp?appid=" & firstAppId
' Response.End
' End If
search = Replace(search, " ", "+")
'----------------------------- Search for CSF Printer Name ----------------------------------------------------------------
If (Left(LCase(search), 3)) = "csf" And Len(search) = 5 Then
strSQL = "SELECT printerid FROM printers WHERE printercsfname = ?"
Set rs = ExecuteParameterizedQuery(objConn, strSQL, Array(search))
If Not rs.EOF Then
printerid = rs("printerid")
rs.Close
Set rs = Nothing
Call CleanupResources()
Response.Redirect "./displayprinter.asp?printerid=" & printerid
Response.End
End If
rs.Close
Set rs = Nothing
End If
'----------------------------- Search for Printer Serial Number (exact match) ----------------------------------------------------------------
strSQL = "SELECT printerid FROM printers WHERE serialnumber = ? AND isactive = 1"
Set rs = ExecuteParameterizedQuery(objConn, strSQL, Array(search))
If Not rs.EOF Then
printerid = rs("printerid")
rs.Close
Set rs = Nothing
Call CleanupResources()
Response.Redirect "./displayprinter.asp?printerid=" & printerid
Response.End
End If
rs.Close
Set rs = Nothing
'----------------------------- Search for Printer FQDN (exact match) ----------------------------------------------------------------
strSQL = "SELECT printerid FROM printers WHERE fqdn = ? AND isactive = 1"
Set rs = ExecuteParameterizedQuery(objConn, strSQL, Array(search))
If Not rs.EOF Then
printerid = rs("printerid")
rs.Close
Set rs = Nothing
Call CleanupResources()
Response.Redirect "./displayprinter.asp?printerid=" & printerid
Response.End
End If
rs.Close
Set rs = Nothing
'----------------------------------Is this an IP address -------------------------------------------------------------------------------------
' Validate IP address format before query
If ValidateIPAddress(search) Then
strSQL = "SELECT subnetid FROM subnets WHERE subnets.isactive = 1 AND INET_ATON(?) >= ipstart AND INET_ATON(?) <= ipend"
Set rs = ExecuteParameterizedQuery(objConn, strSQL, Array(search, search))
If Not rs.EOF Then
subnetid = rs("subnetid")
rs.Close
Set rs = Nothing
Call CleanupResources()
Response.Redirect "./displaysubnet.asp?subnetid=" & subnetid & "&search=" & Server.URLEncode(search)
Response.End
End If
rs.Close
Set rs = Nothing
End If
' ------------------------------- Is this an SSO redirect to displayprofile.asp ----------------------------------------------------------
If IsNumeric(search) And Len(search) = 9 Then
Call CleanupResources()
Response.Redirect "./displayprofile.asp?sso=" & search
Response.End
End If
' ----------------------------------Is this a SNOW ticket-----------------------------------
If (Left(LCase(search), 5)) = "geinc" Then
Call CleanupResources()
Response.Write("<script type='text/javascript'>")
Response.Write("window.open('https://geit.service-now.com/now/nav/ui/search/0f8b85d0c7922010099a308dc7c2606a/params/search-term/" & JavaScriptEncode(search) & "/global-search-data-config-id/c861cea2c7022010099a308dc7c26041/back-button-label/IT4IT%20Homepage/search-context/now%2Fnav%2Fui');")
Response.Write("</script>")
Response.End
End If
If (Left(LCase(search), 5)) = "gechg" Then
Call CleanupResources()
Response.Write("<script type='text/javascript'>")
Response.Write("window.open('https://geit.service-now.com/now/nav/ui/search/0f8b85d0c7922010099a308dc7c2606a/params/search-term/" & JavaScriptEncode(search) & "/global-search-data-config-id/c861cea2c7022010099a308dc7c26041/back-button-label/IT4IT%20Homepage/search-context/now%2Fnav%2Fui');")
Response.Write("</script>")
Response.End
End If
If (Left(LCase(search), 5)) = "gerit" Then
Call CleanupResources()
Response.Write("<script type='text/javascript'>")
Response.Write("window.open('https://geit.service-now.com/now/nav/ui/search/0f8b85d0c7922010099a308dc7c2606a/params/search-term/" & JavaScriptEncode(search) & "/global-search-data-config-id/c861cea2c7022010099a308dc7c26041/back-button-label/IT4IT%20Homepage/search-context/now%2Fnav%2Fui');")
Response.Write("</script>")
Response.End
End If
If (Left(LCase(search), 5)) = "gesct" Then
Call CleanupResources()
Response.Write("<script type='text/javascript'>")
Response.Write("window.open('https://geit.service-now.com/now/nav/ui/search/0f8b85d0c7922010099a308dc7c2606a/params/search-term/" & JavaScriptEncode(search) & "/global-search-data-config-id/c861cea2c7022010099a308dc7c26041/back-button-label/IT4IT%20Homepage/search-context/now%2Fnav%2Fui');")
Response.Write("</script>")
Response.End
End If
%>
<body class="bg-theme <%Response.Write(theme)%>">
<!-- start loader -->
<div id="pageloader-overlay" class="visible incoming"><div class="loader-wrapper-outer"><div class="loader-wrapper-inner" ><div class="loader"></div></div></div></div>
<!-- end loader -->
<!-- Start wrapper-->
<div id="wrapper">
<!--#include file="./includes/leftsidebar.asp"-->
<!--Start topbar header-->
<!--#include file="./includes/topbarheader.asp"-->
<!--End topbar header-->
<div class="clearfix"></div>
<div class="content-wrapper">
<div class="row">
<div class="col-lg-auto">
<div class="card">
<div class="card-body">
<ul class="nav nav-tabs nav-tabs-primary top-icon nav-justified">
<li class="nav-item">
<a href="javascript:void();" data-target="#additem" data-toggle="pill" class="nav-link"><i class="icon-note"></i> <span class="hidden-xs">Add to the Knowledge Base</span></a>
</li>
</ul>
<div class="tab-content p-3">
<div class="tab-pane" id="additem">
<form method="post" action="./addlink_direct.asp">
<div class="form-group row">
<label class="col-lg-3 col-form-label form-control-label">Description:</label>
<div class="col-lg-9">
<input class="form-control" type="text" name="shortdescription" placeholder="Description">
</div>
</div>
<div class="form-group row">
<label class="col-lg-3 col-form-label form-control-label">Link:</label>
<div class="col-lg-9">
<input class="form-control" type="text" name="linkurl" placeholder="KB Link">
</div>
</div>
<div class="form-group row">
<label class="col-lg-3 col-form-label form-control-label">Key Words</label>
<div class="col-lg-9">
<input class="form-control" type="text" name="keywords" placeholder="Key Words">
</div>
</div>
<div class="form-group row">
<label class="col-lg-3 col-form-label form-control-label">Topic:</label>
<div class="col-lg-9">
<select name="appid" class="btn btn-light px-3">
<option value=''>-------------</option>
<%
strSQL = "SELECT appid, appname FROM applications WHERE isactive = 1 ORDER BY appname ASC"
Set rs2 = ExecuteParameterizedQuery(objConn, strSQL, Array())
Do While Not rs2.EOF
Response.Write("<option value='" & rs2("appid") & "'>" & Server.HTMLEncode(rs2("appname")) & "</option>")
rs2.MoveNext
Loop
rs2.Close
Set rs2 = Nothing
%>
</select>
</div>
<div class="form-group row">
<div class="col-lg-4">
<BR>
<input type="submit" class="btn btn-primary" value="Add to KB">
</div>
</div>
</div>
</form>
</div>
<h5 class="card-title">Search Results</h5>
<%
' Show educational banner if this is a shared link
If isShared And search <> "" Then
%>
<div style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: #fff; padding: 15px 20px; border-radius: 8px; margin-bottom: 20px; box-shadow: 0 4px 15px rgba(0,0,0,0.2);">
<div style="display: flex; align-items: center;">
<i class="zmdi zmdi-info" style="font-size: 28px; margin-right: 15px; opacity: 0.9;"></i>
<div>
<strong style="font-size: 15px; display: block; margin-bottom: 5px;">💡 Self-Service Tip</strong>
<span style="font-size: 13px; opacity: 0.95;">This result was found by searching for "<strong><%=Server.HTMLEncode(searchDisplay)%></strong>".</span>
</div>
</div>
</div>
<%
End If
%>
<%
' --------------------------------- Search: Applications + KB Articles -----------------------------------------------------------
' Query applications and KB articles separately, then combine and sort in ASP
' Use searchDisplay (which has spaces) for queries, search (with +) is for URLs
Dim searchTerm
searchTerm = searchDisplay
' First, get applications using FULLTEXT search for consistent relevance scoring
Dim rsApps, appResults(), appCount
appCount = 0
' Use FULLTEXT search with MATCH...AGAINST in BOOLEAN MODE (same as KB articles)
' This provides consistent scoring methodology across apps and KB articles
' NOTE: FULLTEXT has ft_min_word_len=4, so short words like "PC", "3", "of" won't match
strSQL = "SELECT appid, appname, " & _
"MATCH (appname) AGAINST (? IN BOOLEAN MODE) AS relevance " & _
"FROM applications " & _
"WHERE MATCH (appname) AGAINST (? IN BOOLEAN MODE) " & _
" AND isactive = 1 " & _
"ORDER BY relevance DESC LIMIT 10"
Set rsApps = ExecuteParameterizedQuery(objConn, strSQL, Array(searchTerm, searchTerm))
' Store app results in array
ReDim appResults(100, 3) ' [index, 0=type, 1=data, 2=relevance, 3=id]
' DEBUG: Output app count for troubleshooting
Response.Write("<!-- DEBUG: Starting app count: " & appCount & " -->")
Do While Not rsApps.EOF And appCount < 10
' DEBUG: Show each app found
Response.Write("<!-- DEBUG: Found app - ID: " & rsApps("appid") & ", Name: " & rsApps("appname") & ", FULLTEXT Relevance: " & rsApps("relevance") & " -->")
appResults(appCount, 0) = "app"
appResults(appCount, 1) = rsApps("appid") & "|" & rsApps("appname")
' Multiply by 10 to boost apps relative to KB articles (tunable)
appResults(appCount, 2) = CDbl(rsApps("relevance")) * 10
appResults(appCount, 3) = rsApps("appid")
appCount = appCount + 1
rsApps.MoveNext
Loop
rsApps.Close
Set rsApps = Nothing
' DEBUG: Output final app count
Response.Write("<!-- DEBUG: Total apps found after FULLTEXT: " & appCount & " -->")
' Fallback: If FULLTEXT returned no results (common with short words < 4 chars),
' use LIKE-based search with pattern matching
If appCount = 0 Then
Response.Write("<!-- DEBUG: No FULLTEXT results, trying LIKE fallback for short terms -->")
strSQL = "SELECT appid, appname, " & _
"CASE " & _
" WHEN LOWER(appname) = LOWER(?) THEN 25 " & _
" WHEN LOWER(appname) LIKE CONCAT(LOWER(?), '%') THEN 20 " & _
" WHEN LOWER(appname) LIKE CONCAT('%', LOWER(?), '%') THEN 15 " & _
" ELSE 10 " & _
"END AS relevance " & _
"FROM applications " & _
"WHERE LOWER(appname) LIKE CONCAT('%', LOWER(?), '%') " & _
" AND isactive = 1 " & _
"ORDER BY relevance DESC LIMIT 10"
Set rsApps = ExecuteParameterizedQuery(objConn, strSQL, Array(searchTerm, searchTerm, searchTerm, searchTerm))
Do While Not rsApps.EOF And appCount < 10
Response.Write("<!-- DEBUG: Found app via LIKE - ID: " & rsApps("appid") & ", Name: " & rsApps("appname") & ", LIKE Relevance: " & rsApps("relevance") & " -->")
appResults(appCount, 0) = "app"
appResults(appCount, 1) = rsApps("appid") & "|" & rsApps("appname")
appResults(appCount, 2) = CDbl(rsApps("relevance"))
appResults(appCount, 3) = rsApps("appid")
appCount = appCount + 1
rsApps.MoveNext
Loop
rsApps.Close
Set rsApps = Nothing
Response.Write("<!-- DEBUG: Total apps after LIKE fallback: " & appCount & " -->")
End If
' Now get KB articles (removed linkURL from FULLTEXT search - no index exists)
strSQL = "SELECT kb.linkid, kb.shortdescription, kb.linkurl, kb.clicks, app.appid, app.appname, " & _
"MATCH (kb.shortdescription) AGAINST (? IN BOOLEAN MODE) AS relevance1, " & _
"MATCH (kb.keywords) AGAINST (? IN BOOLEAN MODE) AS relevance2, " & _
"MATCH (app.appname) AGAINST (? IN BOOLEAN MODE) AS relevance3 " & _
"FROM knowledgebase kb " & _
"INNER JOIN applications app ON kb.appid = app.appid " & _
"WHERE kb.isactive = 1 " & _
"AND (MATCH (kb.shortdescription) AGAINST (? IN BOOLEAN MODE) > 0 " & _
" OR MATCH (kb.keywords) AGAINST (? IN BOOLEAN MODE) > 0) " & _
"ORDER BY (relevance3 * 3) + (relevance1 * 2) + (relevance2 * 2.5) + (kb.clicks * .1) DESC " & _
"LIMIT 20"
Set rs = ExecuteParameterizedQuery(objConn, strSQL, Array(searchTerm, searchTerm, searchTerm, searchTerm, searchTerm))
' Store KB results in same array
Dim totalCount
totalCount = appCount
Do While Not rs.EOF And totalCount < 100
appResults(totalCount, 0) = "kb"
appResults(totalCount, 1) = rs("linkid") & "|" & rs("appid") & "|" & rs("appname") & "|" & rs("shortdescription") & "|" & rs("linkurl") & "|" & rs("clicks")
' relevance3 = app.appname, relevance1 = description, relevance2 = keywords
appResults(totalCount, 2) = (rs("relevance3") * 3) + (rs("relevance1") * 2) + (rs("relevance2") * 2.5) + (rs("clicks") * 0.1)
appResults(totalCount, 3) = rs("linkid")
totalCount = totalCount + 1
rs.MoveNext
Loop
rs.Close
Set rs = Nothing
' Now get Notifications
Dim rsNotif
strSQL = "SELECT notificationid, notification, starttime, endtime, ticketnumber, link, " & _
"MATCH (notification) AGAINST (? IN BOOLEAN MODE) AS textrelevance, " & _
"CASE " & _
" WHEN NOW() BETWEEN starttime AND endtime THEN 3.0 " & _
" WHEN NOW() < starttime THEN 2.0 " & _
" WHEN DATEDIFF(NOW(), endtime) <= 7 THEN 1.5 " & _
" WHEN DATEDIFF(NOW(), endtime) <= 30 THEN 0.5 " & _
" ELSE 0.1 " & _
"END AS timefactor " & _
"FROM notifications " & _
"WHERE MATCH (notification) AGAINST (? IN BOOLEAN MODE) " & _
" AND isactive = 1 " & _
"ORDER BY (textrelevance * timefactor) DESC LIMIT 5"
Set rsNotif = ExecuteParameterizedQuery(objConn, strSQL, Array(searchTerm, searchTerm))
' DEBUG: Output notification count
Response.Write("<!-- DEBUG: Checking notifications for search term -->")
Do While Not rsNotif.EOF And totalCount < 100
Dim finalNotifScore
finalNotifScore = CDbl(rsNotif("textrelevance")) * CDbl(rsNotif("timefactor")) * 8
Response.Write("<!-- DEBUG: Found notification - ID: " & rsNotif("notificationid") & ", Text: " & rsNotif("notification") & ", TextRelevance: " & rsNotif("textrelevance") & ", TimeFactor: " & rsNotif("timefactor") & ", FinalScore: " & finalNotifScore & " -->")
appResults(totalCount, 0) = "notification"
appResults(totalCount, 1) = rsNotif("notificationid") & "|" & rsNotif("notification") & "|" & rsNotif("starttime") & "|" & rsNotif("endtime") & "|" & rsNotif("ticketnumber") & "|" & rsNotif("link")
' Score = TextRelevance × TimeFactor × 8 (base multiplier)
appResults(totalCount, 2) = finalNotifScore
appResults(totalCount, 3) = rsNotif("notificationid")
totalCount = totalCount + 1
rsNotif.MoveNext
Loop
rsNotif.Close
Set rsNotif = Nothing
' Now get Machines (by machine number, alias, notes, machine type, or vendor)
' NOTE: machinetypeid is now sourced from models table (models.machinetypeid) not machines table
' Also search by hostname for PCs
Dim rsMachines
strSQL = "SELECT m.machineid, m.machinenumber, m.alias, m.hostname, mo.machinetypeid, mt.machinetype " & _
"FROM machines m " & _
"LEFT JOIN models mo ON m.modelnumberid = mo.modelnumberid " & _
"LEFT JOIN machinetypes mt ON mo.machinetypeid = mt.machinetypeid " & _
"LEFT JOIN vendors v ON mo.vendorid = v.vendorid " & _
"WHERE (m.machinenumber LIKE ? OR m.alias LIKE ? OR m.machinenotes LIKE ? OR mt.machinetype LIKE ? OR v.vendor LIKE ? OR m.hostname LIKE ?) " & _
" AND m.isactive = 1 " & _
"LIMIT 10"
Set rsMachines = ExecuteParameterizedQuery(objConn, strSQL, Array("%" & searchTerm & "%", "%" & searchTerm & "%", "%" & searchTerm & "%", "%" & searchTerm & "%", "%" & searchTerm & "%", "%" & searchTerm & "%"))
Response.Write("<!-- DEBUG: Checking machines for search term -->")
Do While Not rsMachines.EOF And totalCount < 100
' Determine display text - use hostname for PCs, machinenumber for equipment
' PCs have machinetypeid 33-43, OR machinetypeid 1 (default) with a hostname
Dim isPC, machTypeId, loopHostname
machTypeId = 0
If Not IsNull(rsMachines("machinetypeid")) Then machTypeId = CLng(rsMachines("machinetypeid"))
loopHostname = rsMachines("hostname") & ""
isPC = (machTypeId >= 33 And machTypeId <= 43) Or (machTypeId = 1 And loopHostname <> "")
If isPC Then
machineDispText = rsMachines("hostname") & ""
If machineDispText = "" Then machineDispText = rsMachines("machinenumber") & ""
Else
machineDispText = rsMachines("machinenumber") & ""
If Not IsNull(rsMachines("alias")) And rsMachines("alias") <> "" Then
machineDispText = machineDispText & " (" & rsMachines("alias") & ")"
End If
End If
Response.Write("<!-- DEBUG: Found machine - ID: " & rsMachines("machineid") & ", Number: " & rsMachines("machinenumber") & ", TypeID: " & machTypeId & ", IsPC: " & isPC & " -->")
appResults(totalCount, 0) = "machine"
' Include isPC flag in data: machineid|machineDisplay|machinetype|isPC
appResults(totalCount, 1) = rsMachines("machineid") & "|" & machineDispText & "|" & rsMachines("machinetype") & "|" & CStr(isPC)
' Score of 15 for machines (moderate priority)
appResults(totalCount, 2) = 15
appResults(totalCount, 3) = rsMachines("machineid")
totalCount = totalCount + 1
rsMachines.MoveNext
Loop
rsMachines.Close
Set rsMachines = Nothing
' Now get Printers (by CSF name, Windows name, serial number, or model)
Dim rsPrinters
strSQL = "SELECT p.printerid, p.printercsfname, p.printerwindowsname, p.serialnumber, m.modelnumber " & _
"FROM printers p " & _
"LEFT JOIN models m ON p.modelid = m.modelnumberid " & _
"WHERE (p.printercsfname LIKE ? OR p.printerwindowsname LIKE ? OR p.serialnumber LIKE ? OR m.modelnumber LIKE ?) " & _
" AND p.isactive = 1 " & _
"LIMIT 10"
Set rsPrinters = ExecuteParameterizedQuery(objConn, strSQL, Array("%" & searchTerm & "%", "%" & searchTerm & "%", "%" & searchTerm & "%", "%" & searchTerm & "%"))
Response.Write("<!-- DEBUG: Checking printers for search term -->")
Do While Not rsPrinters.EOF And totalCount < 100
printerDispText = rsPrinters("printercsfname")
If Not IsNull(rsPrinters("printerwindowsname")) And rsPrinters("printerwindowsname") <> "" Then
printerDispText = printerDispText & " - " & rsPrinters("printerwindowsname")
End If
Response.Write("<!-- DEBUG: Found printer - ID: " & rsPrinters("printerid") & ", CSF: " & rsPrinters("printercsfname") & " -->")
appResults(totalCount, 0) = "printer"
appResults(totalCount, 1) = rsPrinters("printerid") & "|" & printerDispText & "|" & rsPrinters("modelnumber") & "|" & rsPrinters("serialnumber")
' Score of 15 for printers (moderate priority)
appResults(totalCount, 2) = 15
appResults(totalCount, 3) = rsPrinters("printerid")
totalCount = totalCount + 1
rsPrinters.MoveNext
Loop
rsPrinters.Close
Set rsPrinters = Nothing
' Sort combined results by relevance (bubble sort is fine for small arrays)
Dim i, j, tempType, tempData, tempRel, tempId
For i = 0 To totalCount - 1
For j = i + 1 To totalCount - 1
If appResults(j, 2) > appResults(i, 2) Then
' Swap
tempType = appResults(i, 0)
tempData = appResults(i, 1)
tempRel = appResults(i, 2)
tempId = appResults(i, 3)
appResults(i, 0) = appResults(j, 0)
appResults(i, 1) = appResults(j, 1)
appResults(i, 2) = appResults(j, 2)
appResults(i, 3) = appResults(j, 3)
appResults(j, 0) = tempType
appResults(j, 1) = tempData
appResults(j, 2) = tempRel
appResults(j, 3) = tempId
End If
Next
Next
%>
<table class="table table-hover" id="searchResults">
<thead>
<tr>
<th scope="col"><i class='zmdi zmdi-edit'></i></th>
<th scope="col">Topic</th>
<th scope="col">Description</th>
<th scope="col">Relevance</th>
<th scope="col"><i class='zmdi zmdi-share'></i></th>
</tr>
</thead>
<tbody>
<%
' Display combined and sorted results from array
Dim cleanSearch
cleanSearch = Replace(searchDisplay, "+", " ")
If totalCount > 0 Then
Dim k, resultType, relevanceScore, badgeClass, rowStyle, rowId
Dim dataFields, appid, appname, linkid, shortdesc, linkurl, clicks
Dim notifId, notifText, notifStart, notifEnd, notifTicket, notifLink, notifDisplay
Dim machineId, machineDispText, machineType
Dim printerId, printerDispText, printerModel, printerSerial, printerDetailText
For k = 0 To totalCount - 1
resultType = appResults(k, 0)
relevanceScore = CDbl(appResults(k, 2))
' Determine badge color based on score
If relevanceScore >= 200 Then
badgeClass = "badge-success"
ElseIf relevanceScore >= 100 Then
badgeClass = "badge-primary"
ElseIf relevanceScore >= 50 Then
badgeClass = "badge-warning"
Else
badgeClass = "badge-secondary"
End If
' Parse data based on result type
dataFields = Split(appResults(k, 1), "|")
If resultType = "app" Then
' App format: appid|appname
appid = dataFields(0)
appname = dataFields(1)
rowId = "app-result-" & appid
If highlightId <> "" And CStr(appid) = CStr(highlightId) And Request.QueryString("type") = "app" Then
rowStyle = " class='highlighted-result'"
Else
rowStyle = ""
End If
Response.Write("<tr id='" & rowId & "'" & rowStyle & ">")
' Column 1: Empty for apps
Response.Write("<td>&nbsp;</td>")
' Column 2: App name with icon (matches sidebar)
Response.Write("<td><a href='./displayapplication.asp?appid=" & appid & "'><i class='zmdi zmdi-apps text-secondary' style='margin-right: 5px;'></i>" & Server.HTMLEncode(appname) & "</a></td>")
' Column 3: View details link
Response.Write("<td><a href='./displayapplication.asp?appid=" & appid & "'>View Application Details</a></td>")
' Column 4: Relevance badge
Response.Write("<td><span class='badge " & badgeClass & "'><i class='zmdi zmdi-trending-up'></i> " & FormatNumber(relevanceScore, 1) & "</span></td>")
' Column 5: Share button
Response.Write("<td><button class='btn btn-sm btn-outline-primary share-btn' onclick='shareApplicationResult(" & appid & ", """ & JavaScriptEncode(cleanSearch) & """)' title='Share this application'><i class='zmdi zmdi-share'></i></button></td>")
ElseIf resultType = "kb" Then
' KB format: linkid|appid|appname|shortdescription|linkurl|clicks
linkid = dataFields(0)
appid = dataFields(1)
appname = dataFields(2)
shortdesc = dataFields(3)
linkurl = dataFields(4)
clicks = dataFields(5)
rowId = "result-" & linkid
If highlightId <> "" And CStr(linkid) = CStr(highlightId) And Request.QueryString("type") <> "app" Then
rowStyle = " class='highlighted-result'"
Else
rowStyle = ""
End If
Response.Write("<tr id='" & rowId & "'" & rowStyle & ">")
' Column 1: Edit icon
Response.Write("<td><a href='./editlink.asp?linkid=" & linkid & "'><i class='zmdi zmdi-edit' title='Edit This Link'></i></a></td>")
' Column 2: App name with KB icon (matches sidebar)
Response.Write("<td><a href='./displaytopic.asp?appid=" & appid & "' title='Display all " & Server.HTMLEncode(appname) & " related items'><i class='zmdi zmdi-book text-primary' style='margin-right: 5px;'></i>" & Server.HTMLEncode(appname) & "</a></td>")
' Column 3: Description
Response.Write("<td><a href='./clickcounter.asp?linkid=" & linkid & "' target='_blank' title='Click to follow this link: " & Server.HTMLEncode(linkurl) & "'>" & Server.HTMLEncode(shortdesc) & "</a></td>")
' Column 4: Relevance badge
Response.Write("<td><span class='badge " & badgeClass & "'><i class='zmdi zmdi-trending-up'></i> " & FormatNumber(relevanceScore, 1) & "</span></td>")
' Column 5: Share button
Response.Write("<td><button class='btn btn-sm btn-outline-primary share-btn' onclick='shareResult(" & linkid & ", """ & JavaScriptEncode(cleanSearch) & """)' title='Share this result'><i class='zmdi zmdi-share'></i></button></td>")
ElseIf resultType = "notification" Then
' Notification format: notificationid|notification|starttime|endtime|ticketnumber|link
notifId = dataFields(0)
notifText = dataFields(1)
notifStart = dataFields(2)
notifEnd = dataFields(3)
notifTicket = dataFields(4)
notifLink = dataFields(5)
rowId = "notification-result-" & notifId
If highlightId <> "" And CStr(notifId) = CStr(highlightId) And Request.QueryString("type") = "notification" Then
rowStyle = " class='highlighted-result'"
Else
rowStyle = ""
End If
Response.Write("<tr id='" & rowId & "'" & rowStyle & ">")
' Column 1: Empty for notifications
Response.Write("<td>&nbsp;</td>")
' Column 2: Notification icon - link to notifications page (matches KB article pattern)
Response.Write("<td><a href='./displaynotifications.asp'><i class='zmdi zmdi-notifications-none text-success' style='margin-right: 5px;'></i>Notification</a></td>")
' Column 3: Notification text links to edit (like clicking event in calendar)
notifDisplay = Server.HTMLEncode(notifText)
If notifTicket <> "" Then
notifDisplay = notifDisplay & " <span class='badge badge-info'>" & Server.HTMLEncode(notifTicket) & "</span>"
End If
Response.Write("<td><a href='./editnotification.asp?notificationid=" & notifId & "'>" & notifDisplay & "</a></td>")
' Column 4: Relevance badge
Response.Write("<td><span class='badge " & badgeClass & "'><i class='zmdi zmdi-trending-up'></i> " & FormatNumber(relevanceScore, 1) & "</span></td>")
' Column 5: Share button
Response.Write("<td><button class='btn btn-sm btn-outline-primary share-btn' onclick='shareNotificationResult(" & notifId & ", """ & JavaScriptEncode(cleanSearch) & """)' title='Share this notification'><i class='zmdi zmdi-share'></i></button></td>")
ElseIf resultType = "machine" Then
' Machine format: machineid|machineDisplay|machinetype|isPC
machineId = dataFields(0)
machineDispText = dataFields(1)
machineType = dataFields(2)
Dim machineIsPC, machineDetailPage, machineListPage, machineIcon, machineIconColor, machineLabel
machineIsPC = (UBound(dataFields) >= 3 And dataFields(3) = "True")
' Set page, icon and label based on PC vs Equipment
If machineIsPC Then
machineDetailPage = "displaypc.asp"
machineListPage = "displaypcs.asp"
machineIcon = "zmdi-desktop-windows"
machineIconColor = "text-primary"
machineLabel = "PC"
Else
machineDetailPage = "displaymachine.asp"
machineListPage = "displaymachines.asp"
machineIcon = "zmdi-reader"
machineIconColor = "text-warning"
machineLabel = "Machine"
End If
rowId = "machine-result-" & machineId
If highlightId <> "" And CStr(machineId) = CStr(highlightId) And Request.QueryString("type") = "machine" Then
rowStyle = " class='highlighted-result'"
Else
rowStyle = ""
End If
Response.Write("<tr id='" & rowId & "'" & rowStyle & ">")
' Column 1: Empty for machines
Response.Write("<td>&nbsp;</td>")
' Column 2: Machine/PC icon - link to appropriate list page (matches sidebar)
Response.Write("<td><a href='./" & machineListPage & "'><i class='zmdi " & machineIcon & " " & machineIconColor & "' style='margin-right: 5px;'></i>" & machineLabel & "</a></td>")
' Column 3: Machine number/alias or hostname links to appropriate detail page
Response.Write("<td><a href='./" & machineDetailPage & "?machineid=" & machineId & "'>" & Server.HTMLEncode(machineDispText) & " <span class='badge badge-info'>" & Server.HTMLEncode(machineType) & "</span></a></td>")
' Column 4: Relevance badge
Response.Write("<td><span class='badge " & badgeClass & "'><i class='zmdi zmdi-trending-up'></i> " & FormatNumber(relevanceScore, 1) & "</span></td>")
' Column 5: Share button
Response.Write("<td><button class='btn btn-sm btn-outline-primary share-btn' onclick='shareMachineResult(" & machineId & ", """ & JavaScriptEncode(cleanSearch) & """)' title='Share this " & LCase(machineLabel) & "'><i class='zmdi zmdi-share'></i></button></td>")
ElseIf resultType = "printer" Then
' Printer format: printerid|printerDisplay|modelnumber|serialnumber
printerId = dataFields(0)
printerDispText = dataFields(1)
printerModel = dataFields(2)
printerSerial = dataFields(3)
rowId = "printer-result-" & printerId
If highlightId <> "" And CStr(printerId) = CStr(highlightId) And Request.QueryString("type") = "printer" Then
rowStyle = " class='highlighted-result'"
Else
rowStyle = ""
End If
Response.Write("<tr id='" & rowId & "'" & rowStyle & ">")
' Column 1: Empty for printers
Response.Write("<td>&nbsp;</td>")
' Column 2: Printer icon - link to printers page (matches sidebar)
Response.Write("<td><a href='./displayprinters.asp'><i class='zmdi zmdi-print text-info' style='margin-right: 5px;'></i>Printer</a></td>")
' Column 3: Printer name links to printer detail
printerDetailText = Server.HTMLEncode(printerDispText)
If Not IsNull(printerModel) And printerModel <> "" Then
printerDetailText = printerDetailText & " <span class='badge badge-info'>" & Server.HTMLEncode(printerModel) & "</span>"
End If
Response.Write("<td><a href='./displayprinter.asp?printerid=" & printerId & "'>" & printerDetailText & "</a></td>")
' Column 4: Relevance badge
Response.Write("<td><span class='badge " & badgeClass & "'><i class='zmdi zmdi-trending-up'></i> " & FormatNumber(relevanceScore, 1) & "</span></td>")
' Column 5: Share button
Response.Write("<td><button class='btn btn-sm btn-outline-primary share-btn' onclick='sharePrinterResult(" & printerId & ", """ & JavaScriptEncode(cleanSearch) & """)' title='Share this printer'><i class='zmdi zmdi-share'></i></button></td>")
End If
Response.Write("</tr>")
Next
End If
Call CleanupResources()
%>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div><!--End Row-->
<!-- End container-fluid-->
</div><!--End content-wrapper-->
<!--Start Back To Top Button-->
<a href="javaScript:void();" class="back-to-top"><i class="fa fa-angle-double-up"></i> </a>
<!--End Back To Top Button-->
<!--Start footer-->
<footer class="footer">
</div>
</footer>
<!--End footer-->
</div><!--End wrapper-->
<!-- Bootstrap core JavaScript-->
<script src="assets/js/jquery.min.js"></script>
<script src="assets/js/popper.min.js"></script>
<script src="assets/js/bootstrap.min.js"></script>
<!-- simplebar js -->
<script src="assets/plugins/simplebar/js/simplebar.js"></script>
<!-- sidebar-menu js -->
<script src="assets/js/sidebar-menu.js"></script>
<!-- Custom scripts -->
<script src="assets/js/app-script.js"></script>
<style>
/* Highlighter effect - works in both dark and light themes */
.highlighted-result {
background-color: rgba(255, 235, 59, 0.25) !important; /* Semi-transparent yellow */
box-shadow: inset 4px 0 0 0 rgba(255, 235, 59, 0.8), inset 0 0 0 2px rgba(255, 235, 59, 0.4); /* Left marker + border glow */
transition: all 0.3s ease;
}
.share-btn {
padding: 6px 12px;
font-size: 16px;
transition: all 0.2s;
background: rgba(102, 126, 234, 0.15);
border: 1px solid rgba(102, 126, 234, 0.3);
border-radius: 4px;
color: #667eea;
}
.share-btn:hover {
transform: scale(1.05);
background: rgba(102, 126, 234, 0.25);
border-color: rgba(102, 126, 234, 0.5);
color: #667eea;
box-shadow: 0 2px 8px rgba(102, 126, 234, 0.3);
}
.share-btn:active {
transform: scale(0.98);
}
.share-btn i {
font-size: 16px;
color: #667eea;
}
#shareToast {
position: fixed;
top: 80px;
right: 20px;
z-index: 9999;
min-width: 300px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
padding: 15px 20px;
border-radius: 8px;
box-shadow: 0 4px 20px rgba(0,0,0,0.3);
display: none;
animation: slideIn 0.3s ease-out;
}
@keyframes slideIn {
from {
transform: translateX(400px);
opacity: 0;
}
to {
transform: translateX(0);
opacity: 1;
}
}
@keyframes slideOut {
from {
transform: translateX(0);
opacity: 1;
}
to {
transform: translateX(400px);
opacity: 0;
}
}
</style>
<div id="shareToast">
<div style="display: flex; align-items: center;">
<i class="zmdi zmdi-check-circle" style="font-size: 24px; margin-right: 12px;"></i>
<div>
<strong style="display: block; margin-bottom: 5px;">Link Copied!</strong>
<span style="font-size: 13px; opacity: 0.9;">This link will show the search term and highlight the result</span>
</div>
</div>
</div>
<script>
function shareApplicationResult(appId, searchTerm) {
// Share link to search results with application highlighted
var baseUrl = window.location.origin + window.location.pathname;
var cleanSearch = searchTerm.replace(/\s+/g, '+');
var shareUrl = baseUrl + '?search=' + cleanSearch + '&highlight=' + appId + '&type=app&shared=1';
// Copy to clipboard
if (navigator.clipboard && navigator.clipboard.writeText) {
navigator.clipboard.writeText(shareUrl).then(function() {
showToast();
}).catch(function(err) {
// Fallback for older browsers
copyToClipboardFallback(shareUrl);
});
} else {
// Fallback for older browsers
copyToClipboardFallback(shareUrl);
}
}
function shareResult(linkId, searchTerm) {
// Build share URL with clean encoding
// Use + for spaces to keep URLs short and readable
var baseUrl = window.location.origin + window.location.pathname;
var cleanSearch = searchTerm.replace(/\s+/g, '+'); // Replace spaces with +
var shareUrl = baseUrl + '?search=' + cleanSearch + '&highlight=' + linkId + '&shared=1';
// Copy to clipboard
if (navigator.clipboard && navigator.clipboard.writeText) {
navigator.clipboard.writeText(shareUrl).then(function() {
showToast();
}).catch(function(err) {
// Fallback for older browsers
copyToClipboardFallback(shareUrl);
});
} else {
// Fallback for older browsers
copyToClipboardFallback(shareUrl);
}
}
function shareNotificationResult(notifId, searchTerm) {
// Share link to search results with notification highlighted
var baseUrl = window.location.origin + window.location.pathname;
var cleanSearch = searchTerm.replace(/\s+/g, '+');
var shareUrl = baseUrl + '?search=' + cleanSearch + '&highlight=' + notifId + '&type=notification&shared=1';
// Copy to clipboard
if (navigator.clipboard && navigator.clipboard.writeText) {
navigator.clipboard.writeText(shareUrl).then(function() {
showToast();
}).catch(function(err) {
copyToClipboardFallback(shareUrl);
});
} else {
copyToClipboardFallback(shareUrl);
}
}
function shareMachineResult(machineId, searchTerm) {
// Share link to search results with machine highlighted
var baseUrl = window.location.origin + window.location.pathname;
var cleanSearch = searchTerm.replace(/\s+/g, '+');
var shareUrl = baseUrl + '?search=' + cleanSearch + '&highlight=' + machineId + '&type=machine&shared=1';
// Copy to clipboard
if (navigator.clipboard && navigator.clipboard.writeText) {
navigator.clipboard.writeText(shareUrl).then(function() {
showToast();
}).catch(function(err) {
copyToClipboardFallback(shareUrl);
});
} else {
copyToClipboardFallback(shareUrl);
}
}
function sharePrinterResult(printerId, searchTerm) {
// Share link to search results with printer highlighted
var baseUrl = window.location.origin + window.location.pathname;
var cleanSearch = searchTerm.replace(/\s+/g, '+');
var shareUrl = baseUrl + '?search=' + cleanSearch + '&highlight=' + printerId + '&type=printer&shared=1';
// Copy to clipboard
if (navigator.clipboard && navigator.clipboard.writeText) {
navigator.clipboard.writeText(shareUrl).then(function() {
showToast();
}).catch(function(err) {
copyToClipboardFallback(shareUrl);
});
} else {
copyToClipboardFallback(shareUrl);
}
}
function copyToClipboardFallback(text) {
var textArea = document.createElement("textarea");
textArea.value = text;
textArea.style.position = "fixed";
textArea.style.left = "-999999px";
document.body.appendChild(textArea);
textArea.focus();
textArea.select();
try {
document.execCommand('copy');
showToast();
} catch (err) {
alert('Unable to copy link. Please copy manually: ' + text);
}
document.body.removeChild(textArea);
}
function showToast() {
var toast = document.getElementById('shareToast');
toast.style.display = 'block';
toast.style.animation = 'slideIn 0.3s ease-out';
setTimeout(function() {
toast.style.animation = 'slideOut 0.3s ease-out';
setTimeout(function() {
toast.style.display = 'none';
}, 300);
}, 3500);
}
// Scroll to highlighted result on page load
window.addEventListener('DOMContentLoaded', function() {
var urlParams = new URLSearchParams(window.location.search);
var highlightId = urlParams.get('highlight');
var highlightType = urlParams.get('type');
if (highlightId) {
var elementId;
// Build the correct element ID based on type
if (highlightType === 'app') {
elementId = 'app-result-' + highlightId;
} else if (highlightType === 'notification') {
elementId = 'notification-result-' + highlightId;
} else if (highlightType === 'machine') {
elementId = 'machine-result-' + highlightId;
} else if (highlightType === 'printer') {
elementId = 'printer-result-' + highlightId;
} else {
// Default to KB article format
elementId = 'result-' + highlightId;
}
var element = document.getElementById(elementId);
if (element) {
// Scroll to the highlighted result (highlight is applied via CSS class 'highlighted-result')
setTimeout(function() {
element.scrollIntoView({ behavior: 'smooth', block: 'center' });
}, 500);
}
}
});
</script>
</body>
</html>