Fix search_ and player_location related bugs

On the search pages use the search_location and player_location, in
addition to the base_url, to associate filter and index entries, such
that the filter actually works.

Fix the .index file location for projects with a search_location. As
this is now consistent, we happen to fix a crash that happened when
changing the search_location.

New config option:

    deny_bespoke_templates
         Indexers may use the "template" attribute in the video node of
         an .hmml file to set a bespoke template for that entry,
         superseding any configured player_template. Setting
         "deny_bespoke_templates" to true prevents this.
This commit is contained in:
Matt Mascarenhas 2020-05-17 02:04:26 +01:00
parent e2ea8fdaf3
commit 9dfdc117be
3 changed files with 144 additions and 96 deletions

View File

@ -23,7 +23,7 @@ typedef struct
version CINERA_APP_VERSION = {
.Major = 0,
.Minor = 7,
.Patch = 7
.Patch = 8
};
#include <stdarg.h> // NOTE(matt): varargs
@ -1306,6 +1306,7 @@ video node of the entries for which they should be credited."
{ "db_location", "Absolute file path where the database file resides. If you run multiple instances of Cinera on the same machine, please ensure this db_location differs between them." },
{ "default_medium", "The ID of a medium (see also medium) which will be the default for the project. May be overridden by setting the medium in the video node of an HMML file." },
{ "deny", "See allow." },
{ "deny_bespoke_templates", "Indexers may use the \"template\" attribute in the video node of an .hmml file to set a bespoke template for that entry, superseding any configured player_template. Setting \"deny_bespoke_templates\" to true prevents this." },
{ "genre", "This is a setting for the future. We currently only support the \"video\" genre, which is the default." },
{ "global_search_dir", "Absolute directory path, where the global search page will be located, collating the search pages of all projects in the Cinera instance." },
{ "global_search_template", "Path of a HTML template file relative to the global_templates_dir from which the global search page will be generated." },
@ -1425,6 +1426,7 @@ typedef enum
IDENT_DB_LOCATION,
IDENT_DEFAULT_MEDIUM,
IDENT_DENY,
IDENT_DENY_BESPOKE_TEMPLATES,
IDENT_GENRE,
IDENT_GLOBAL_SEARCH_DIR,
IDENT_GLOBAL_SEARCH_TEMPLATE,
@ -4640,16 +4642,16 @@ PrintAssetsBlock_(db_block_assets *B, int LineNumber)
}
char *
ConstructDirectoryPath(db_header_project *P, string *PageLocation, string *EntryOutput)
ConstructDirectoryPath(string *BaseDir, string *PageLocation, string *EntryOutput)
{
char *Result = 0;
if(P)
if(BaseDir)
{
ExtendString0(&Result, Wrap0i(P->BaseDir, sizeof(P->BaseDir)));
ExtendString0(&Result, *BaseDir);
}
if(PageLocation && PageLocation->Length > 0)
{
if(P)
if(BaseDir)
{
ExtendString0(&Result, Wrap0("/"));
}
@ -4665,18 +4667,17 @@ ConstructDirectoryPath(db_header_project *P, string *PageLocation, string *Entry
}
char *
ConstructHTMLIndexFilePath(db_header_project *P, string *PageLocation, string *EntryOutput)
ConstructHTMLIndexFilePath(string *BaseDir, string *PageLocation, string *EntryOutput)
{
char *Result = ConstructDirectoryPath(P, PageLocation, EntryOutput);
char *Result = ConstructDirectoryPath(BaseDir, PageLocation, EntryOutput);
ExtendString0(&Result, Wrap0("/index.html"));
return Result;
}
rc
ReadSearchPageIntoBuffer(db_header_project *P, file *File)
ReadSearchPageIntoBuffer(file *File, string *BaseDir, string *SearchLocation)
{
string SearchLocationL = Wrap0i(P->SearchLocation, sizeof(P->SearchLocation));
File->Path = ConstructHTMLIndexFilePath(P, &SearchLocationL, 0);
File->Path = ConstructHTMLIndexFilePath(BaseDir, SearchLocation, 0);
return ReadFileIntoBuffer(File);
}
@ -4690,11 +4691,9 @@ ReadGlobalSearchPageIntoBuffer(file *File)
}
rc
ReadPlayerPageIntoBuffer(db_header_project *P, file *File, db_entry *Entry)
ReadPlayerPageIntoBuffer(file *File, string BaseDir, string PlayerLocation, string OutputLocation)
{
string EntryOutput = Wrap0i(Entry->OutputLocation, sizeof(Entry->OutputLocation));
string PlayerLocationL = Wrap0i(P->PlayerLocation, sizeof(P->PlayerLocation));
File->Path = ConstructHTMLIndexFilePath(P, &PlayerLocationL, &EntryOutput);
File->Path = ConstructHTMLIndexFilePath(&BaseDir, &PlayerLocation, &OutputLocation);
return ReadFileIntoBuffer(File);
}
@ -4718,7 +4717,10 @@ SnipeChecksumIntoHTML(db_asset *Asset, buffer *Checksum)
db_entry *Entry = LocateEntry(Landmark->Project, Landmark->EntryIndex);
if(Entry)
{
FileReadRC = ReadPlayerPageIntoBuffer(P, &HTML, Entry);
string BaseDir = Wrap0i(P->BaseDir, sizeof(P->BaseDir));
string PlayerLocation = Wrap0i(P->PlayerLocation, sizeof(P->PlayerLocation));
string OutputLocation = Wrap0i(Entry->OutputLocation, sizeof(Entry->OutputLocation));
FileReadRC = ReadPlayerPageIntoBuffer(&HTML, BaseDir, PlayerLocation, OutputLocation);
}
else
{
@ -4737,7 +4739,9 @@ SnipeChecksumIntoHTML(db_asset *Asset, buffer *Checksum)
{
if(P)
{
FileReadRC = ReadSearchPageIntoBuffer(P, &HTML);
string BaseDir = Wrap0i(P->BaseDir, sizeof(P->BaseDir));
string SearchLocation = Wrap0i(P->SearchLocation, sizeof(P->SearchLocation));
FileReadRC = ReadSearchPageIntoBuffer(&HTML, &BaseDir, &SearchLocation);
}
else
{
@ -8653,31 +8657,27 @@ DeclaimPlayerBuffers(player_buffers *B)
}
char *
ConstructIndexFilePath(db_header_project *Project)
ConstructIndexFilePath(string BaseDir, string SearchLocation, string ProjectID)
{
string SearchLocation = Wrap0i(Project->SearchLocation, sizeof(Project->SearchLocation));
char *Result = ConstructDirectoryPath(Project, &SearchLocation, 0);
char *Result = ConstructDirectoryPath(&BaseDir, &SearchLocation, 0);
ExtendString0(&Result, Wrap0("/"));
ExtendString0(&Result, Wrap0i(Project->ID, sizeof(Project->ID)));
ExtendString0(&Result, ProjectID);
ExtendString0(&Result, ExtensionStrings[EXT_INDEX]);
return Result;
}
void
DeleteSearchPageFromFilesystem(db_header_project *Project) // NOTE(matt): Do we need to handle relocating, like the PlayerPage function?
DeleteSearchPageFromFilesystem(string BaseDir, string SearchLocation, string ProjectID) // NOTE(matt): Do we need to handle relocating, like the PlayerPage function?
{
string SearchLocationL = Wrap0i(Project->SearchLocation, sizeof(Project->SearchLocation));
char *SearchPagePath = ConstructHTMLIndexFilePath(Project, &SearchLocationL, 0);
char *SearchPagePath = ConstructHTMLIndexFilePath(&BaseDir, &SearchLocation, 0);
remove(SearchPagePath);
Free(SearchPagePath);
char *IndexFilePath = ConstructIndexFilePath(Project);
remove(IndexFilePath);
Free(IndexFilePath);
// TODO(matt): Consider the correctness of this
remove(DB.File.Path);
// TODO(matt): Consider the correctness of this.
FreeFile(&DB.File);
char *SearchDirectory = ConstructDirectoryPath(Project, &SearchLocationL, 0);
char *SearchDirectory = ConstructDirectoryPath(&BaseDir, &SearchLocation, 0);
remove(SearchDirectory);
Free(SearchDirectory);
}
@ -8714,10 +8714,9 @@ PrintLineageAndEntry(string Lineage, string EntryID, string EntryTitle, bool App
}
rc
DeletePlayerPageFromFilesystem(db_header_project *P, string EntryOutput, bool Relocating, bool Echo)
DeletePlayerPageFromFilesystem(string BaseDir, string PlayerLocation, string EntryOutput, bool Relocating, bool Echo)
{
string PlayerLocation = Wrap0i(P->PlayerLocation, sizeof(P->PlayerLocation));
char *OutputDirectoryPath = ConstructDirectoryPath(P, &PlayerLocation, &EntryOutput);
char *OutputDirectoryPath = ConstructDirectoryPath(&BaseDir, &PlayerLocation, &EntryOutput);
DIR *PlayerDir;
if((PlayerDir = opendir(OutputDirectoryPath))) // There is a directory for the Player, which there probably should be if not for manual intervention
@ -8951,7 +8950,7 @@ HMMLToBuffers(buffers *CollationBuffers, template *BespokeTemplate, string BaseF
if(!HaveErrors)
{
if(HMML.metadata.template)
if(!CurrentProject->DenyBespokeTemplates && HMML.metadata.template)
{
switch(PackTemplate(BespokeTemplate, Wrap0(HMML.metadata.template), TEMPLATE_BESPOKE, CurrentProject))
{
@ -8993,7 +8992,7 @@ HMMLToBuffers(buffers *CollationBuffers, template *BespokeTemplate, string BaseF
string NewOutputLocation = Wrap0i(N->WorkingThis.OutputLocation, sizeof(N->WorkingThis.OutputLocation));
if(StringsDiffer(OldOutputLocation, NewOutputLocation))
{
DeletePlayerPageFromFilesystem(N->Project, OldOutputLocation, FALSE, TRUE);
DeletePlayerPageFromFilesystem(CurrentProject->BaseDir, CurrentProject->PlayerLocation, OldOutputLocation, FALSE, TRUE);
}
}
@ -10703,23 +10702,24 @@ void
InitIndexFile(project *P)
{
DB.File.Buffer.ID = BID_DATABASE;
DB.File = InitFile(&P->BaseDir, &P->ID, EXT_INDEX);
DB.File.Path = ConstructIndexFilePath(P->BaseDir, P->SearchLocation, P->ID);
ReadFileIntoBuffer(&DB.File); // NOTE(matt): Could we actually catch errors (permissions?) here and bail?
if(!DB.File.Buffer.Location)
{
char *BaseDir0 = MakeString0("l", &P->BaseDir);
DIR *OutputDirectoryHandle = opendir(BaseDir0);
string IndexFileDir = StripComponentFromPath(Wrap0(DB.File.Path));
char *IndexFileDir0 = MakeString0("l", &IndexFileDir);
DIR *OutputDirectoryHandle = opendir(IndexFileDir0);
if(!OutputDirectoryHandle)
{
if(!MakeDir(P->BaseDir))
if(!MakeDir(IndexFileDir))
{
LogError(LOG_ERROR, "Unable to create directory %.*s: %s", (int)P->BaseDir.Length, P->BaseDir.Base, strerror(errno));
fprintf(stderr, "Unable to create directory %.*s: %s\n", (int)P->BaseDir.Length, P->BaseDir.Base, strerror(errno));
Free(BaseDir0);
Free(IndexFileDir0);
return;
};
}
Free(BaseDir0);
Free(IndexFileDir0);
closedir(OutputDirectoryHandle);
DB.File.Handle = fopen(DB.File.Path, "w");
@ -11905,7 +11905,7 @@ InsertNeighbourLink(db_header_project *P, db_entry *From, db_entry *To, enum8(li
{
MEM_TEST_INITIAL();
file HTML = {};
ReadPlayerPageIntoBuffer(P, &HTML, From);
ReadPlayerPageIntoBuffer(&HTML, Wrap0i(P->BaseDir, sizeof(P->BaseDir)), Wrap0i(P->PlayerLocation, sizeof(P->PlayerLocation)), Wrap0i(From->OutputLocation, sizeof(From->OutputLocation)));
MEM_TEST_MID("InsertNeighbourLink1");
if(HTML.Buffer.Location)
@ -12050,7 +12050,7 @@ DeleteNeighbourLinks(neighbourhood *N)
}
file HTML = {};
ReadPlayerPageIntoBuffer(N->Project, &HTML, Entry);
ReadPlayerPageIntoBuffer(&HTML, Wrap0i(N->Project->BaseDir, sizeof(N->Project->BaseDir)), Wrap0i(N->Project->PlayerLocation, sizeof(N->Project->PlayerLocation)), Wrap0i(Entry->OutputLocation, sizeof(Entry->OutputLocation)));
if(HTML.Buffer.Location)
{
if(!(HTML.Handle = fopen(HTML.Path, "w"))) { FreeFile(&HTML); return RC_ERROR_FILE; };
@ -12130,7 +12130,7 @@ MarkNextAsFirst(neighbourhood *N)
{
// TODO(matt): We added some untested logic in here. If things related to the prev / next links fail, we screwed up here
file HTML = {};
ReadPlayerPageIntoBuffer(N->Project, &HTML, N->Next);
ReadPlayerPageIntoBuffer(&HTML, Wrap0i(N->Project->BaseDir, sizeof(N->Project->BaseDir)), Wrap0i(N->Project->PlayerLocation, sizeof(N->Project->PlayerLocation)), Wrap0i(N->Next->OutputLocation, sizeof(N->Next->OutputLocation)));
if(HTML.Buffer.Location)
{
buffer Link;
@ -12167,32 +12167,32 @@ MarkNextAsFirst(neighbourhood *N)
void
MarkPrevAsFinal(neighbourhood *N)
{
file File = {};
ReadPlayerPageIntoBuffer(N->Project, &File, N->Prev);
file HTML = {};
ReadPlayerPageIntoBuffer(&HTML, Wrap0i(N->Project->BaseDir, sizeof(N->Project->BaseDir)), Wrap0i(N->Project->PlayerLocation, sizeof(N->Project->PlayerLocation)), Wrap0i(N->Prev->OutputLocation, sizeof(N->Prev->OutputLocation)));
if(File.Buffer.Location)
if(HTML.Buffer.Location)
{
File.Handle = fopen(File.Path, "w");
HTML.Handle = fopen(HTML.Path, "w");
buffer Link;
ClaimBuffer(&Link, BID_LINK, Kilobytes(4));
fwrite(File.Buffer.Location, N->Prev->LinkOffsets.PrevStart + N->Prev->LinkOffsets.PrevEnd + N->Prev->LinkOffsets.NextStart, 1, File.Handle);
fwrite(HTML.Buffer.Location, N->Prev->LinkOffsets.PrevStart + N->Prev->LinkOffsets.PrevEnd + N->Prev->LinkOffsets.NextStart, 1, HTML.Handle);
CopyStringToBuffer(&Link,
" <div class=\"episodeMarker last\"><div>&#8226;</div><div>You have arrived at the (current) end of <cite>%.*s</cite></div><div>&#8226;</div></div>\n", (int)CurrentProject->Title.Length, CurrentProject->Title.Base);
fwrite(Link.Location, (Link.Ptr - Link.Location), 1, File.Handle);
fwrite(Link.Location, (Link.Ptr - Link.Location), 1, HTML.Handle);
fwrite(File.Buffer.Location + N->Prev->LinkOffsets.PrevStart + N->Prev->LinkOffsets.PrevEnd + N->Prev->LinkOffsets.NextStart + N->Prev->LinkOffsets.NextEnd,
File.Buffer.Size - (N->Prev->LinkOffsets.PrevStart + N->Prev->LinkOffsets.PrevEnd + N->Prev->LinkOffsets.NextStart + N->Prev->LinkOffsets.NextEnd),
fwrite(HTML.Buffer.Location + N->Prev->LinkOffsets.PrevStart + N->Prev->LinkOffsets.PrevEnd + N->Prev->LinkOffsets.NextStart + N->Prev->LinkOffsets.NextEnd,
HTML.Buffer.Size - (N->Prev->LinkOffsets.PrevStart + N->Prev->LinkOffsets.PrevEnd + N->Prev->LinkOffsets.NextStart + N->Prev->LinkOffsets.NextEnd),
1,
File.Handle);
HTML.Handle);
N->Prev->LinkOffsets.NextEnd = Link.Ptr - Link.Location;
DeclaimBuffer(&Link);
fclose(File.Handle);
fclose(HTML.Handle);
}
else
{
@ -12200,7 +12200,7 @@ MarkPrevAsFinal(neighbourhood *N)
N->Prev->LinkOffsets = Blank;
}
FreeFile(&File);
FreeFile(&HTML);
}
void
@ -12308,7 +12308,7 @@ DeleteFromDB(neighbourhood *N, string BaseFilename)
--NewEntryCount;
if(NewEntryCount == 0)
{
DeleteSearchPageFromFilesystem(N->Project);
DeleteSearchPageFromFilesystem(Wrap0i(N->Project->BaseDir, sizeof(N->Project->BaseDir)), Wrap0i(N->Project->SearchLocation, sizeof(N->Project->SearchLocation)), Wrap0i(N->Project->ID, sizeof(N->Project->ID)));
DeleteLandmarksForSearch(CurrentProject->Index);
UpdateNeighbourhoodPointers(N, &DB.Metadata.Signposts);
}
@ -12369,6 +12369,10 @@ GenerateFilterOfProjectAndChildren(buffer *Filter, db_header_project *StoredP, p
OpenNodeNewLine(Filter, &Filter->IndentLevel, NODE_DIV, 0);
AppendStringToBuffer(Filter, Wrap0(" class=\"cineraFilterProject\" data-baseURL=\""));
AppendStringToBuffer(Filter, P->BaseURL);
AppendStringToBuffer(Filter, Wrap0("\" data-searchLocation=\""));
AppendStringToBuffer(Filter, P->SearchLocation);
AppendStringToBuffer(Filter, Wrap0("\" data-playerLocation=\""));
AppendStringToBuffer(Filter, P->PlayerLocation);
AppendStringToBuffer(Filter, Wrap0("\">"));
OpenNodeNewLine(Filter, &Filter->IndentLevel, NODE_SPAN, 0);
@ -12406,6 +12410,8 @@ GenerateIndexOfProjectAndChildren(buffer *Index, db_header_project *StoredP, pro
AppendStringToBuffer(Index, P->ID);
AppendStringToBuffer(Index, Wrap0("\" data-baseURL=\""));
AppendStringToBuffer(Index, P->BaseURL);
AppendStringToBuffer(Index, Wrap0("\" data-searchLocation=\""));
AppendStringToBuffer(Index, P->SearchLocation);
AppendStringToBuffer(Index, Wrap0("\" data-playerLocation=\""));
AppendStringToBuffer(Index, P->PlayerLocation);
AppendStringToBuffer(Index, Wrap0("\">"));
@ -12909,9 +12915,10 @@ int
GeneratePlayerPage(neighbourhood *N, buffers *CollationBuffers, template *PlayerTemplate, string OutputLocation, bool Reinserting)
{
MEM_TEST_INITIAL();
string PlayerLocationL = Wrap0i(N->Project->PlayerLocation, sizeof(N->Project->PlayerLocation));
MEM_TEST_MID("GeneratePlayerPage1");
char *PlayerPath = ConstructDirectoryPath(N->Project, &PlayerLocationL, &OutputLocation);
string BaseDir = Wrap0i(N->Project->BaseDir, sizeof(N->Project->BaseDir));
string PlayerLocation = Wrap0i(N->Project->PlayerLocation, sizeof(N->Project->PlayerLocation));
char *PlayerPath = ConstructDirectoryPath(&BaseDir, &PlayerLocation, &OutputLocation);
MEM_TEST_MID("GeneratePlayerPage2");
DIR *OutputDirectoryHandle;
@ -12966,8 +12973,9 @@ GeneratePlayerPage(neighbourhood *N, buffers *CollationBuffers, template *Player
rc
GenerateSearchPage(neighbourhood *N, buffers *CollationBuffers, db_header_project *StoredP, project *P)
{
string SearchLocationL = Wrap0i(StoredP->SearchLocation, sizeof(StoredP->SearchLocation));
char *SearchPath = ConstructDirectoryPath(StoredP, &SearchLocationL, 0);
string BaseDir = Wrap0i(N->Project->BaseDir, sizeof(N->Project->BaseDir));
string SearchLocation = Wrap0i(N->Project->SearchLocation, sizeof(N->Project->SearchLocation));
char *SearchPath = ConstructDirectoryPath(&BaseDir, &SearchLocation, 0);
DIR *OutputDirectoryHandle;
if(!(OutputDirectoryHandle = opendir(SearchPath))) // TODO(matt): open()
@ -12994,7 +13002,7 @@ GenerateSearchPage(neighbourhood *N, buffers *CollationBuffers, db_header_projec
}
case RC_NOOP:
{
DeleteSearchPageFromFilesystem(StoredP);
DeleteSearchPageFromFilesystem(BaseDir, SearchLocation, P->ID);
DeleteLandmarksForSearch(P->Index);
UpdateNeighbourhoodPointers(N, &DB.Metadata.Signposts);
break;
@ -13093,7 +13101,10 @@ DeleteEntry(neighbourhood *N, string BaseFilename)
{
LinkNeighbours(N, LINK_EXCLUDE);
DeletePlayerPageFromFilesystem(N->Project, BaseFilename, FALSE, TRUE);
string BaseDir = Wrap0i(N->Project->BaseDir, sizeof(N->Project->BaseDir));
string PlayerLocation = Wrap0i(N->Project->PlayerLocation, sizeof(N->Project->PlayerLocation));
DeletePlayerPageFromFilesystem(BaseDir, PlayerLocation, BaseFilename, FALSE, TRUE);
UpdateLandmarksForNeighbourhood(N, EDIT_DELETION);
return RC_SUCCESS;
}
@ -13336,18 +13347,22 @@ rc
DeleteDeadDBEntries(neighbourhood *N, bool *Modified)
{
// TODO(matt): Additionally remove the BaseDir if it changed
bool NewPlayerLocation = FALSE;
bool NewSearchLocation = FALSE;
bool HasNewPlayerLocation = FALSE;
bool HasNewSearchLocation = FALSE;
// TODO(matt): Here is where the relocation happens. This could be wrong
if(StringsDiffer(CurrentProject->BaseDir, Wrap0i(N->Project->BaseDir, sizeof(N->Project->BaseDir))) ||
StringsDiffer(CurrentProject->PlayerLocation, Wrap0i(N->Project->PlayerLocation, sizeof(N->Project->PlayerLocation))))
{
string PlayerLocationL = Wrap0i(N->Project->PlayerLocation, sizeof(N->Project->PlayerLocation));
char *OldPlayerDirectory = ConstructDirectoryPath(N->Project, &PlayerLocationL, 0);
string OldBaseDir = Wrap0i(N->Project->BaseDir, sizeof(N->Project->BaseDir));
string OldPlayerLocation = Wrap0i(N->Project->PlayerLocation, sizeof(N->Project->PlayerLocation));
char *OldPlayerDirectory = ConstructDirectoryPath(&OldBaseDir, &OldPlayerLocation, 0);
db_header_project NewProjectHeader = *N->Project;
ClearCopyStringNoFormat(NewProjectHeader.BaseDir, sizeof(NewProjectHeader.BaseDir), CurrentProject->BaseDir);
char *NewPlayerDirectory = ConstructDirectoryPath(&NewProjectHeader, &CurrentProject->PlayerLocation, 0);
string NewBaseDir = Wrap0i(NewProjectHeader.BaseDir, sizeof(NewProjectHeader.BaseDir));
char *NewPlayerDirectory = ConstructDirectoryPath(&NewBaseDir, &CurrentProject->PlayerLocation, 0);
printf("%sRelocating Player Page%s from %s to %s%s\n",
ColourStrings[CS_REINSERTION], N->Project->EntryCount > 1 ? "s" : "",
@ -13358,29 +13373,35 @@ DeleteDeadDBEntries(neighbourhood *N, bool *Modified)
for(int EntryIndex = 0; EntryIndex < N->Project->EntryCount; ++EntryIndex)
{
db_entry *This = FirstEntry + EntryIndex;
DeletePlayerPageFromFilesystem(N->Project, Wrap0i(This->OutputLocation, sizeof(This->OutputLocation)), TRUE, TRUE);
string BaseDir = Wrap0i(N->Project->BaseDir, sizeof(N->Project->BaseDir));
string PlayerLocation = Wrap0i(N->Project->PlayerLocation, sizeof(N->Project->PlayerLocation));
DeletePlayerPageFromFilesystem(BaseDir, PlayerLocation, Wrap0i(This->OutputLocation, sizeof(This->OutputLocation)), TRUE, TRUE);
}
if(PlayerLocationL.Length > 0)
if(OldPlayerLocation.Length > 0)
{
RemoveChildDirectories(Wrap0(OldPlayerDirectory), Wrap0i(N->Project->BaseDir, sizeof(N->Project->BaseDir)));
}
Free(OldPlayerDirectory);
ClearCopyStringNoFormat(N->Project->PlayerLocation, sizeof(N->Project->PlayerLocation), CurrentProject->PlayerLocation);
NewPlayerLocation = TRUE;
HasNewPlayerLocation = TRUE;
}
if(StringsDiffer(CurrentProject->BaseDir, Wrap0i(N->Project->BaseDir, sizeof(N->Project->BaseDir))) ||
StringsDiffer(CurrentProject->SearchLocation, Wrap0i(N->Project->SearchLocation, sizeof(N->Project->SearchLocation))))
{
string SearchLocationL = Wrap0i(N->Project->SearchLocation, sizeof(N->Project->SearchLocation));
char *OldSearchDirectory = ConstructDirectoryPath(N->Project, &SearchLocationL, 0);
string OldBaseDir = Wrap0i(N->Project->BaseDir, sizeof(N->Project->BaseDir));
string OldSearchLocation = Wrap0i(N->Project->SearchLocation, sizeof(N->Project->SearchLocation));
char *OldSearchDirectory = ConstructDirectoryPath(&OldBaseDir, &OldSearchLocation, 0);
db_header_project NewProjectHeader = *N->Project;
ClearCopyStringNoFormat(NewProjectHeader.BaseDir, sizeof(NewProjectHeader.BaseDir), CurrentProject->BaseDir);
ClearCopyStringNoFormat(NewProjectHeader.SearchLocation, sizeof(NewProjectHeader.SearchLocation), CurrentProject->SearchLocation);
char *NewSearchDirectory = ConstructDirectoryPath(&NewProjectHeader, &CurrentProject->SearchLocation, 0);
string NewBaseDir = Wrap0i(NewProjectHeader.BaseDir, sizeof(NewProjectHeader.BaseDir));
string NewSearchLocation = Wrap0i(NewProjectHeader.SearchLocation, sizeof(NewProjectHeader.SearchLocation));
char *NewSearchDirectory = ConstructDirectoryPath(&NewBaseDir, &NewSearchLocation, 0);
MakeDir(Wrap0(NewSearchDirectory));
printf("%sRelocating Search Page from %s to %s%s\n",
@ -13392,8 +13413,8 @@ DeleteDeadDBEntries(neighbourhood *N, bool *Modified)
Free(OldSearchPagePath);
Free(NewSearchPagePath);
char *OldSearchIndexPath = ConstructIndexFilePath(N->Project);
char *NewSearchIndexPath = ConstructIndexFilePath(&NewProjectHeader);
char *OldSearchIndexPath = ConstructIndexFilePath(OldBaseDir, OldSearchLocation, Wrap0i(N->Project->ID, sizeof(N->Project->ID)));
char *NewSearchIndexPath = ConstructIndexFilePath(NewBaseDir, NewSearchLocation, Wrap0i(N->Project->ID, sizeof(N->Project->ID)));
rename(OldSearchIndexPath, NewSearchIndexPath);
Free(OldSearchIndexPath);
Free(NewSearchIndexPath);
@ -13401,10 +13422,10 @@ DeleteDeadDBEntries(neighbourhood *N, bool *Modified)
Free(NewSearchDirectory);
FreeFile(&DB.File);
DB.File = InitFile(&CurrentProject->BaseDir, &CurrentProject->ID, EXT_INDEX);
DB.File.Path = ConstructIndexFilePath(CurrentProject->BaseDir, CurrentProject->SearchLocation, CurrentProject->ID);
ReadFileIntoBuffer(&DB.File); // NOTE(matt): Could we actually catch errors (permissions?) here and bail?
if(SearchLocationL.Length > 0)
if(OldSearchLocation.Length > 0)
{
RemoveChildDirectories(Wrap0(OldSearchDirectory), Wrap0i(N->Project->BaseDir, sizeof(N->Project->BaseDir)));
}
@ -13412,7 +13433,7 @@ DeleteDeadDBEntries(neighbourhood *N, bool *Modified)
remove(OldSearchDirectory);
Free(OldSearchDirectory);
ClearCopyStringNoFormat(N->Project->SearchLocation, sizeof(N->Project->SearchLocation), CurrentProject->SearchLocation);
NewSearchLocation = TRUE;
HasNewSearchLocation = TRUE;
}
if(StringsDiffer(CurrentProject->BaseDir, Wrap0i(N->Project->BaseDir, sizeof(N->Project->BaseDir))))
@ -13435,7 +13456,7 @@ DeleteDeadDBEntries(neighbourhood *N, bool *Modified)
*Modified = TRUE;
}
if(NewPlayerLocation || NewSearchLocation)
if(HasNewPlayerLocation || HasNewSearchLocation)
{
if(!(DB.Metadata.File.Handle = fopen(DB.Metadata.File.Path, "w"))) { FreeBuffer(&DB.Metadata.File.Buffer); return RC_ERROR_FILE; }
ClearCopyStringNoFormat(N->Project->BaseDir, sizeof(N->Project->BaseDir), CurrentProject->BaseDir);
@ -14113,13 +14134,16 @@ DeleteHTMLFilesOfProject(db_header_project *Project)
char *Ptr = (char *)Project;
Ptr += sizeof(db_header_project) + sizeof(db_entry) * Project->EntryCount;
string BaseDir = Wrap0i(Project->BaseDir, sizeof(Project->BaseDir));
string PlayerLocation = Wrap0i(Project->PlayerLocation, sizeof(Project->PlayerLocation));
db_entry *Entry = LocateFirstEntry(Project);
for(int i = 0; i < Project->EntryCount; ++i, ++Entry)
{
DeletePlayerPageFromFilesystem(Project, Wrap0i(Entry->OutputLocation, sizeof(Entry->OutputLocation)), FALSE, FALSE);
DeletePlayerPageFromFilesystem(BaseDir, PlayerLocation, Wrap0i(Entry->OutputLocation, sizeof(Entry->OutputLocation)), FALSE, FALSE);
}
DeleteSearchPageFromFilesystem(Project);
DeleteSearchPageFromFilesystem(BaseDir, PlayerLocation, Wrap0i(Project->ID, sizeof(Project->ID)));
return Ptr;
}
@ -14132,7 +14156,9 @@ DeleteHTMLFilesOfProjectAndChildren(db_header_project *Project)
{
Ptr = DeleteHTMLFilesOfProjectAndChildren(Ptr);
}
DeleteSearchPageFromFilesystem(Project);
string BaseDir = Wrap0i(Project->BaseDir, sizeof(Project->BaseDir));
string SearchLocation = Wrap0i(Project->SearchLocation, sizeof(Project->SearchLocation));
DeleteSearchPageFromFilesystem(BaseDir, SearchLocation, Wrap0i(Project->ID, sizeof(Project->ID)));
return Ptr;
}
@ -14437,7 +14463,10 @@ SyncProjects_TopLevel(project_generations *G, project *C, db_block_projects **SP
if((*SChild)->ChildCount == 0 && (*SChild)->EntryCount == 0)
{
DeleteSearchPageFromFilesystem(*SChild);
string BaseDir = Wrap0i((*SChild)->BaseDir, sizeof((*SChild)->BaseDir));
string SearchLocation = Wrap0i((*SChild)->SearchLocation, sizeof((*SChild)->SearchLocation));
string ProjectID = Wrap0i((*SChild)->ID, sizeof((*SChild)->ID));
DeleteSearchPageFromFilesystem(BaseDir, SearchLocation, ProjectID);
DeleteLandmarksForSearch(C->Index);
DeleteStaleAssets();
}
@ -14491,7 +14520,11 @@ SyncProjects(project_generations *G, project *C, db_header_project **SParent, db
if((*SChild)->ChildCount == 0 && (*SChild)->EntryCount == 0)
{
DeleteSearchPageFromFilesystem(*SChild);
string BaseDir = Wrap0i((*SChild)->BaseDir, sizeof((*SChild)->BaseDir));
string SearchLocation = Wrap0i((*SChild)->SearchLocation, sizeof((*SChild)->SearchLocation));
string ProjectID = Wrap0i((*SChild)->ID, sizeof((*SChild)->ID));
DeleteSearchPageFromFilesystem(BaseDir, SearchLocation, ProjectID);
DeleteLandmarksForSearch(C->Index);
}

View File

@ -521,6 +521,7 @@ typedef struct project
string StreamUsername;
string VODPlatform;
bool DenyBespokeTemplates;
bool SingleBrowserTab;
bool IgnorePrivacy;
@ -917,6 +918,7 @@ InitTypeSpecs(void)
////
PushTypeSpecField(Root, FT_STRING, IDENT_UNIT, TRUE);
PushTypeSpecField(Root, FT_BARE, IDENT_TITLE_LIST_END, TRUE);
PushTypeSpecField(Root, FT_BOOLEAN, IDENT_DENY_BESPOKE_TEMPLATES, TRUE);
PushTypeSpecField(Root, FT_BOOLEAN, IDENT_IGNORE_PRIVACY, TRUE);
PushTypeSpecField(Root, FT_BOOLEAN, IDENT_SINGLE_BROWSER_TAB, TRUE);
PushTypeSpecField(Root, FT_NUMBER, IDENT_PRIVACY_CHECK_INTERVAL, TRUE);
@ -997,6 +999,7 @@ InitTypeSpecs(void)
PushTypeSpecField(Project, FT_BARE, IDENT_TITLE_LIST_END, TRUE);
// NOTE(matt): Modes
PushTypeSpecField(Project, FT_BOOLEAN, IDENT_DENY_BESPOKE_TEMPLATES, TRUE);
PushTypeSpecField(Project, FT_BOOLEAN, IDENT_IGNORE_PRIVACY, TRUE);
PushTypeSpecField(Project, FT_BOOLEAN, IDENT_SINGLE_BROWSER_TAB, TRUE);
//
@ -3377,6 +3380,8 @@ PushProject(config *C, resolution_errors *E, config_verifiers *V, project *P, sc
config_bool_pair *This = ProjectTree->BoolPairs + i;
switch(This->Key)
{
case IDENT_DENY_BESPOKE_TEMPLATES:
{ P->DenyBespokeTemplates = This->Value; } break;
case IDENT_IGNORE_PRIVACY:
{ P->IgnorePrivacy = This->Value; } break;
case IDENT_SINGLE_BROWSER_TAB:

View File

@ -34,10 +34,11 @@ function hideEntriesOfProject(ProjectElement)
ProjectElement.classList.add("off");
}
var baseURL = ProjectElement.attributes.getNamedItem("data-baseURL").value;
var searchLocation = ProjectElement.attributes.getNamedItem("data-searchLocation").value;
for(var i = 0; i < projects.length; ++i)
{
var ThisProject = projects[i];
if(ThisProject.baseURL === baseURL)
if(baseURL === ThisProject.baseURL && searchLocation === ThisProject.searchLocation)
{
ThisProject.filteredOut = true;
if(ThisProject.entriesContainer != null)
@ -56,10 +57,11 @@ function showEntriesOfProject(ProjectElement)
ProjectElement.classList.remove("off");
}
var baseURL = ProjectElement.attributes.getNamedItem("data-baseURL").value;
var searchLocation = ProjectElement.attributes.getNamedItem("data-searchLocation").value;
for(var i = 0; i < projects.length; ++i)
{
var ThisProject = projects[i];
if(ThisProject.baseURL === baseURL)
if(baseURL === ThisProject.baseURL && searchLocation === ThisProject.searchLocation)
{
ThisProject.filteredOut = false;
if(ThisProject.entriesContainer != null)
@ -71,7 +73,7 @@ function showEntriesOfProject(ProjectElement)
}
}
function hideProjectSearchResults(baseURL)
function hideProjectSearchResults(baseURL, playerLocation)
{
var cineraResults = document.getElementById("cineraResults");
if(cineraResults)
@ -80,7 +82,8 @@ function hideProjectSearchResults(baseURL)
for(var i = 0; i < cineraResultsProjects.length; ++i)
{
var resultBaseURL = cineraResultsProjects[i].attributes.getNamedItem("data-baseURL").value;
if(baseURL === resultBaseURL)
var resultPlayerLocation = cineraResultsProjects[i].attributes.getNamedItem("data-playerLocation").value;
if(baseURL === resultBaseURL && playerLocation === resultPlayerLocation)
{
cineraResultsProjects[i].style.display = "none";
return;
@ -89,7 +92,7 @@ function hideProjectSearchResults(baseURL)
}
}
function showProjectSearchResults(baseURL)
function showProjectSearchResults(baseURL, playerLocation)
{
var cineraResults = document.getElementById("cineraResults");
if(cineraResults)
@ -98,7 +101,8 @@ function showProjectSearchResults(baseURL)
for(var i = 0; i < cineraResultsProjects.length; ++i)
{
var resultBaseURL = cineraResultsProjects[i].attributes.getNamedItem("data-baseURL").value;
if(baseURL === resultBaseURL)
var resultPlayerLocation = cineraResultsProjects[i].attributes.getNamedItem("data-playerLocation").value;
if(baseURL === resultBaseURL && playerLocation === resultPlayerLocation)
{
cineraResultsProjects[i].style.display = "flex";
return;
@ -110,6 +114,7 @@ function showProjectSearchResults(baseURL)
function toggleEntriesOfProjectAndChildren(ProjectFilterElement)
{
var baseURL = ProjectFilterElement.attributes.getNamedItem("data-baseURL").value;
var searchLocation = ProjectFilterElement.attributes.getNamedItem("data-searchLocation").value;
var shouldShow = ProjectFilterElement.classList.contains("off");
if(shouldShow)
{
@ -125,7 +130,8 @@ function toggleEntriesOfProjectAndChildren(ProjectFilterElement)
for(var i = 0; i < projects.length; ++i)
{
var ThisProject = projects[i];
if(ThisProject.baseURL === baseURL)
if(ThisProject.baseURL === baseURL && ThisProject.searchLocation === searchLocation)
{
if(shouldShow)
{
@ -135,7 +141,7 @@ function toggleEntriesOfProjectAndChildren(ProjectFilterElement)
{
ThisProject.entriesContainer.style.display = "flex";
}
showProjectSearchResults(projects[i].baseURL);
showProjectSearchResults(ThisProject.baseURL, ThisProject.playerLocation);
}
else
{
@ -145,7 +151,7 @@ function toggleEntriesOfProjectAndChildren(ProjectFilterElement)
{
ThisProject.entriesContainer.style.display = "none";
}
hideProjectSearchResults(ThisProject.baseURL);
hideProjectSearchResults(ThisProject.baseURL, ThisProject.playerLocation);
}
}
}
@ -155,17 +161,17 @@ function toggleEntriesOfProjectAndChildren(ProjectFilterElement)
for(var j = 0; j < indexChildFilterProjects.length; ++j)
{
var ThisElement = indexChildFilterProjects[j];
var baseURL = ThisElement.attributes.getNamedItem("data-baseURL").value;
var playerLocation = ThisElement.attributes.getNamedItem("data-playerLocation").value;
if(shouldShow)
{
var baseURL = ThisElement.attributes.getNamedItem("data-baseURL").value;
showEntriesOfProject(ThisElement);
showProjectSearchResults(baseURL);
showProjectSearchResults(baseURL, playerLocation);
}
else
{
var baseURL = ThisElement.attributes.getNamedItem("data-baseURL").value;
hideEntriesOfProject(ThisElement);
hideProjectSearchResults(baseURL);
hideProjectSearchResults(baseURL, playerLocation);
}
}
}
@ -280,14 +286,17 @@ function prepareProjects()
{
var ID = projectsContainer[i].attributes.getNamedItem("data-project").value;
var baseURL = projectsContainer[i].attributes.getNamedItem("data-baseURL").value;
var searchLocation = projectsContainer[i].attributes.getNamedItem("data-searchLocation").value;
var playerLocation = projectsContainer[i].attributes.getNamedItem("data-playerLocation").value;
var theme = projectsContainer[i].classList.item(1);
projects[i] =
{
baseURL: baseURL,
searchLocation: searchLocation,
playerLocation: playerLocation,
playerURLPrefix: (baseURL ? baseURL + "/" : "") + (playerLocation ? playerLocation + "/" : ""),
indexLocation: (baseURL ? baseURL + "/" : "") + ID + ".index",
indexLocation: (baseURL ? baseURL + "/" : "") + (searchLocation ? searchLocation + "/" : "") + ID + ".index",
projectTitleElement: projectsContainer[i].querySelector(":scope > .cineraProjectTitle"),
entriesContainer: projectsContainer[i].querySelector(":scope > .cineraIndexEntries"),
dayContainerPrototype: dayContainerPrototype.cloneNode(true),
@ -529,6 +538,7 @@ function renderResults() {
if(projects[i].playerURLPrefix === episode.playerURLPrefix)
{
projectContainer.setAttribute("data-baseURL", projects[i].baseURL);
projectContainer.setAttribute("data-playerLocation", projects[i].playerLocation);
if(projects[i].filteredOut)
{
projectContainer.style.display = "none";