(* ::Package:: *) (* ========================================================================== This is a package to define the SyCo suite functions. Version 1.5.4 by ~sh, 2024-04-20 Obsolete as of v1.5.4, but still maintained until the development of the superseding ProblemBook package and Sycomore stylesheet is finished. =========================================================================== *) BeginPackage["SyCo`"]; Unprotect["SyCo`*"]; ClearAll["SyCo`*"]; (**************** problem["name"]: ****************) problem::usage = "\ problem[\"\!\(\* StyleBox[\"name\", \"TI\"]\)\"] opens problem \"\!\(\* StyleBox[\"name\", \"TI\"]\)\" imported from a remote source, prompting to save it as \"NotebookDirectory[]"<>$PathnameSeparator<>"SyCo"<>$PathnameSeparator<>"\!\(\* StyleBox[\"name\", \"TI\"]\).nb\" (if the evaluation notebook contains a unique tag) or \"NotebookDirectory[]"<>$PathnameSeparator<>"\!\(\* StyleBox[\"name\", \"TI\"]\).nb\" (otherwise). problem[\"*\!\(\* StyleBox[\"name\", \"TI\"]\)\"] opens solution to problem \"\!\(\* StyleBox[\"name\", \"TI\"]\)\" imported from a remote source, prompting to save it as \"NotebookDirectory[]"<>$PathnameSeparator<>"SyCo"<>$PathnameSeparator<>"\!\(\* StyleBox[\"name\", \"TI\"]\)_S.nb\" (if the evaluation notebook contains a unique tag) or \"NotebookDirectory[]"<>$PathnameSeparator<>"\!\(\* StyleBox[\"name\", \"TI\"]\)_S.nb\" (otherwise). problem[\"/\!\(\* StyleBox[\"name\", \"TI\"]\)\"] as problem[\"\!\(\* StyleBox[\"name\", \"TI\"]\)\"] but uses a local source (for developer's needs only, hardly will work for others, so don't waste time). problem[\"/*\!\(\* StyleBox[\"name\", \"TI\"]\)\"] as problem[\"*\!\(\* StyleBox[\"name\", \"TI\"]\)\"] but uses a local source (for developer's needs only, hardly will work for others, so don't waste time)."; (**************** resource["filespec"]: ****************) resource::usage = "\ resource[\"\!\(\* StyleBox[\"file\", \"TI\"]\)\"] returns string representation of the \!\(\* StyleBox[\"file\", \"TI\"]\)'s current remote source URL. No check for existence is done. resource[\"\!\(\* StyleBox[\"dir\", \"TI\"]\)\!\(\* StyleBox[\"/\", \"TI\"]\)\!\(\* StyleBox[\"\[Ellipsis]\", \"TI\"]\)\!\(\* StyleBox[\"/\", \"TI\"]\)\!\(\* StyleBox[\"file\", \"TI\"]\)\"] returns string representation of the \!\(\* StyleBox[\"dir\", \"TI\"]\)\!\(\* StyleBox[\"/\", \"TI\"]\)\!\(\* StyleBox[\"\[Ellipsis]\", \"TI\"]\)\!\(\* StyleBox[\"/\", \"TI\"]\)\!\(\* StyleBox[\"file\", \"TI\"]\)'s current remote source URL. resource[\"=\!\(\* StyleBox[\"filespec\", \"TI\"]\)\"] wraps URL around the string given by either of the above \!\(\* StyleBox[\"filespec\", \"TI\"]\) syntaxes."; (**************** submit["usr", "pwd", ccq], submit["usr"], submit[]: ****************) submit::usage = "\ submit[\!\(\* StyleBox[\"usr\", \"TI\"]\), \!\(\* StyleBox[\"pwd\", \"TI\"]\), \!\(\* StyleBox[\"ccq\", \"TI\"]\)] emails attached current evaluation notebook from a CRO user \!\(\* StyleBox[\"usr\", \"TI\"]\) authenticated with password \!\(\* StyleBox[\"pwd\", \"TI\"]\) to the teacher (including \!\(\* StyleBox[\"usr\", \"TI\"]\) as Cc iff Boolean \!\(\* StyleBox[\"ccq\", \"TI\"]\) is chosen True), preceded by automatically deleting all output and docked cells, and saving the notebook. submit[\!\(\* StyleBox[\"usr\", \"TI\"]\), \!\(\* StyleBox[\"pwd\", \"TI\"]\)] as submit[\!\(\* StyleBox[\"usr\", \"TI\"]\), \!\(\* StyleBox[\"pwd\", \"TI\"]\), \!\(\* StyleBox[\"ccq\", \"TI\"]\)] but the \!\(\* StyleBox[\"ccq\", \"TI\"]\) choice is requested in a dialog instead of giving it as an argument. submit[\!\(\* StyleBox[\"usr\", \"TI\"]\)] as submit[\!\(\* StyleBox[\"usr\", \"TI\"]\), \!\(\* StyleBox[\"pwd\", \"TI\"]\), \!\(\* StyleBox[\"ccq\", \"TI\"]\)] but the CRO password \!\(\* StyleBox[\"pwd\", \"TI\"]\) and the \!\(\* StyleBox[\"ccq\", \"TI\"]\) choice are requested in dialogs instead of giving them as arguments. submit[] as submit[\!\(\* StyleBox[\"usr\", \"TI\"]\), \!\(\* StyleBox[\"pwd\", \"TI\"]\), \!\(\* StyleBox[\"ccq\", \"TI\"]\)] but both CRO credentials and the \!\(\* StyleBox[\"ccq\", \"TI\"]\) choice are requested in dialogs instead of giving them as arguments."; (**************** uninstall[]: ****************) uninstall::usage = "\ uninstall[] interactively uninstalls those components of the SyCo suite that currently remain installed. (Notebooks currently open can lock the stylesheet, preventing it from deleting or overwriting; in this case read the error message.)"; (**************** install[]: ****************) install::usage = "\ install[] installs/upgrades SyCo components uninstalled by prefatory call to uninstall[] (ie. not necessarily all ones). install[All] coerces an installation/upgrade of all components of the SyCo suite without prior uninstalling. (Notebooks currently open can lock the stylesheet, preventing it from deleting or overwriting; in this case read the error message.)"; Begin["`Private`"]; (**************** repeatedly used variables: ****************) $instdom="slu.cz"(* institution domain *); $mycro="https://cro."<>$instdom<>"/"(* CRO portal *); $croabout="https://www."<>$instdom<>"/slu/en/croabout"(* about CRO web page *); $teacher="stanislav.hledik@physics."(* addressee teacher *); $urlbase="https://is."<>$instdom<>"/www/hle0002/vyuka/"(* URL base *); $resdir="resources"(* resource dir joined to URL base/$suitname *); $suitname="SyCo" (* the Symbolical Computations brief base name *); $nbx=".nb"(* notebook and stylesheet file extension *); $styext=$nbx(* stylesheet file extension *); $stysuf="_Style"(* stylesheet suffix *); $stydir=FileNameJoin[{"SystemFiles","FrontEnd","StyleSheets"}](* stylesheet path under $UserBaseDirectory *); $pkgext=".wl"(* package file extension *); $pkgdir=FileNameJoin[{"Applications"}](* package path under $UserBaseDirectory *); $prosuf="_P"(* problem suffix *); $solsuf="_S"(* solution suffix *); $ausdef=True(* default value for whether to append CRO username as suffix to basename in 'submit' *); $prodir="Problems"(* problems local directory *); $avaidirs={$resdir,ToLowerCase[$prodir]}(* available dirs joined to URL base/$suitname *); $uniquekey="NotebookUniqueTag"(* key for unique tag list *); $uniquetags={ "B0KSSOup5DSZFLzi7NiaKA","BBsxhRK2FDqbZM6evqFyjZ","BdX7xCPjZGgLOyPryVDY8k","Ci7pz1N99LNaTmhrE7D5qd", "CprgeOzNVE0Joh1SiYJdLE","D9O5IyyWxBzKC1jEnrUHf8","DK2EbpGZZB3aJweZKaKCPH","DkKq1JbTtCHYDMqWhcoaQc", "DQcvF6mDNEMrA6PdkS8y2F","DynUrsVBVMGJBtPS0k2IGN","DzzFTezuVA9aij2Qcf6iKD" }(* notebook unique tags, currently 11 notebooks: AssignmentMN.nb *); $zfmloc="";$zfmrem=".gz"(* local and remote compression format *); $zfmail=".zip"(* email attachment compression format *); (**************** helper functions: ****************) $authWithCRO[(* a dialog that returns sequence usr,pwd or pwd only *) type_(* "Password" or "UsernamePassword" *), wtit_(* window title *), mycro_(* my CRO web page *), croabout_(* about CRO web page *) ]:= Apply[Sequence, AuthenticationDialog[ type(* dialog type: "Password" or "UsernamePassword" *), SystemCredentialKey->If[type==="Password","SluCroP"(* Password only *),"SluCroUP"(* both Username and Password *)], WindowTitle->wtit, AppearanceRules-><| "Description"->TextGrid[{ {Item[Row[{ "Authenticate using ",Hyperlink["your CRO",mycro]," ", If[type==="Password",Hyperlink["identity password",croabout],Hyperlink["identity credentials",croabout]],"." }],Alignment->Left,BaseStyle->Medium] }, {"Check the box to securely store the "<>If[type==="Password","password","dialog fields"]<>" for future submitting."}, {"Uncheck the box before submitting to remove the "<>If[type==="Password","password","dialog fields"]<>" stored."} },ItemStyle->Small,Spacings->{Automatic,{{0,1},0,0}}] |> ], Heads->False] $includeCc[(* a dialog that returns Boolean: whether to include sender's address as Cc or not: *) wtit_(* window title *), usr_(* CRO username *), dom_(* domain *) ]:= ChoiceDialog["Also include \""<>ToLowerCase[usr]<>"@"<>dom<>"\" as Cc address?",{"Yes please"->True,"No, thanks"->False},WindowTitle->wtit] $nbksContainStyle[ sty_(* stylesheet name (just basename) or filename (w/o path, just basename.ext) *), wtit_(* window title *) ]:= With[{ stybas=FileBaseName[sty], in=" ", oug="wolframscript -api \"https://www.wolframcloud.com/obj/hle0002/syco/install\" -local"(* install/upgrade w/ original API *), sug="wolframscript -api \"https://wolfr.am/1k8pdkOvy\" -local"(* install/upgrade shortened API *), oui="wolframscript -api \"https://www.wolframcloud.com/obj/hle0002/syco/uninstall\" -local"(* uninstall w/ original API *), sui="wolframscript -api \"https://wolfr.am/1k8iyMNzS\" -local"(* uninstall shortened API *), wsc="https://reference.wolfram.com/language/ref/program/wolframscript.html"(* WolframScript web reference page *), mat="Mathematica" }, MessageDialog[ TextGrid[{ {"Unable to install/upgrade or uninstall the "<>wtit<>" suite because the \""<>stybas<>"\" stylesheet is being used and locked by some notebooks currently open in the front end, preventing it from deleting or overwriting."}, {"Close all such notebooks (including the current one), and reevaluate this function in a notebook not using the\.08 \""<>stybas<>"\" stylesheet. A fresh "<>mat<>" session may even be necessary."}, {"Alternatively, you can bypass "<>mat<>" entirely and run cloud API scripts from the command line"}, {ClickToCopy[in<>oug,sug]}(* problems when full APIs are copied, short ones used instead as expression copied *), {ClickToCopy[in<>oui,sui]}, {"to install/upgrade and uninstall the "<>wtit<>" suite, respectively. Customize escape characters in the text copied to your shell (note: command with a shortened cloud URL will be copied when clicked)."}, {Item[Row[{"Regarding the scripts, go to ",Hyperlink["WolframScript",wsc]," for more information."}]](* /Item *)} }, Alignment->Left,(*ItemSize->{Scaled[1],Automatic},*)Spacings->{0,{1.5,1.5,1.5,0.5,0,0.5,1.5}}](* /TextGrid *), WindowMargins->{{0,Automatic},{0,Automatic}},WindowSize->{Scaled[1/2],All},WindowTitle->wtit](* /MessageDialog *); ](* /With *) (**************** general error messages: ****************) General::nunsav = "This notebook has never been saved as a file. Save it and retry."; (**************** problem["name"]: ****************) problem::prbinv = "Only letters, digits, and leading /, *, /* allowable in \"`1`\", no diacritics or spaces permitted."; problem::prbnof = "Problem \"`1`\" not found on the `2` site."; problem::solnof = "Solution to problem \"`1`\" not found on the `2` site."; SyntaxInformation[problem]={"ArgumentsPattern"->{_}}; problem[ n_/;Or[ And[StringQ[n],StringReplace[n," "..->""]===n,RemoveDiacritics[n]===n, StringMatchQ[n,{WordCharacter..,"\\*"~~(WordCharacter..),"/"~~(WordCharacter..),"/\\*"~~(WordCharacter..)}] ], Throw[Message[problem::prbinv,n],"prbinv",#1&] ] ]:= Block[{ nd=Quiet[NotebookDirectory[]](* save notebook directory quietly *), ls(* local source *), rb=$urlbase<>ToLowerCase[$suitname]<>"/"(* remote base *), rs(* remote source *), st(* solution target directory *), n1=StringDrop[n,1],n2=StringDrop[n,2](* signaling characters removal *), fe={"NB","NotebookObject"}(* {format, element} *), nx=$nbx,px=$prosuf<>$nbx,sx=$solsuf<>$nbx(* plain notebook extension, problem and solution file suffix and extension *), zl=$zfmloc,zr=$zfmrem(* local and remote compression format *), no(* imported notebook object *), tn(* target notebook file name *) }, (* preparatory actions: *) And[nd===$Failed,Throw[Message[problem::nunsav],"nunsav",#1&]](* saved notebook allowed only *); ls=FileNameJoin[{nd,$prodir}](* local source *); rs=rb<>ToLowerCase[FileNameTake[ls]]<>"/"(* remote source *); st=If[MemberQ[$uniquetags,CurrentValue[EvaluationNotebook[],{TaggingRules,$uniquekey}],Heads->False], FileNameJoin[{nd,$suitname}](* solution target directory for those notebooks that contain a unique tag, .. *), nd(* .. and for those that don't *) ]; Or[DirectoryQ[st],CreateDirectory[st,CreateIntermediateDirectories->True]](* create solution target directory unless it exists *); (* import, uncompress and open notebook object: *) no=Switch[Characters[n], {"/","*",__},tn=FileNameJoin[{st,n2<>sx}];Quiet[Check[Import[FileNameJoin[{ls,n2<>sx<>zl}],fe],Message[problem::solnof,n2,"local"]],{Import::nffil}], {"/",__},tn=FileNameJoin[{st,n1<>nx}];Quiet[Check[Import[FileNameJoin[{ls,n1<>px<>zl}],fe],Message[problem::prbnof,n1,"local"]],{Import::nffil}], {"*",__},tn=FileNameJoin[{st,n1<>sx}];Quiet[Check[Import[URL[rs<>n1<>sx<>zr],fe],Message[problem::solnof,n1,"remote"]],{Syntax::sntx}], _,tn=FileNameJoin[{st,n<>nx}];Quiet[Check[Import[URL[rs<>n<>px<>zr],fe],Message[problem::prbnof,n,"remote"]],{Syntax::sntx}] ]; (* save notebook object to file with intended name: *) And[MatchQ[no,_NotebookObject],NotebookSave[no,tn,Interactive->True]]; ]; (**************** resource["filespec"]: ****************) resource::resinv = "Only letters, digits, ensuing dots, _'s, -'s, single /'s, leading ='s allowed in \"`1`\", no diacritics or spaces permitted."; resource::resnav = "Only \""<>StringRiffle[$avaidirs,", "]<>"\" directories available on the remote site."; SyntaxInformation[resource]={"ArgumentsPattern"->{_}}; resource[ n_/;Or[ And[StringQ[n],StringReplace[n," "..->""]===n,RemoveDiacritics[n]===n, StringMatchQ[n,{ (WordCharacter..~~("/"|"_"|"-"|".")...~~WordCharacter...)..(* filespec pattern *), "="~~(WordCharacter..~~("/"|"_"|"-"|".")...~~WordCharacter...)..(* =filespec pattern *) }] ], Throw[Message[resource::resinv,n]/.Null->$Failed,"resinv",#1&] ] ]:= Module[{ m=If[StringStartsQ[n,"="],StringDrop[n,1],n](* discard "=" if present at the beginning of 'n' *), d(* list of 'm' parts separated by '/' *), u=$urlbase(* URL base *), s=ToLowerCase[$suitname](* the Symbolical Computations brief base name in lowercase *), p(* joined dirs (most of the 'd' list joined), if any *), a(* assembled URL string *) }, d=StringSplit[m,"/"](* if '/' separator present, divide 'm' into dirs and file *); If[MemberQ[d,""],Throw[Message[resource::resinv,n]/.Null->$Failed,"resinv",#1&]](* no double '//' allowed *); a=If[Length[d]<2, (* if 'm' free of slash(es), use default $resdir directory to assembly URL string: *) u<>s<>"/"<>$resdir<>"/"<>m, (* if 'm' contains slash(es), use user supplied directory/ies to assembly URL string *) p=StringRiffle[Most[d],"/"]; Or[MemberQ[$avaidirs,p],Throw[Message[resource::resnav]/.Null->$Failed,"resnav",#1&]]; u<>s<>"/"<>p<>"/"<>Last[d] ]; If[StringStartsQ[n,"="],URL[a],a](* return URL[string] or string depending on whether 'n' starts with "=" or not *) ] (**************** submit["usr", "pwd", ccq], submit["usr"], submit[]: ****************) submit::usrinv = "Invalid CRO username \"`1`\"; 3 letters followed by 4 digits expected, no diacritics or spaces."; submit::pwdinv = "Invalid CRO password \"`1`\"; a string expected."; submit::chcinv = "Invalid Cc choice `1`; Boolean expected."; submit::fnminv = "Invalid attachment basename \"`1`\"; only letters, digits and underscores expected; no diacritics or spaces."; submit::crfail = "Creating or deleting `1` \"`2`\" failed for unclear reasons."; SyntaxInformation[submit]={"ArgumentsPattern"->{_.,_.,_.}}(* the 4th arg ignored intentionally: UNDOCUMENTED *); (* if all 1st, 2nd, 3rd regular args, and undocumented 4th arg are given: *) submit[ usr_/;Or[(* CRO username *) And[StringQ[usr],StringReplace[usr," "..->""]===usr,RemoveDiacritics[usr]===usr, StringMatchQ[usr, LetterCharacter~~LetterCharacter~~LetterCharacter~~DigitCharacter~~DigitCharacter~~DigitCharacter~~DigitCharacter] ], Throw[Message[submit::usrinv,usr],"usrinv",#1&] ], pwd_/;Or[(* CRO password *) StringQ[pwd], Throw[Message[submit::pwdinv,pwd],"pwdinv",#1&] ], ccq_/;Or[(* whether to include sender's address as Cc or not *) BooleanQ[ccq], Throw[Message[submit::chcinv,ccq],"chcinv",#1&] ], aus_/;Or[(* whether to append CRO username as suffix to basename or not: UNDOCUMENTED *) BooleanQ[aus], Throw[Message[submit::chcinv,aus],"chcinv",#1&] ] ]:= Module[{ suban=$suitname(* suite base name *), nbfil=Quiet[NotebookFileName[]](* save current evaluation notebook file name quietly *), lcusr=ToLowerCase[usr],ucusr=ToUpperCase[usr](* CRO username in lowercase and uppercase *), dom=$instdom(* institution domain *), to=$teacher(* addressee teacher *), cclist(* Cc recipient list *), from(* 'from' email address *), nbbas(* current evaluation notebook base name (neither path nor extension) *), nbext(* current evaluation notebook base name.ext or name_usr.ext (no path) *), sep="_"(* separator between attached notebook base name and CRO username *), nx=$nbx(* plain notebook extension *), prbid(* problem id *), artarg=$TemporaryDirectory(* current evaluation notebook archive location *), az=$zfmail(* current evaluation notebook archive compression format *), caret,smret(* CreateArchive and SendMail return value *) }, (* preparatory actions: *) And[nbfil===$Failed,Throw[Message[submit::nunsav],"nunsav",#1&]](* saved notebook allowed only *); nbbas=FileBaseName[nbfil](* current evaluation notebook base name (neither path nor extension) *); Or[(* letters and digits, no diacritics or spaces *) And[StringReplace[nbbas," "..->""]===nbbas(* redundant (' 's not matched in vvvv), possibly faster *),nbbas=!="", RemoveDiacritics[nbbas]===nbbas,StringMatchQ[nbbas,((WordCharacter...)~~("_"...)~~(WordCharacter...))..]], Throw[Message[submit::fnminv,nbbas],"fnminv",#1&] ]; If[aus===True, nbext=nbbas<>sep<>lcusr<>nx(* current evaluation notebook base name_usr.ext (no path) *), nbext=FileNameTake[nbfil](* current evaluation notebook base name.ext (no path) *) ]; from=lcusr<>"@"<>dom(* sender's email address assembly *); to=to<>dom(* recipient's email address assembly *); cclist=If[ccq,{from},{}](* the Cc address *); prbid=suban<>"/"<>nbbas(* problem id: concatenation of 'suban' and 'nbbas' *); FrontEndTokenExecute["DeleteGeneratedCells"](* delete all output cells *); CurrentValue[EvaluationNotebook[],DockedCells]=Inherited(* delete all docked cells *); NotebookSave[EvaluationNotebook[]](* save current evaluation notebook *); If[(caret=CreateArchive[(* compress current evaluation notebook - attachment w/o CRO username suffix *) <|nbext->nbfil|>(* current evaluation notebook (including renaming that only has sense for name_usr.ext) *), FileNameJoin[{artarg,nbext<>az}](* current evaluation notebook archive *), "CompressionLevel"->1,CreateIntermediateDirectories->True,OverwriteTarget->True])===$Failed, Throw[Message[submit::crfail,"archive",FileNameJoin[{artarg,nbext<>az}]],"crfail",#1&] ]; (* sending mail with attachment: *) smret=SendMail[<| "To"->{to}, "Cc"->cclist, "Subject"->prbid, "Body"->{StringJoin[ ucusr," presents a worked out solution to the \"",prbid,"\" problem.", "\n\nSelected global system information:", "\n$Version: ",$Version, "\n$MachineType: ",$MachineType, "\n$ProcessorType: ",$ProcessorType, "\n$ProcessorCount: ",ToString[$ProcessorCount], "\n$SystemWordLength: ",ToString[$SystemWordLength], "\n$ByteOrdering: ",ToString[$ByteOrdering], "\n$SystemCharacterEncoding: ",$SystemCharacterEncoding ]}, "Server"->"smtp."<>dom, "PortNumber"->465, "EncryptionProtocol"->"SSL", "Username"->lcusr, "FromAddress"->from, "FromName"->lcusr, "ReplyTo"->from, "Password"->pwd, "HTMLBody"->EmbeddedHTML[ "

Sent from Wolfram Mathematica using the SendMail function.

" ], "AttachedFiles"->{caret}(* attach compressed current evaluation notebook file saved w/o output/docked cells *), MailAddressValidation->(StringEndsQ[#,dom]&) |> ]; (* keep things tidy: *) And[FileExistsQ[caret], If[DeleteFile[caret]===$Failed, Throw[Message[submit::crfail,"archive",caret],"crfail",#1&] ] ]; smret(* return value comes from SendMail *) ](* /Module *) (* if only 1st, 2nd and 3rd regular args given, default value is substituted for undocumented 4th arg: *) submit[ usr_(* CRO username *), pwd_(* CRO password *), ccq_(* whether to include sender's address as Cc or not *) ]:= submit[usr,pwd,ccq,$ausdef](* see 'repeatedly used variables' section above for $ausdef *) (* if only 1st and 2nd regular args given, remaining 3rd regular arg is requested in dialog, default value is subst. for undocumented 4th arg: *) submit[ usr_(* CRO username *), pwd_(* CRO password *) ]:= submit[usr,pwd,$includeCc[$suitname,usr,$instdom],$ausdef](* see 'repeatedly used variables' section above for $ausdef *) (* if only 1st regular arg given, remaining 2nd and 3rd reg. args are requested in dialogs, default value is subst. for undocumented 4th arg: *) submit[ usr_(* CRO username *) ]:= With[{pwd=$authWithCRO["Password",$suitname,$mycro,$croabout]}, If[pwd=!=$Canceled, submit[usr,pwd,$includeCc[$suitname,usr,$instdom],$ausdef](* see 'repeatedly used variables' section above for $ausdef *), pwd ] ] (* if no regular arg given, all 1st, 2nd and 3rd args are requested in dialogs, default value is subst. for undocumented 4th arg: *) submit[]:= With[{uspw=$authWithCRO["UsernamePassword",$suitname,$mycro,$croabout]}, If[uspw=!=$Canceled, submit[uspw,$includeCc[$suitname,First[{uspw}],$instdom],$ausdef](* see 'repeatedly used variables' section above for $ausdef *), uspw ] ] (**************** uninstall[]: ****************) SyntaxInformation[uninstall]={"ArgumentsPattern"->{}}(* the 'dummy' arg ignored intentionally *); uninstall[ Optional[dummy_?BooleanQ,False](* use default False when evaluated within 'install', True when evaluated as a standalone function *) ]:= Module[{ n=$suitname(* suite base name *), w=$pkgext(* package extension *), g=$pkgdir(* package path under $UserBaseDirectory *), e=$stysuf<>$styext(* stylesheet suffix and extension *), y=$stydir(* stylesheet path under $UserBaseDirectory *), r=$UserBaseDirectory(* base directory in which user-specific files are placed *), x={}(* initial list of components to be uninstalled *), p,s }, p=FileNameJoin[{r,g,n<>w}](* package installation location *); s=FileNameJoin[{r,y,n<>e}](* stylesheet installation location *); If[MemberQ[StyleDefinitions/.Map[Options[#,StyleDefinitions]&,Notebooks[],Heads->False],n<>e], Throw[$nbksContainStyle[n<>e,n],"stylck",#1&] ]; And[FileExistsQ[p](* interactively agree package to be uninstalled if found installed *), If[ChoiceDialog["Sure to uninstall package\n\""<>p<>"\"?",WindowSize->{Automatic,All},WindowTitle->n],AppendTo[x,p]]]; And[FileExistsQ[s](* interactively agree stylesheet to be uninstalled if found installed *), If[ChoiceDialog["Sure to uninstall stylesheet\n\""<>s<>"\"?",WindowSize->{Automatic,All},WindowTitle->n],AppendTo[x,s]]]; If[x=!={}(* carry out uninstallation if at least one component has been agreed to be uninstalled *), DeleteFile[x]; MessageDialog[Column[{ n<>" suite component(s)",Sequence@@("\""<>#<>"\""&/@x),If[dummy,"installed/upgraded and reloaded.","uninstalled."] }],WindowSize->{Automatic,All},WindowTitle->n], MessageDialog[If[dummy,"No "<>n<>" components have been installed/upgraded.","No "<>n<>" components have been uninstalled."],WindowTitle->n] ]; If[dummy,x](* return list of uninstalled components 'x' if 'dummy' is True, return Null otherwise *) ] (**************** install[], install[All]: ****************) SyntaxInformation[install]={"ArgumentsPattern"->{_.}}; install[]:= ( Module[{ x=uninstall[True](* uninstalls the SyCo suite components interactively and saves list of uninstalled components *), n=$suitname(* suite base name *), w=$pkgext(* package extension *), g=$pkgdir(* package path under $UserBaseDirectory *), e=$stysuf<>$styext(* stylesheet suffix and extension *), y=$stydir(* stylesheet path under $UserBaseDirectory *), r=$UserBaseDirectory(* base directory in which user-specific files are placed *), p,s,u }, p=FileNameJoin[{r,g,n<>w}](* package installation target *); s=FileNameJoin[{r,y,n<>e}](* stylesheet installation target *); u=$urlbase<>ToLowerCase[n]<>"/"<>$resdir<>"/"(* remote installation source *); If[x=!={},(* reinstall uninstalled components *) Map[Or[FileExistsQ[#],CopyFile[URL[u<>FileNameTake[#]],#,OverwriteTarget->False]]&,x,Heads->False] ]; ]; Get[$suitname<>"`"](* coerced package reload *); ) install[All]:= ( Module[{ n=$suitname(* suite base name *), w=$pkgext(* package extension *), g=$pkgdir(* package path under $UserBaseDirectory *), e=$stysuf<>$styext(* stylesheet suffix and extension *), y=$stydir(* stylesheet path under $UserBaseDirectory *), r=$UserBaseDirectory(* base directory in which user-specific files are placed *), p,s,u }, p=FileNameJoin[{r,g,n<>w}](* package installation target *); s=FileNameJoin[{r,y,n<>e}](* stylesheet installation target *); u=$urlbase<>ToLowerCase[n]<>"/"<>$resdir<>"/"(* remote installation source *); If[MemberQ[StyleDefinitions/.Map[Options[#,StyleDefinitions]&,Notebooks[],Heads->False],n<>e], Throw[$nbksContainStyle[n<>e,n],"stylck",#1&] ]; Map[CopyFile[URL[u<>FileNameTake[#]],#,OverwriteTarget->True]&,{p,s},Heads->False](* overwrite all components *); MessageDialog["All "<>n<>" components have been installed/upgraded.",WindowTitle->n] ]; Get[$suitname<>"`"](* coerced package reload *); ) With[{syms = Apply[Join,Map[Names,{$suitname<>"`*"},Heads->False],Heads->False]}, SetAttributes[syms, {Protected, ReadProtected}] ]; End[(*"`Private`"*)]; EndPackage[(*"SyCo`"*)];