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:
AccentuSoft
2023-01-24 14:10:46 +02:00
parent f535f5f9aa
commit 1c8452be08
10 changed files with 125 additions and 33 deletions

View File

@@ -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 '

View File

@@ -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":

View File

@@ -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)

View File

@@ -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

View File

@@ -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'}})

View 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

View File

@@ -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")

View File

@@ -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'],

View File

@@ -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" \

View File

@@ -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" ^