Push commits through:
Minor optimizations. Make the properties editors use Global Variables for hidden fields. Finalize banners - they should now be persistent & work properly. Update build scripts. Make DockBarTwo.py use Global Variables for the dockbars' hidden fields. Add logging for LinkScope version. Update GlobalVariables.py - adjust the hidden fields & the fields to avoid parsing. Update Settings Object to have version & TOR file location parameters. Add npmjs search.
This commit is contained in:
@@ -3,9 +3,10 @@
|
||||
import random
|
||||
|
||||
non_string_fields = ('Icon', 'Child UIDs')
|
||||
hidden_fields = ('uid', 'Date Last Edited', 'Child UIDs')
|
||||
hidden_fields = ('uid', 'Date Last Edited', 'Child UIDs', 'Canvas Banner', 'Entity Type')
|
||||
hidden_fields_dockbars = ('uid', 'Child UIDs', 'Canvas Banner', 'Icon')
|
||||
meta_fields = ('Child UIDs',)
|
||||
avoid_parsing_fields = ('uid', 'Date Last Edited', 'Child UIDs', 'Icon')
|
||||
avoid_parsing_fields = ('uid', 'Date Last Edited', 'Child UIDs', 'Icon', 'Canvas Banner')
|
||||
|
||||
# Closer to the top means more recent.
|
||||
user_agents = {'Chrome': {'Windows': ('Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 '
|
||||
|
||||
@@ -22,6 +22,7 @@ from PySide6.QtWebEngineWidgets import QWebEngineView
|
||||
|
||||
from Core.Interface import Entity, Stylesheets
|
||||
from Core.ResourceHandler import RichNotesEditor
|
||||
from Core.GlobalVariables import hidden_fields
|
||||
|
||||
|
||||
class WorkspaceWidget(QtWidgets.QWidget):
|
||||
@@ -193,6 +194,9 @@ class TabbedPane(QtWidgets.QTabWidget):
|
||||
self.tabsNotesDict = {}
|
||||
self.previousTab = None
|
||||
|
||||
self.allBanners = {bannerID: bannerPath
|
||||
for bannerID, bannerPath in self.mainWindow.RESOURCEHANDLER.banners.items()}
|
||||
|
||||
self.currentChanged.connect(self.currentTabChangedListener)
|
||||
|
||||
def getCanvasDBPath(self):
|
||||
@@ -1164,7 +1168,10 @@ class CanvasView(QtWidgets.QGraphicsView):
|
||||
def clearBanners(self) -> None:
|
||||
selectedEntities = [item for item in self.scene().selectedItems() if isinstance(item, Entity.BaseNode)]
|
||||
for entity in selectedEntities:
|
||||
entityJson = self.tabbedPane.mainWindow.LENTDB.getEntity(entity.uid)
|
||||
entityJson['Canvas Banner'] = ''
|
||||
entity.updateBanner(True, None)
|
||||
self.tabbedPane.mainWindow.LENTDB.addEntity(entityJson, updateTimeline=False)
|
||||
|
||||
def setBanners(self) -> None:
|
||||
selectedEntities = [item for item in self.scene().selectedItems() if isinstance(item, Entity.BaseNode)]
|
||||
@@ -1172,23 +1179,18 @@ class CanvasView(QtWidgets.QGraphicsView):
|
||||
self.tabbedPane.mainWindow.MESSAGEHANDLER.warning('Need to select at least one Entity to set its banner.',
|
||||
popUp=True)
|
||||
return
|
||||
allBanners = {bannerID: bannerPath
|
||||
for bannerID, bannerPath in self.tabbedPane.mainWindow.RESOURCEHANDLER.banners.items()}
|
||||
bannerDialog = BannerSelector(allBanners)
|
||||
bannerDialog = BannerSelector(self.tabbedPane.allBanners)
|
||||
if bannerDialog.exec():
|
||||
try:
|
||||
selectedBannerItem = bannerDialog.bannerIconContainer.selectedItems()[0]
|
||||
bannerPathStr = allBanners[selectedBannerItem.text()]
|
||||
with open(bannerPathStr, 'rb') as bannerFile:
|
||||
bannerByteArray = QtCore.QByteArray(bannerFile.read())
|
||||
# This following line will throw IndexError if no banner is selected.
|
||||
selectedBannerItem = bannerDialog.bannerIconContainer.selectedItems()[0].text()
|
||||
self.scene().bannerDrawHelper(selectedEntities, selectedBannerItem)
|
||||
for entity in selectedEntities:
|
||||
entity.updateBanner(False, bannerByteArray)
|
||||
entityJson = self.tabbedPane.mainWindow.LENTDB.getEntity(entity.uid)
|
||||
entityJson['Canvas Banner'] = selectedBannerItem
|
||||
self.tabbedPane.mainWindow.LENTDB.addEntity(entityJson, updateTimeline=False)
|
||||
except IndexError:
|
||||
self.tabbedPane.mainWindow.MESSAGEHANDLER.warning('No Banner selected.', popUp=True)
|
||||
except FileNotFoundError:
|
||||
self.tabbedPane.mainWindow.MESSAGEHANDLER.error('Banner Icon not found in filesystem.',
|
||||
popUp=True,
|
||||
exc_info=False)
|
||||
|
||||
def takePictureOfView(self, justViewport: bool = True, transparentBackground: bool = False) -> QtGui.QImage:
|
||||
# Need to set size and format of pic before using it.
|
||||
@@ -1279,6 +1281,48 @@ class CanvasScene(QtWidgets.QGraphicsScene):
|
||||
# Re-Center the Label
|
||||
item.updateLabel(item.labelItem.text())
|
||||
|
||||
def bannerDrawHelper(self, entities: list, bannerName: str = None) -> None:
|
||||
"""
|
||||
If we are given a banner name, try to set each canvas entity banner to the banner with the given name.
|
||||
If not, then instead we get the banner that each entity is already assigned, and make sure it's drawn.
|
||||
"""
|
||||
if bannerName:
|
||||
try:
|
||||
bannerPathStr = self.parent().allBanners[bannerName]
|
||||
with open(bannerPathStr, 'rb') as bannerFile:
|
||||
bannerByteArray = QtCore.QByteArray(bannerFile.read())
|
||||
for entity in entities:
|
||||
entity.updateBanner(False, bannerByteArray)
|
||||
except FileNotFoundError:
|
||||
self.parent().mainWindow.MESSAGEHANDLER.error(f'Banner Icon not found in filesystem: {bannerName}',
|
||||
popUp=True,
|
||||
exc_info=False)
|
||||
except KeyError:
|
||||
self.parent().mainWindow.MESSAGEHANDLER.warning(f'Invalid Banner: {bannerName}', popUp=True)
|
||||
else:
|
||||
notFoundBanners = set()
|
||||
for entity in entities:
|
||||
try:
|
||||
entityJson = self.parent().mainWindow.LENTDB.getEntity(entity.uid)
|
||||
bannerPathStr = self.parent().allBanners.get(entityJson.get('Canvas Banner', ''), '')
|
||||
if not bannerPathStr:
|
||||
entity.updateBanner(True, None)
|
||||
else:
|
||||
with open(bannerPathStr, 'rb') as bannerFile:
|
||||
bannerByteArray = QtCore.QByteArray(bannerFile.read())
|
||||
entity.updateBanner(False, bannerByteArray)
|
||||
except FileNotFoundError:
|
||||
if bannerName not in notFoundBanners:
|
||||
self.parent().mainWindow.MESSAGEHANDLER.error(f'Banner Icon not found in filesystem: '
|
||||
f'{bannerName}',
|
||||
popUp=True,
|
||||
exc_info=False)
|
||||
notFoundBanners.add(bannerName)
|
||||
except KeyError:
|
||||
if bannerName not in notFoundBanners:
|
||||
self.parent().mainWindow.MESSAGEHANDLER.warning(f'Invalid Banner: {bannerName}', popUp=True)
|
||||
notFoundBanners.add(bannerName)
|
||||
|
||||
# Redefined so that the BaseConnector items are not considered.
|
||||
def itemsBoundingRect(self) -> QtCore.QRectF:
|
||||
try:
|
||||
@@ -1301,6 +1345,7 @@ class CanvasScene(QtWidgets.QGraphicsScene):
|
||||
def addNodeToScene(self, item, x=0, y=0) -> None:
|
||||
self.nodesDict[item.uid] = item
|
||||
self.addItem(item)
|
||||
self.bannerDrawHelper([item])
|
||||
item.setPos(QtCore.QPointF(x, y))
|
||||
self.parent().mainWindow.MESSAGEHANDLER.info(f'Added node: {str(item.uid)} | {item.labelItem.toPlainText()}')
|
||||
|
||||
@@ -2033,7 +2078,7 @@ class PropertiesEditor(QtWidgets.QDialog):
|
||||
|
||||
self.itemProperties = QtWidgets.QFormLayout()
|
||||
for key in objectJson:
|
||||
if key in ('uid', 'Entity Type', 'Date Last Edited', 'Child UIDs'):
|
||||
if key in hidden_fields:
|
||||
continue
|
||||
keyField = QtWidgets.QLabel(key)
|
||||
if key == "Notes":
|
||||
|
||||
@@ -5,6 +5,7 @@ import magic
|
||||
from PySide6 import QtWidgets, QtCore, QtGui
|
||||
from Core.Interface import Stylesheets
|
||||
from Core.ResourceHandler import MinSizeStackedLayout, RichNotesEditor
|
||||
from Core.GlobalVariables import hidden_fields_dockbars
|
||||
|
||||
|
||||
class DockBarTwo(QtWidgets.QDockWidget):
|
||||
@@ -276,7 +277,7 @@ class EntityDetails(QtWidgets.QWidget):
|
||||
return
|
||||
rowCount = 0
|
||||
for key in jsonDict:
|
||||
if key in ["uid", "Child UIDs", "Icon"]:
|
||||
if key in hidden_fields_dockbars:
|
||||
continue
|
||||
elif key == "Notes":
|
||||
notesTextArea = RichNotesEditor(self, jsonDict[key], False)
|
||||
|
||||
@@ -36,7 +36,6 @@ class BaseNode(QGraphicsItemGroup):
|
||||
self.labelItem = QGraphicsTextItem('')
|
||||
# Have to do it this way; directly assigning stuff does not work due to how PySide6 works.
|
||||
labelDocument = self.labelItem.document()
|
||||
labelDocument.setTextWidth(280)
|
||||
textOption = labelDocument.defaultTextOption()
|
||||
textOption.setWrapMode(QtGui.QTextOption.WrapMode.WrapAtWordBoundaryOrAnywhere)
|
||||
textOption.setAlignment(QtCore.Qt.AlignmentFlag.AlignHCenter)
|
||||
@@ -57,10 +56,10 @@ class BaseNode(QGraphicsItemGroup):
|
||||
if brush is not None:
|
||||
self.labelItem.setDefaultTextColor(brush.color())
|
||||
|
||||
self.labelItem.setPos(self.iconItem.x() - 120, self.iconItem.y() + 45)
|
||||
self.updateLabel(primaryAttribute)
|
||||
|
||||
self.bannerIconItem.setPos(self.iconItem.x() + 15, self.iconItem.y() - 9)
|
||||
self.bannerIconItem.setZValue(10)
|
||||
|
||||
self.uid = uid
|
||||
self.setFlag(QGraphicsItem.ItemIsMovable, True)
|
||||
@@ -72,12 +71,12 @@ class BaseNode(QGraphicsItemGroup):
|
||||
self.connectors = []
|
||||
|
||||
def updateLabel(self, newText: str = '') -> None:
|
||||
if not isinstance(newText, str):
|
||||
newText = newText
|
||||
if newText != '':
|
||||
if len(newText) > 50:
|
||||
newText = f"{newText[:47]}..."
|
||||
self.labelItem.setPlainText(newText)
|
||||
self.labelItem.document().adjustSize()
|
||||
self.labelItem.setPos(self.iconItem.x() + 20 - (self.labelItem.textWidth() / 2), self.iconItem.y() + 45)
|
||||
|
||||
def updateBanner(self, bannerHidden: bool = True, bannerGraphic: QtCore.QByteArray = None) -> None:
|
||||
if bannerHidden: # No icon visible
|
||||
|
||||
@@ -2732,12 +2732,13 @@ class ImportBrowserTabsThread(QtCore.QThread):
|
||||
first = True
|
||||
for browserEntry in browserTab['entries']:
|
||||
url = browserEntry['url']
|
||||
title = browserEntry.get('title', '')
|
||||
if not url.startswith('about:'):
|
||||
if first:
|
||||
tabsToOpen.append((url, browserEntry['title'], True))
|
||||
tabsToOpen.append((url, title, True))
|
||||
first = False
|
||||
else:
|
||||
tabsToOpen.append((url, browserEntry['title'], False))
|
||||
tabsToOpen.append((url, title, False))
|
||||
else:
|
||||
browserEntry = browserTab['entries'][browserTab['index'] - 1]
|
||||
url = browserEntry['url']
|
||||
@@ -2833,7 +2834,9 @@ class ImportBrowserTabsThread(QtCore.QThread):
|
||||
[screenshotEntity,
|
||||
{len(returnResults) + 1: {'Resolution': 'Screenshot of Tab',
|
||||
'Notes': ''}}])
|
||||
|
||||
else:
|
||||
newEntity = [{'Phrase': actualURL,
|
||||
'Entity Type': 'Phrase'}]
|
||||
if len(tabToOpen) == 3:
|
||||
if historyMark != -1 and not tabToOpen[2]:
|
||||
newEntity.append({historyMark: {'Resolution': 'Next Page'}})
|
||||
|
||||
38
Core/Resolutions/Core/NPMJSSearch.py
Normal file
38
Core/Resolutions/Core/NPMJSSearch.py
Normal file
@@ -0,0 +1,38 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
|
||||
class NPMJSSearch:
|
||||
name = "Find NPM organization"
|
||||
category = "Online Identity"
|
||||
description = "Find a collective's npmjs organization page."
|
||||
originTypes = {'Phrase', 'Company', 'Organization'}
|
||||
resultTypes = {'Website'}
|
||||
|
||||
parameters = {}
|
||||
|
||||
def resolution(self, entityJsonList, parameters):
|
||||
import requests
|
||||
|
||||
headers = {'User-Agent': 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:108.0) Gecko/20100101 Firefox/108.0'}
|
||||
url_base = 'https://www.npmjs.com/org/'
|
||||
|
||||
returnResults = []
|
||||
|
||||
for entity in entityJsonList:
|
||||
primaryField = entity[list(entity)[1]].lower()
|
||||
|
||||
string_checks = set()
|
||||
string_checks.add(''.join(primaryField.split(' ')))
|
||||
string_checks.add('_'.join(primaryField.split(' ')))
|
||||
string_checks.add('-'.join(primaryField.split(' ')))
|
||||
|
||||
for check in string_checks:
|
||||
check_url = url_base + check
|
||||
request = requests.head(check_url, headers=headers)
|
||||
if request.status_code == 200:
|
||||
returnResults.append([{'URL': check_url,
|
||||
'Entity Type': 'Website'},
|
||||
{entity['uid']: {'Resolution': 'NPMJS Org',
|
||||
'Notes': ''}}])
|
||||
|
||||
return returnResults
|
||||
@@ -22,6 +22,8 @@ class SettingsObject(dict):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.setValue("Program/BaseDir", "Unset") # dirname(abspath(getsourcefile(lambda:0))) + "/../" )
|
||||
self.setValue("Program/Version", "v1.4.5")
|
||||
self.setValue("Program/DarkWeb/TORProfileLocation", "")
|
||||
self.setValue("Program/GraphLayout", "dot")
|
||||
self.setValue("Program/Graphics/EntityTextFontType", "Mono")
|
||||
self.setValue("Program/Graphics/EntityTextFontSize", "11")
|
||||
@@ -36,7 +38,7 @@ class SettingsObject(dict):
|
||||
self.setValue("Project/BaseDir", "")
|
||||
self.setValue("Project/FilesDir", "")
|
||||
# For any entity with a Path variable, this dictates whether a copy of the original is made or whether a
|
||||
# symlink is created. Symlinks require special permissions or developer mode in Windows however.
|
||||
# symlink is created. Symlinks however require special permissions or developer mode in Windows.
|
||||
# To ensure that the software works out-of-the-box on all platforms, the default is set to 'Copy'.
|
||||
self.setValue("Project/Symlink or Copy Materials", "Copy") # Values are 'Copy' or 'Symlink'.
|
||||
self.setValue("Project/Resolution Result Grouping Threshold", "15")
|
||||
|
||||
13
LinkScope.py
13
LinkScope.py
@@ -1882,7 +1882,8 @@ class MainWindow(QtWidgets.QMainWindow):
|
||||
self.setMenuBar(MenuBar.MenuBar(self))
|
||||
|
||||
# Set the main window title and show it to the user.
|
||||
self.setWindowTitle("LinkScope 1.4.0 - " + self.SETTINGS.get('Project/Name', 'Untitled'))
|
||||
self.setWindowTitle(f"LinkScope {self.SETTINGS.get('Program/Version', 'VU')}"
|
||||
f" - {self.SETTINGS.get('Project/Name', 'Untitled')}")
|
||||
iconPath = Path(self.SETTINGS.get('Program/BaseDir')) / 'Icon.ico'
|
||||
appIcon = QtGui.QIcon(str(iconPath))
|
||||
self.setWindowIcon(appIcon)
|
||||
@@ -1959,10 +1960,11 @@ class MainWindow(QtWidgets.QMainWindow):
|
||||
self.SETTINGS.setValue("Project/FilesDir", str(projectDir / "Project Files"))
|
||||
|
||||
self.MESSAGEHANDLER = MessageHandler.MessageHandler(self)
|
||||
self.RESOURCEHANDLER = ResourceHandler.ResourceHandler(self, self.MESSAGEHANDLER)
|
||||
self.dockbarThree = DockBarThree.DockBarThree(self)
|
||||
self.LENTDB = EntityDB.EntitiesDB(self, self.MESSAGEHANDLER, self.RESOURCEHANDLER)
|
||||
self.MESSAGEHANDLER.info(f'Starting LinkScope Client, Version {self.SETTINGS.value("Program/Version", "N/A")}')
|
||||
self.URLMANAGER = URLManager.URLManager(self)
|
||||
self.dockbarThree = DockBarThree.DockBarThree(self)
|
||||
self.RESOURCEHANDLER = ResourceHandler.ResourceHandler(self, self.MESSAGEHANDLER)
|
||||
self.LENTDB = EntityDB.EntitiesDB(self, self.MESSAGEHANDLER, self.RESOURCEHANDLER)
|
||||
self.RESOLUTIONMANAGER = ResolutionManager.ResolutionManager(self, self.MESSAGEHANDLER)
|
||||
self.FCOM = FrontendCommunicationsHandler.CommunicationsHandler(self)
|
||||
self.LQLWIZARD = LQLQueryBuilder(self)
|
||||
@@ -2998,7 +3000,8 @@ class ProgramEditDialog(QtWidgets.QDialog):
|
||||
for setting in self.settings:
|
||||
if setting.startswith('Program/'):
|
||||
keyName = setting.split('Program/', 1)[1]
|
||||
if keyName != "BaseDir" and len(setting.split('/')) == 2: # Don't allow users to mess with these.
|
||||
# Don't allow users to mess with sensitive settings.
|
||||
if keyName not in ["BaseDir", "Version"] and len(setting.split('/')) == 2:
|
||||
# A bit redundant to do it this way, but it'll be cleaner if / when more settings are added.
|
||||
if keyName == "GraphLayout":
|
||||
settingSingleChoice = SettingsEditSingleChoice(['dot', 'sfdp', 'neato'],
|
||||
|
||||
4
build.sh
4
build.sh
@@ -11,7 +11,7 @@ python${PYTHON_VER} -m venv buildEnv
|
||||
source buildEnv/bin/activate
|
||||
|
||||
# orderedset package installed for compile time performance.
|
||||
python${PYTHON_VER} -m pip install --upgrade wheel pip nuitka orderedset
|
||||
python${PYTHON_VER} -m pip install --upgrade wheel pip nuitka ordered-set
|
||||
|
||||
python${PYTHON_VER} -m pip cache purge
|
||||
|
||||
@@ -26,7 +26,7 @@ FIREFOX_VER=$(python -c "from pathlib import Path;x=Path(\"buildEnv/lib/python${
|
||||
|
||||
python${PYTHON_VER} -m nuitka --follow-imports --standalone --noinclude-pytest-mode=nofollow \
|
||||
--noinclude-setuptools-mode=nofollow --noinclude-custom-mode=setuptools:error --noinclude-IPython-mode=nofollow \
|
||||
--enable-plugin=pyside6 --enable-plugin=numpy --enable-plugin=trio --assume-yes-for-downloads --remove-output \
|
||||
--noinclude-unittest-mode=nofollow --enable-plugin=pyside6 --enable-plugin=trio --assume-yes-for-downloads --remove-output \
|
||||
--disable-console --include-data-dir="Resources=Resources" --include-plugin-directory=Modules --include-package=Core \
|
||||
--include-data-dir="Core/Entities=Core/Entities" --include-data-dir="Core/Resolutions/Core=Core/Resolutions/Core" \
|
||||
--include-data-dir="Modules=Modules" --warn-unusual-code --show-modules --include-data-files="Icon.ico=Icon.ico" \
|
||||
|
||||
@@ -14,7 +14,7 @@ python -m venv buildEnv
|
||||
|
||||
call buildEnv\Scripts\activate.bat
|
||||
|
||||
python -m pip install --upgrade wheel pip nuitka orderedset
|
||||
python -m pip install --upgrade wheel pip nuitka ordered-set
|
||||
|
||||
python -m pip cache purge
|
||||
|
||||
@@ -34,7 +34,7 @@ FOR /F "usebackq" %%L in (`python -c "from pathlib import Path;x=Path('buildEnv\
|
||||
|
||||
python -m nuitka --follow-imports --standalone --noinclude-pytest-mode=nofollow --noinclude-setuptools-mode=nofollow ^
|
||||
--noinclude-custom-mode=setuptools:error --noinclude-IPython-mode=nofollow --enable-plugin=pyside6 ^
|
||||
--enable-plugin=numpy --enable-plugin=trio --assume-yes-for-downloads --remove-output --disable-console ^
|
||||
--noinclude-unittest-mode=nofollow --enable-plugin=trio --assume-yes-for-downloads --remove-output --disable-console ^
|
||||
--include-data-dir="Resources=Resources" --include-plugin-directory=Modules --include-package=Core ^
|
||||
--include-data-dir="Core\Entities=Core\Entities" --include-data-dir="Core\Resolutions\Core=Core\Resolutions\Core" ^
|
||||
--include-data-dir="Modules=Modules" --warn-unusual-code --show-modules --include-data-files="Icon.ico=Icon.ico" ^
|
||||
|
||||
Reference in New Issue
Block a user