organizr.class.php 271 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585458645874588458945904591459245934594459545964597459845994600460146024603460446054606460746084609461046114612461346144615461646174618461946204621462246234624462546264627462846294630463146324633463446354636463746384639464046414642464346444645464646474648464946504651465246534654465546564657465846594660466146624663466446654666466746684669467046714672467346744675467646774678467946804681468246834684468546864687468846894690469146924693469446954696469746984699470047014702470347044705470647074708470947104711471247134714471547164717471847194720472147224723472447254726472747284729473047314732473347344735473647374738473947404741474247434744474547464747474847494750475147524753475447554756475747584759476047614762476347644765476647674768476947704771477247734774477547764777477847794780478147824783478447854786478747884789479047914792479347944795479647974798479948004801480248034804480548064807480848094810481148124813481448154816481748184819482048214822482348244825482648274828482948304831483248334834483548364837483848394840484148424843484448454846484748484849485048514852485348544855485648574858485948604861486248634864486548664867486848694870487148724873487448754876487748784879488048814882488348844885488648874888488948904891489248934894489548964897489848994900490149024903490449054906490749084909491049114912491349144915491649174918491949204921492249234924492549264927492849294930493149324933493449354936493749384939494049414942494349444945494649474948494949504951495249534954495549564957495849594960496149624963496449654966496749684969497049714972497349744975497649774978497949804981498249834984498549864987498849894990499149924993499449954996499749984999500050015002500350045005500650075008500950105011501250135014501550165017501850195020502150225023502450255026502750285029503050315032503350345035503650375038503950405041504250435044504550465047504850495050505150525053505450555056505750585059506050615062506350645065506650675068506950705071507250735074507550765077507850795080508150825083508450855086508750885089509050915092509350945095509650975098509951005101510251035104510551065107510851095110511151125113511451155116511751185119512051215122512351245125512651275128512951305131513251335134513551365137513851395140514151425143514451455146514751485149515051515152515351545155515651575158515951605161516251635164516551665167516851695170517151725173517451755176517751785179518051815182518351845185518651875188518951905191519251935194519551965197519851995200520152025203520452055206520752085209521052115212521352145215521652175218521952205221522252235224522552265227522852295230523152325233523452355236523752385239524052415242524352445245524652475248524952505251525252535254525552565257525852595260526152625263526452655266526752685269527052715272527352745275527652775278527952805281528252835284528552865287528852895290529152925293529452955296529752985299530053015302530353045305530653075308530953105311531253135314531553165317531853195320532153225323532453255326532753285329533053315332533353345335533653375338533953405341534253435344534553465347534853495350535153525353535453555356535753585359536053615362536353645365536653675368536953705371537253735374537553765377537853795380538153825383538453855386538753885389539053915392539353945395539653975398539954005401540254035404540554065407540854095410541154125413541454155416541754185419542054215422542354245425542654275428542954305431543254335434543554365437543854395440544154425443544454455446544754485449545054515452545354545455545654575458545954605461546254635464546554665467546854695470547154725473547454755476547754785479548054815482548354845485548654875488548954905491549254935494549554965497549854995500550155025503550455055506550755085509551055115512551355145515551655175518551955205521552255235524552555265527552855295530553155325533553455355536553755385539554055415542554355445545554655475548554955505551555255535554555555565557555855595560556155625563556455655566556755685569557055715572557355745575557655775578557955805581558255835584558555865587558855895590559155925593559455955596559755985599560056015602560356045605560656075608560956105611561256135614561556165617561856195620562156225623562456255626562756285629563056315632563356345635563656375638563956405641564256435644564556465647564856495650565156525653565456555656565756585659566056615662566356645665566656675668566956705671567256735674567556765677567856795680568156825683568456855686568756885689569056915692569356945695569656975698569957005701570257035704570557065707570857095710571157125713571457155716571757185719572057215722572357245725572657275728572957305731573257335734573557365737573857395740574157425743574457455746574757485749575057515752575357545755575657575758575957605761576257635764576557665767576857695770577157725773577457755776577757785779578057815782578357845785578657875788578957905791579257935794579557965797579857995800580158025803580458055806580758085809581058115812581358145815581658175818581958205821582258235824582558265827582858295830583158325833583458355836583758385839584058415842584358445845584658475848584958505851585258535854585558565857585858595860586158625863586458655866586758685869587058715872587358745875587658775878587958805881588258835884588558865887588858895890589158925893589458955896589758985899590059015902590359045905590659075908590959105911591259135914591559165917591859195920592159225923592459255926592759285929593059315932593359345935593659375938593959405941594259435944594559465947594859495950595159525953595459555956595759585959596059615962596359645965596659675968596959705971597259735974597559765977597859795980598159825983598459855986598759885989599059915992599359945995599659975998599960006001600260036004600560066007600860096010601160126013601460156016601760186019602060216022602360246025602660276028602960306031603260336034603560366037603860396040604160426043604460456046604760486049605060516052605360546055605660576058605960606061606260636064606560666067606860696070607160726073607460756076607760786079608060816082608360846085608660876088608960906091609260936094609560966097609860996100610161026103610461056106610761086109611061116112611361146115611661176118611961206121612261236124612561266127612861296130613161326133613461356136613761386139614061416142614361446145614661476148614961506151615261536154615561566157615861596160616161626163616461656166616761686169617061716172617361746175617661776178617961806181618261836184618561866187618861896190619161926193619461956196619761986199620062016202620362046205620662076208620962106211621262136214621562166217621862196220622162226223622462256226622762286229623062316232623362346235623662376238623962406241624262436244624562466247624862496250625162526253625462556256625762586259626062616262626362646265626662676268626962706271627262736274627562766277627862796280628162826283628462856286628762886289629062916292629362946295629662976298629963006301630263036304630563066307630863096310631163126313631463156316631763186319632063216322632363246325632663276328632963306331633263336334633563366337633863396340634163426343634463456346634763486349635063516352635363546355635663576358635963606361636263636364636563666367636863696370637163726373637463756376637763786379638063816382638363846385638663876388638963906391639263936394639563966397639863996400640164026403640464056406640764086409641064116412641364146415641664176418641964206421642264236424642564266427642864296430643164326433643464356436643764386439644064416442644364446445644664476448644964506451645264536454645564566457645864596460646164626463646464656466646764686469647064716472647364746475647664776478647964806481648264836484648564866487648864896490649164926493649464956496649764986499650065016502650365046505650665076508650965106511651265136514651565166517651865196520652165226523652465256526652765286529653065316532653365346535653665376538653965406541654265436544654565466547654865496550655165526553655465556556655765586559656065616562656365646565656665676568656965706571657265736574657565766577657865796580658165826583658465856586658765886589659065916592659365946595659665976598659966006601660266036604660566066607660866096610661166126613661466156616661766186619662066216622662366246625662666276628662966306631663266336634663566366637663866396640664166426643664466456646664766486649665066516652665366546655665666576658665966606661666266636664666566666667666866696670667166726673667466756676667766786679668066816682668366846685668666876688668966906691669266936694669566966697669866996700670167026703670467056706670767086709671067116712671367146715671667176718671967206721672267236724672567266727672867296730673167326733673467356736673767386739674067416742674367446745674667476748674967506751675267536754675567566757675867596760676167626763676467656766676767686769677067716772677367746775677667776778677967806781678267836784678567866787678867896790679167926793679467956796679767986799680068016802680368046805680668076808680968106811681268136814681568166817681868196820682168226823682468256826682768286829683068316832683368346835683668376838683968406841684268436844684568466847684868496850685168526853685468556856685768586859686068616862686368646865686668676868686968706871687268736874687568766877687868796880688168826883688468856886688768886889689068916892689368946895689668976898689969006901690269036904690569066907690869096910691169126913691469156916691769186919692069216922692369246925692669276928692969306931693269336934693569366937693869396940694169426943694469456946694769486949695069516952695369546955695669576958695969606961696269636964696569666967696869696970697169726973697469756976697769786979698069816982698369846985698669876988698969906991699269936994699569966997699869997000700170027003700470057006700770087009701070117012701370147015701670177018701970207021702270237024702570267027702870297030703170327033703470357036703770387039704070417042704370447045704670477048704970507051705270537054705570567057705870597060706170627063706470657066706770687069707070717072707370747075707670777078707970807081708270837084708570867087708870897090709170927093709470957096709770987099710071017102710371047105710671077108710971107111711271137114711571167117711871197120712171227123712471257126712771287129713071317132713371347135713671377138713971407141714271437144714571467147714871497150715171527153715471557156715771587159716071617162716371647165716671677168716971707171717271737174717571767177717871797180718171827183718471857186718771887189719071917192719371947195719671977198719972007201720272037204720572067207720872097210721172127213721472157216721772187219722072217222722372247225722672277228722972307231723272337234723572367237723872397240724172427243724472457246724772487249725072517252725372547255725672577258725972607261726272637264726572667267726872697270727172727273727472757276727772787279728072817282728372847285728672877288728972907291729272937294729572967297729872997300730173027303730473057306730773087309731073117312731373147315731673177318731973207321732273237324732573267327732873297330733173327333733473357336733773387339734073417342734373447345734673477348734973507351735273537354735573567357735873597360736173627363736473657366736773687369737073717372737373747375737673777378737973807381738273837384738573867387738873897390739173927393739473957396739773987399740074017402740374047405740674077408740974107411741274137414741574167417741874197420742174227423742474257426742774287429743074317432743374347435743674377438743974407441744274437444744574467447744874497450745174527453745474557456745774587459746074617462746374647465746674677468746974707471747274737474747574767477747874797480748174827483748474857486748774887489749074917492749374947495749674977498749975007501750275037504750575067507750875097510751175127513751475157516751775187519752075217522752375247525752675277528752975307531753275337534753575367537753875397540754175427543754475457546754775487549755075517552755375547555755675577558755975607561756275637564756575667567756875697570757175727573757475757576757775787579758075817582758375847585758675877588758975907591759275937594759575967597759875997600760176027603760476057606760776087609761076117612761376147615761676177618761976207621762276237624762576267627762876297630763176327633763476357636763776387639764076417642764376447645764676477648764976507651765276537654765576567657765876597660766176627663766476657666766776687669767076717672767376747675767676777678767976807681768276837684768576867687768876897690769176927693769476957696769776987699770077017702770377047705770677077708770977107711771277137714771577167717771877197720772177227723772477257726772777287729773077317732773377347735773677377738773977407741774277437744774577467747774877497750775177527753775477557756775777587759776077617762776377647765776677677768776977707771777277737774777577767777777877797780778177827783778477857786778777887789779077917792779377947795779677977798779978007801780278037804780578067807780878097810781178127813781478157816781778187819782078217822782378247825782678277828782978307831783278337834783578367837783878397840784178427843784478457846784778487849785078517852785378547855785678577858785978607861786278637864786578667867786878697870787178727873787478757876787778787879788078817882788378847885788678877888788978907891789278937894789578967897789878997900790179027903790479057906790779087909791079117912791379147915791679177918791979207921792279237924792579267927792879297930793179327933793479357936793779387939794079417942794379447945794679477948794979507951795279537954795579567957795879597960796179627963796479657966796779687969797079717972797379747975797679777978797979807981798279837984798579867987798879897990799179927993799479957996799779987999800080018002800380048005800680078008800980108011801280138014801580168017801880198020802180228023802480258026802780288029803080318032803380348035803680378038803980408041804280438044804580468047804880498050805180528053805480558056805780588059806080618062806380648065806680678068806980708071807280738074807580768077807880798080808180828083808480858086808780888089809080918092809380948095809680978098809981008101810281038104810581068107810881098110811181128113811481158116811781188119812081218122812381248125812681278128812981308131813281338134813581368137813881398140814181428143814481458146814781488149815081518152815381548155815681578158815981608161816281638164816581668167816881698170817181728173817481758176817781788179818081818182818381848185818681878188818981908191819281938194819581968197819881998200820182028203820482058206820782088209821082118212821382148215821682178218821982208221822282238224822582268227822882298230823182328233823482358236823782388239824082418242824382448245824682478248824982508251825282538254825582568257825882598260826182628263826482658266826782688269827082718272827382748275827682778278827982808281828282838284828582868287828882898290829182928293829482958296829782988299830083018302830383048305830683078308830983108311831283138314831583168317831883198320832183228323832483258326832783288329833083318332833383348335833683378338833983408341834283438344834583468347834883498350835183528353835483558356835783588359836083618362836383648365836683678368836983708371837283738374837583768377837883798380838183828383838483858386838783888389839083918392839383948395839683978398839984008401840284038404840584068407840884098410841184128413841484158416841784188419842084218422842384248425842684278428842984308431843284338434843584368437843884398440844184428443844484458446844784488449845084518452845384548455845684578458845984608461846284638464846584668467846884698470847184728473847484758476847784788479848084818482848384848485848684878488848984908491849284938494849584968497849884998500850185028503850485058506850785088509851085118512851385148515851685178518851985208521852285238524852585268527852885298530853185328533853485358536853785388539854085418542854385448545854685478548854985508551855285538554855585568557855885598560856185628563856485658566856785688569857085718572857385748575857685778578857985808581858285838584858585868587858885898590859185928593859485958596859785988599860086018602860386048605860686078608860986108611861286138614861586168617861886198620862186228623862486258626862786288629863086318632863386348635863686378638863986408641864286438644864586468647864886498650865186528653865486558656865786588659866086618662866386648665866686678668866986708671867286738674867586768677867886798680868186828683868486858686868786888689869086918692869386948695869686978698869987008701870287038704870587068707870887098710871187128713871487158716871787188719872087218722872387248725872687278728872987308731873287338734873587368737873887398740874187428743874487458746874787488749875087518752875387548755875687578758875987608761876287638764876587668767876887698770877187728773877487758776877787788779878087818782878387848785878687878788878987908791879287938794879587968797879887998800880188028803880488058806880788088809881088118812881388148815881688178818881988208821882288238824882588268827882888298830883188328833883488358836883788388839884088418842884388448845884688478848884988508851885288538854885588568857885888598860886188628863886488658866886788688869887088718872887388748875887688778878887988808881888288838884888588868887888888898890889188928893889488958896889788988899890089018902890389048905890689078908890989108911891289138914891589168917891889198920892189228923892489258926892789288929893089318932893389348935893689378938893989408941894289438944894589468947894889498950895189528953895489558956895789588959896089618962896389648965896689678968896989708971897289738974897589768977897889798980898189828983898489858986898789888989899089918992899389948995899689978998899990009001900290039004900590069007900890099010901190129013901490159016901790189019902090219022902390249025902690279028902990309031903290339034903590369037903890399040904190429043904490459046904790489049905090519052905390549055905690579058905990609061906290639064906590669067906890699070907190729073907490759076907790789079908090819082908390849085908690879088908990909091909290939094909590969097909890999100910191029103910491059106910791089109911091119112911391149115911691179118911991209121912291239124
  1. <?php
  2. use Dibi\Connection;
  3. class Organizr
  4. {
  5. // Use Custom Functions From Traits;
  6. use TwoFAFunctions;
  7. use ApiFunctions;
  8. use AuthFunctions;
  9. use BackupFunctions;
  10. use ConfigFunctions;
  11. use HomepageConnectFunctions;
  12. use HomepageFunctions;
  13. use LogFunctions;
  14. use NetDataFunctions;
  15. use NormalFunctions;
  16. use OptionsFunction;
  17. use OrganizrFunctions;
  18. use PluginFunctions;
  19. use StaticFunctions;
  20. use SSOFunctions;
  21. use TokenFunctions;
  22. use UpdateFunctions;
  23. use UpgradeFunctions;
  24. // Use homepage item functions
  25. use CalendarHomepageItem;
  26. use CouchPotatoHomepageItem;
  27. use DelugeHomepageItem;
  28. use EmbyHomepageItem;
  29. use HealthChecksHomepageItem;
  30. use ICalHomepageItem;
  31. use JDownloaderHomepageItem;
  32. use JellyfinHomepageItem;
  33. use LidarrHomepageItem;
  34. use MonitorrHomepageItem;
  35. use NetDataHomepageItem;
  36. use NZBGetHomepageItem;
  37. use OctoPrintHomepageItem;
  38. use OmbiHomepageItem;
  39. use PiHoleHomepageItem;
  40. use PlexHomepageItem;
  41. use QBitTorrentHomepageItem;
  42. use RadarrHomepageItem;
  43. use RTorrentHomepageItem;
  44. use SabNZBdHomepageItem;
  45. use SickRageHomepageItem;
  46. use SonarrHomepageItem;
  47. use SpeedTestHomepageItem;
  48. use TautulliHomepageItem;
  49. use TransmissionHomepageItem;
  50. use UnifiHomepageItem;
  51. use WeatherHomepageItem;
  52. // ===================================
  53. // Organizr Version
  54. public $version = '2.1.0';
  55. // ===================================
  56. // Quick php Version check
  57. public $minimumPHP = '7.2';
  58. // ===================================
  59. protected $db;
  60. protected $otherDb;
  61. public $config;
  62. public $user;
  63. public $userConfigPath;
  64. public $defaultConfigPath;
  65. public $currentTime;
  66. public $docker;
  67. public $dev;
  68. public $demo;
  69. public $commit;
  70. public $fileHash;
  71. public $cookieName;
  72. public $organizrLog;
  73. public $organizrLoginLog;
  74. public $timeExecution;
  75. public $root;
  76. public $paths;
  77. public $updating;
  78. public function __construct($updating = false)
  79. {
  80. // First Check PHP Version
  81. $this->checkPHP();
  82. // Constructed from Updater?
  83. $this->updating = $updating;
  84. // Set Project Root directory
  85. $this->root = dirname(__DIR__, 2);
  86. // Set Start Execution Time
  87. $this->timeExecution = $this->timeExecution();
  88. // Set location path to user config path
  89. $this->userConfigPath = dirname(__DIR__, 1) . DIRECTORY_SEPARATOR . 'config' . DIRECTORY_SEPARATOR . 'config.php';
  90. // Set location path to default config path
  91. $this->defaultConfigPath = dirname(__DIR__, 1) . DIRECTORY_SEPARATOR . 'config' . DIRECTORY_SEPARATOR . 'default.php';
  92. // Set current time
  93. $this->currentTime = gmdate("Y-m-d\TH:i:s\Z");
  94. // Set variable if install is for official docker
  95. $this->docker = (file_exists(dirname(__DIR__, 2) . DIRECTORY_SEPARATOR . 'Docker.txt'));
  96. // Set variable if install is for develop
  97. $this->dev = (file_exists(dirname(__DIR__, 2) . DIRECTORY_SEPARATOR . 'Dev.txt'));
  98. // Set variable if install is for demo
  99. $this->demo = (file_exists(dirname(__DIR__, 2) . DIRECTORY_SEPARATOR . 'Demo.txt'));
  100. // Set variable if install has commit hash
  101. $this->commit = ($this->docker && !$this->dev) ? file_get_contents(dirname(__DIR__, 2) . DIRECTORY_SEPARATOR . 'Github.txt') : null;
  102. // Set variable to be used as hash for files
  103. $this->fileHash = ($this->commit) ?? $this->version;
  104. // Load Config file
  105. $this->config = $this->config();
  106. // Set organizr Log file location
  107. $this->organizrLog = ($this->hasDB()) ? $this->config['dbLocation'] . 'organizrLog.json' : false;
  108. // Set organizr Login Log file location
  109. $this->organizrLoginLog = ($this->hasDB()) ? $this->config['dbLocation'] . 'organizrLoginLog.json' : false;
  110. // Set Paths
  111. $this->paths = array(
  112. 'Root Folder' => dirname(__DIR__, 2) . DIRECTORY_SEPARATOR,
  113. 'API Folder' => dirname(__DIR__, 1) . DIRECTORY_SEPARATOR,
  114. 'DB Folder' => ($this->hasDB()) ? $this->config['dbLocation'] : false
  115. );
  116. // Connect to DB
  117. $this->connectDB();
  118. // Set cookie name for Organizr Instance
  119. $this->cookieName = ($this->hasDB()) ? $this->config['uuid'] !== '' ? 'organizr_token_' . $this->config['uuid'] : 'organizr_token_temp' : 'organizr_token_temp';
  120. // Get token form cookie and validate
  121. $this->user = $this->hasCookie() ? $this->validateToken($_COOKIE[$this->cookieName]) ?? $this->guestUser() : $this->guestUser();
  122. // might just run this at index
  123. $this->upgradeCheck();
  124. // Is Page load Organizr OAuth?
  125. $this->checkForOrganizrOAuth();
  126. }
  127. protected function connectDB()
  128. {
  129. if ($this->hasDB()) {
  130. try {
  131. $this->db = new Connection([
  132. 'driver' => 'sqlite3',
  133. 'database' => $this->config['dbLocation'] . $this->config['dbName'],
  134. ]);
  135. } catch (Dibi\Exception $e) {
  136. $this->db = null;
  137. }
  138. } else {
  139. $this->db = null;
  140. }
  141. }
  142. public function connectOtherDB($file = null)
  143. {
  144. $file = $file ?? $this->config['dbLocation'] . 'tempMigration.db';
  145. try {
  146. $this->otherDb = new Connection([
  147. 'driver' => 'sqlite3',
  148. 'database' => $file,
  149. ]);
  150. } catch (Dibi\Exception $e) {
  151. $this->otherDb = null;
  152. }
  153. }
  154. public function checkForOrganizrOAuth()
  155. {
  156. // Oauth?
  157. if ($this->config['authProxyEnabled'] && $this->config['authProxyHeaderName'] !== '' && $this->config['authProxyWhitelist'] !== '') {
  158. if (isset(getallheaders()[$this->config['authProxyHeaderName']])) {
  159. $this->coookieSeconds('set', 'organizrOAuth', 'true', 20000, false);
  160. }
  161. }
  162. }
  163. public function auth()
  164. {
  165. if ($this->hasDB()) {
  166. $whitelist = isset($_GET['whitelist']) ? $_GET['whitelist'] : false;
  167. $blacklist = isset($_GET['blacklist']) ? $_GET['blacklist'] : false;
  168. $group = 0;
  169. $groupParam = ($_GET['group']) ?? 0;
  170. $redirect = false;
  171. if (isset($groupParam)) {
  172. if (is_numeric($groupParam)) {
  173. $group = (int)$groupParam;
  174. } else {
  175. $group = $this->getTabGroupByTabName($groupParam);
  176. }
  177. }
  178. $currentIP = $this->userIP();
  179. $unlocked = ($this->user['locked'] == '1') ? false : true;
  180. if (isset($this->user)) {
  181. $currentUser = $this->user['username'];
  182. $currentGroup = $this->user['groupID'];
  183. $currentEmail = $this->user['email'];
  184. } else {
  185. $currentUser = 'Guest';
  186. $currentGroup = $this->getUserLevel();
  187. $currentEmail = 'guest@guest.com';
  188. }
  189. $userInfo = "User: $currentUser | Group: $currentGroup | IP: $currentIP | Requesting Access to Group $group | Result: ";
  190. if ($whitelist) {
  191. if (in_array($currentIP, $this->arrayIP($whitelist))) {
  192. $this->setAPIResponse('success', 'User is whitelisted', 200);
  193. }
  194. }
  195. if ($blacklist) {
  196. if (in_array($currentIP, $this->arrayIP($blacklist))) {
  197. $this->setAPIResponse('error', $userInfo . ' User is blacklisted', 401);
  198. }
  199. }
  200. if ($group !== null) {
  201. if ((isset($_SERVER['HTTP_X_FORWARDED_SERVER']) && $_SERVER['HTTP_X_FORWARDED_SERVER'] == 'traefik') || $this->config['traefikAuthEnable']) {
  202. $redirect = 'Location: ' . $this->getServerPath();
  203. }
  204. if ($this->qualifyRequest($group) && $unlocked) {
  205. header("X-Organizr-User: $currentUser");
  206. header("X-Organizr-Email: $currentEmail");
  207. $this->setAPIResponse('success', $userInfo . ' User is Authorized', 200);
  208. } else {
  209. if (!$redirect) {
  210. $this->setAPIResponse('error', $userInfo . ' User is not Authorized or User is locked', 401);
  211. } else {
  212. exit(http_response_code(401) . header($redirect));
  213. }
  214. }
  215. } else {
  216. $this->setAPIResponse('error', 'Missing info', 401);
  217. }
  218. }
  219. return true;
  220. }
  221. public function setAPIResponse($result = null, $message = null, $responseCode = null, $data = null)
  222. {
  223. if ($result) {
  224. $GLOBALS['api']['response']['result'] = $result;
  225. }
  226. if ($message) {
  227. $GLOBALS['api']['response']['message'] = $message;
  228. }
  229. if ($responseCode) {
  230. $GLOBALS['responseCode'] = $responseCode;
  231. }
  232. if ($data) {
  233. $GLOBALS['api']['response']['data'] = $data;
  234. }
  235. }
  236. public function checkRoute($request)
  237. {
  238. $route = $request->getUri()->getPath();
  239. $method = $request->getMethod();
  240. $data = $this->apiData($request);
  241. if (!in_array($route, $GLOBALS['bypass'])) {
  242. if ($this->isApprovedRequest($method, $data) === false) {
  243. $this->setAPIResponse('error', 'Not authorized', 401);
  244. $this->writeLog('success', 'Killed Attack From [' . (isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : 'No Referer') . ']', $this->user['username']);
  245. return false;
  246. }
  247. }
  248. return true;
  249. }
  250. public function apiData($request)
  251. {
  252. switch ($request->getMethod()) {
  253. case 'POST':
  254. if ($request->getHeaderLine('Content-Type') == 'application/json') {
  255. return json_decode(file_get_contents('php://input', 'r'), true);
  256. } else {
  257. return $request->getParsedBody();
  258. }
  259. default:
  260. if ($request->getHeaderLine('Content-Type') == 'application/json') {
  261. return json_decode(file_get_contents('php://input', 'r'), true);
  262. } else {
  263. return null;
  264. }
  265. }
  266. }
  267. public function getPlugins()
  268. {
  269. if ($this->hasDB()) {
  270. $pluginList = [];
  271. foreach ($GLOBALS['plugins'] as $plugin) {
  272. foreach ($plugin as $key => $value) {
  273. if (strpos($value['license'], $this->config['license']) !== false) {
  274. $plugin[$key]['enabled'] = $this->config[$value['configPrefix'] . '-enabled'];
  275. $pluginList[$key] = $plugin[$key];
  276. }
  277. }
  278. }
  279. return $pluginList;
  280. }
  281. return false;
  282. }
  283. public function refreshCookieName()
  284. {
  285. $this->cookieName = $this->config['uuid'] !== '' ? 'organizr_token_' . $this->config['uuid'] : 'organizr_token_temp';
  286. }
  287. public function favIcons()
  288. {
  289. $favicon = '
  290. <link rel="apple-touch-icon" sizes="180x180" href="plugins/images/favicon/apple-touch-icon.png">
  291. <link rel="icon" type="image/png" sizes="32x32" href="plugins/images/favicon/favicon-32x32.png">
  292. <link rel="icon" type="image/png" sizes="16x16" href="plugins/images/favicon/favicon-16x16.png">
  293. <link rel="manifest" href="plugins/images/favicon/site.webmanifest">
  294. <link rel="mask-icon" href="plugins/images/favicon/safari-pinned-tab.svg" color="#5bbad5">
  295. <link rel="shortcut icon" href="plugins/images/favicon/favicon.ico">
  296. <meta name="msapplication-TileColor" content="#da532c">
  297. <meta name="msapplication-TileImage" content="plugins/images/favicon/mstile-144x144.png">
  298. <meta name="msapplication-config" content="plugins/images/favicon/browserconfig.xml">
  299. <meta name="theme-color" content="#ffffff">
  300. ';
  301. return ($this->config['favIcon'] == '') ? $favicon : $this->config['favIcon'];
  302. }
  303. public function pluginGlobalList()
  304. {
  305. $pluginSearch = '-enabled';
  306. $pluginInclude = '-include';
  307. $plugins = array_filter($this->config, function ($k) use ($pluginSearch) {
  308. return stripos($k, $pluginSearch) !== false;
  309. }, ARRAY_FILTER_USE_KEY);
  310. $plugins['includes'] = array_filter($this->config, function ($k) use ($pluginInclude) {
  311. return stripos($k, $pluginInclude) !== false;
  312. }, ARRAY_FILTER_USE_KEY);
  313. return $plugins;
  314. }
  315. public function googleTracking()
  316. {
  317. if ($this->config['gaTrackingID'] !== '') {
  318. return '
  319. <script async src="https://www.googletagmanager.com/gtag/js?id=' . $this->config['gaTrackingID'] . '"></script>
  320. <script>
  321. window.dataLayer = window.dataLayer || [];
  322. function gtag(){dataLayer.push(arguments);}
  323. gtag("js", new Date());
  324. gtag("config","' . $this->config['gaTrackingID'] . '");
  325. </script>
  326. ';
  327. }
  328. return null;
  329. }
  330. public function matchBrackets($text, $brackets = 's')
  331. {
  332. switch ($brackets) {
  333. case 's':
  334. case 'square':
  335. $pattern = '#\[(.*?)\]#';
  336. break;
  337. case 'c':
  338. case 'curly':
  339. $pattern = '#\((.*?)\)#';
  340. break;
  341. default:
  342. return null;
  343. }
  344. preg_match($pattern, $text, $match);
  345. return $match[1];
  346. }
  347. public function languagePacks($encode = false)
  348. {
  349. $files = array();
  350. foreach (glob(dirname(__DIR__, 2) . DIRECTORY_SEPARATOR . 'js' . DIRECTORY_SEPARATOR . 'langpack' . DIRECTORY_SEPARATOR . "*.json") as $filename) {
  351. if (strpos(basename($filename), '[') !== false) {
  352. $explode = explode('[', basename($filename));
  353. $files[] = array(
  354. 'filename' => basename($filename),
  355. 'code' => $explode[0],
  356. 'language' => $this->matchBrackets(basename($filename))
  357. );
  358. }
  359. }
  360. usort($files, function ($a, $b) {
  361. return $a['language'] <=> $b['language'];
  362. });
  363. return ($encode) ? json_encode($files) : $files;
  364. }
  365. public function pluginFiles($type)
  366. {
  367. $files = '';
  368. switch ($type) {
  369. case 'js':
  370. foreach (glob(dirname(__DIR__, 1) . DIRECTORY_SEPARATOR . 'plugins' . DIRECTORY_SEPARATOR . 'js' . DIRECTORY_SEPARATOR . "*.js") as $filename) {
  371. $files .= '<script src="api/plugins/js/' . basename($filename) . '?v=' . $this->fileHash . '" defer="true"></script>';
  372. }
  373. break;
  374. case 'css':
  375. foreach (glob(dirname(__DIR__, 1) . DIRECTORY_SEPARATOR . 'plugins' . DIRECTORY_SEPARATOR . 'css' . DIRECTORY_SEPARATOR . "*.css") as $filename) {
  376. $files .= '<link href="api/plugins/css/' . basename($filename) . '?v=' . $this->fileHash . '" rel="stylesheet">';
  377. }
  378. break;
  379. default:
  380. break;
  381. }
  382. return $files;
  383. }
  384. public function formKey($script = true)
  385. {
  386. if (isset($this->config['organizrHash'])) {
  387. if ($this->config['organizrHash'] !== '') {
  388. $hash = password_hash(substr($this->config['organizrHash'], 2, 10), PASSWORD_BCRYPT);
  389. return ($script) ? '<script>local("s","formKey","' . $hash . '");</script>' : $hash;
  390. }
  391. }
  392. }
  393. private function checkPHP()
  394. {
  395. if (!(version_compare(PHP_VERSION, $this->minimumPHP) >= 0)) {
  396. die('Organizr needs PHP Version: ' . $this->minimumPHP . '<br/> You have PHP Version: ' . PHP_VERSION);
  397. }
  398. }
  399. public function upgradeCheck()
  400. {
  401. if ($this->hasDB()) {
  402. $tempLock = $this->config['dbLocation'] . 'DBLOCK.txt';
  403. $updateComplete = $this->config['dbLocation'] . 'completed.txt';
  404. $cleanup = dirname(__DIR__, 2) . DIRECTORY_SEPARATOR . 'upgrade' . DIRECTORY_SEPARATOR;
  405. if (file_exists($updateComplete)) {
  406. @unlink($updateComplete);
  407. @$this->rrmdir($cleanup);
  408. }
  409. if (file_exists($tempLock)) {
  410. die('upgrading');
  411. }
  412. $updateDB = false;
  413. $updateSuccess = true;
  414. $compare = new Composer\Semver\Comparator;
  415. $oldVer = $this->config['configVersion'];
  416. // Upgrade check start for version below
  417. $versionCheck = '2.0.0-beta-200';
  418. if ($compare->lessThan($oldVer, $versionCheck)) {
  419. $updateDB = true;
  420. $oldVer = $versionCheck;
  421. }
  422. // End Upgrade check start for version above
  423. // Upgrade check start for version below
  424. $versionCheck = '2.0.0-beta-500';
  425. if ($compare->lessThan($oldVer, $versionCheck)) {
  426. $updateDB = true;
  427. $oldVer = $versionCheck;
  428. }
  429. // End Upgrade check start for version above
  430. // Upgrade check start for version below
  431. $versionCheck = '2.0.0-beta-800';
  432. if ($compare->lessThan($oldVer, $versionCheck)) {
  433. $updateDB = true;
  434. $oldVer = $versionCheck;
  435. }
  436. // End Upgrade check start for version above
  437. // Upgrade check start for version below
  438. $versionCheck = '2.1.0';
  439. if ($compare->lessThan($oldVer, $versionCheck)) {
  440. $updateDB = false;
  441. $oldVer = $versionCheck;
  442. $this->upgradeToVersion($versionCheck);
  443. }
  444. // End Upgrade check start for version above
  445. if ($updateDB == true) {
  446. //return 'Upgraded Needed - Current Version '.$oldVer.' - New Version: '.$versionCheck;
  447. // Upgrade database to latest version
  448. $updateSuccess = $this->updateDB($oldVer);
  449. }
  450. // Update config.php version if different to the installed version
  451. if ($updateSuccess && $this->version !== $this->config['configVersion']) {
  452. $this->updateConfig(array('apply_CONFIG_VERSION' => $this->version));
  453. }
  454. if ($updateSuccess == false) {
  455. die('Database update failed - Please manually check logs and fix - Then reload this page');
  456. }
  457. return true;
  458. }
  459. }
  460. public function updateDB($oldVerNum = false)
  461. {
  462. $tempLock = $this->config['dbLocation'] . 'DBLOCK.txt';
  463. if (!file_exists($tempLock)) {
  464. touch($tempLock);
  465. $migrationDB = 'tempMigration.db';
  466. $pathDigest = pathinfo($this->config['dbLocation'] . $this->config['dbName']);
  467. if (file_exists($this->config['dbLocation'] . $migrationDB)) {
  468. unlink($this->config['dbLocation'] . $migrationDB);
  469. }
  470. // Create Temp DB First
  471. $this->connectOtherDB();
  472. $backupDB = $pathDigest['dirname'] . '/' . $pathDigest['filename'] . '[' . date('Y-m-d_H-i-s') . ']' . ($oldVerNum ? '[' . $oldVerNum . ']' : '') . '.bak.db';
  473. copy($this->config['dbLocation'] . $this->config['dbName'], $backupDB);
  474. $success = $this->createDB($this->config['dbLocation'], true);
  475. if ($success) {
  476. $response = [
  477. array(
  478. 'function' => 'fetchAll',
  479. 'query' => array(
  480. 'SELECT name FROM sqlite_master WHERE type="table"'
  481. )
  482. ),
  483. ];
  484. $tables = $this->processQueries($response);
  485. foreach ($tables as $table) {
  486. $response = [
  487. array(
  488. 'function' => 'fetchAll',
  489. 'query' => array(
  490. 'SELECT * FROM ' . $table['name']
  491. )
  492. ),
  493. ];
  494. $data = $this->processQueries($response);
  495. $this->writeLog('success', 'Update Function - Grabbed Table data for Table: ' . $table['name'], 'Database');
  496. foreach ($data as $row) {
  497. $response = [
  498. array(
  499. 'function' => 'query',
  500. 'query' => array(
  501. 'INSERT into ' . $table['name'],
  502. $row
  503. )
  504. ),
  505. ];
  506. $this->processQueries($response, true);
  507. }
  508. $this->writeLog('success', 'Update Function - Wrote Table data for Table: ' . $table['name'], 'Database');
  509. }
  510. $this->writeLog('success', 'Update Function - All Table data converted - Starting Movement', 'Database');
  511. $this->db->disconnect();
  512. $this->otherDb->disconnect();
  513. // Remove Current Database
  514. if (file_exists($this->config['dbLocation'] . $migrationDB)) {
  515. $oldFileSize = filesize($this->config['dbLocation'] . $this->config['dbName']);
  516. $newFileSize = filesize($this->config['dbLocation'] . $migrationDB);
  517. if ($newFileSize > 0) {
  518. $this->writeLog('success', 'Update Function - Table Size of new DB ok..', 'Database');
  519. @unlink($this->config['dbLocation'] . $this->config['dbName']);
  520. copy($this->config['dbLocation'] . $migrationDB, $this->config['dbLocation'] . $this->config['dbName']);
  521. @unlink($this->config['dbLocation'] . $migrationDB);
  522. $this->writeLog('success', 'Update Function - Migrated Old Info to new Database', 'Database');
  523. @unlink($tempLock);
  524. return true;
  525. } else {
  526. $this->writeLog('error', 'Update Function - Filesize is zero', 'Database');
  527. }
  528. } else {
  529. $this->writeLog('error', 'Update Function - Migration DB does not exist', 'Database');
  530. }
  531. @unlink($tempLock);
  532. return false;
  533. } else {
  534. $this->writeLog('error', 'Update Function - Could not create migration DB', 'Database');
  535. }
  536. @unlink($tempLock);
  537. return false;
  538. }
  539. return false;
  540. }
  541. // Create config file in the return syntax
  542. public function createConfig($array, $path = null, $nest = 0)
  543. {
  544. $path = ($path) ? $path : $this->userConfigPath;
  545. // Define Initial Value
  546. $output = array();
  547. // Sort Items
  548. ksort($array);
  549. // Update the current config version
  550. if (!$nest) {
  551. // Inject Current Version
  552. $output[] = "\t'configVersion' => '" . (isset($array['apply_CONFIG_VERSION']) ? $array['apply_CONFIG_VERSION'] : $this->version) . "'";
  553. }
  554. unset($array['configVersion']);
  555. unset($array['apply_CONFIG_VERSION']);
  556. // Process Settings
  557. foreach ($array as $k => $v) {
  558. $allowCommit = true;
  559. $item = '';
  560. switch (gettype($v)) {
  561. case 'boolean':
  562. $item = ($v ? 'true' : 'false');
  563. break;
  564. case 'integer':
  565. case 'double':
  566. case 'NULL':
  567. $item = $v;
  568. break;
  569. case 'string':
  570. $item = "'" . str_replace(array('\\', "'"), array('\\\\', "\'"), $v) . "'";
  571. break;
  572. case 'array':
  573. $item = $this->createConfig($v, false, $nest + 1);
  574. break;
  575. default:
  576. $allowCommit = false;
  577. }
  578. if ($allowCommit) {
  579. $output[] = str_repeat("\t", $nest + 1) . "'$k' => $item";
  580. }
  581. }
  582. // Build output
  583. $output = (!$nest ? "<?php\nreturn " : '') . "array(\n" . implode(",\n", $output) . "\n" . str_repeat("\t", $nest) . ')' . (!$nest ? ';' : '');
  584. if (!$nest && $path) {
  585. $pathDigest = pathinfo($path);
  586. @mkdir($pathDigest['dirname'], 0770, true);
  587. if (file_exists($path)) {
  588. rename($path, $pathDigest['dirname'] . '/' . $pathDigest['filename'] . '.bak.php');
  589. }
  590. $file = fopen($path, 'w');
  591. fwrite($file, $output);
  592. fclose($file);
  593. if (file_exists($path)) {
  594. return true;
  595. }
  596. return false;
  597. } else {
  598. return $output;
  599. }
  600. }
  601. // Commit new values to the configuration
  602. public function updateConfig($new, $current = false)
  603. {
  604. // Get config if not supplied
  605. if ($current === false) {
  606. //$current = $this->loadConfig();
  607. $current = $this->config;
  608. } elseif (is_string($current) && is_file($current)) {
  609. $current = $this->loadConfig($current);
  610. }
  611. // Inject Parts
  612. foreach ($new as $k => $v) {
  613. $current[$k] = $v;
  614. }
  615. // Return Create
  616. return $this->createConfig($current);
  617. }
  618. public function loadConfig($path = null)
  619. {
  620. $path = ($path) ? $path : $this->userConfigPath;
  621. if (!is_file($path)) {
  622. return null;
  623. } else {
  624. return (array)call_user_func(function () use ($path) {
  625. return include($path);
  626. });
  627. }
  628. }
  629. public function fillDefaultConfig($array)
  630. {
  631. $path = $this->defaultConfigPath;
  632. if (is_string($path)) {
  633. $loadedDefaults = $this->loadConfig($path);
  634. } else {
  635. $loadedDefaults = $path;
  636. }
  637. // Include all plugin config files
  638. foreach (glob(dirname(__DIR__, 1) . DIRECTORY_SEPARATOR . 'plugins' . DIRECTORY_SEPARATOR . 'config' . DIRECTORY_SEPARATOR . "*.php") as $filename) {
  639. $loadedDefaults = array_merge($loadedDefaults, $this->loadConfig($filename));
  640. }
  641. return (is_array($loadedDefaults) ? $this->fillDefaultConfig_recurse($array, $loadedDefaults) : false);
  642. }
  643. public function fillDefaultConfig_recurse($current, $defaults)
  644. {
  645. foreach ($defaults as $k => $v) {
  646. if (!isset($current[$k])) {
  647. $current[$k] = $v;
  648. } elseif (is_array($current[$k]) && is_array($v)) {
  649. $current[$k] = $this->fillDefaultConfig_recurse($current[$k], $v);
  650. }
  651. }
  652. return $current;
  653. }
  654. public function config()
  655. {
  656. // Load config or default
  657. if (file_exists($this->userConfigPath)) {
  658. $config = $this->fillDefaultConfig($this->loadConfig($this->userConfigPath));
  659. } else {
  660. $config = $this->fillDefaultConfig($this->loadConfig($this->defaultConfigPath));
  661. }
  662. return $config;
  663. }
  664. public function combineConfig($array)
  665. {
  666. $this->config = array_merge($this->config, $array);
  667. return $this->config;
  668. }
  669. public function status()
  670. {
  671. $status = array();
  672. $dependenciesActive = array();
  673. $dependenciesInactive = array();
  674. $extensions = array("PDO_SQLITE", "PDO", "SQLITE3", "zip", "cURL", "openssl", "simplexml", "json", "session", "filter");
  675. $functions = array("hash", "fopen", "fsockopen", "fwrite", "fclose", "readfile");
  676. foreach ($extensions as $check) {
  677. if (extension_loaded($check)) {
  678. array_push($dependenciesActive, $check);
  679. } else {
  680. array_push($dependenciesInactive, $check);
  681. }
  682. }
  683. foreach ($functions as $check) {
  684. if (function_exists($check)) {
  685. array_push($dependenciesActive, $check);
  686. } else {
  687. array_push($dependenciesInactive, $check);
  688. }
  689. }
  690. if (!file_exists($this->userConfigPath)) {
  691. $status['status'] = "wizard";//wizard - ok for test
  692. }
  693. if (count($dependenciesInactive) > 0 || !is_writable(dirname(__DIR__, 2)) || !(version_compare(PHP_VERSION, $this->minimumPHP) >= 0)) {
  694. $status['status'] = "dependencies";
  695. }
  696. $status['status'] = ($status['status']) ?? "ok";
  697. $status['writable'] = is_writable(dirname(__DIR__, 2)) ? 'yes' : 'no';
  698. $status['minVersion'] = (version_compare(PHP_VERSION, $this->minimumPHP) >= 0) ? 'yes' : 'no';
  699. $status['dependenciesActive'] = $dependenciesActive;
  700. $status['dependenciesInactive'] = $dependenciesInactive;
  701. $status['version'] = $this->version;
  702. $status['os'] = $this->getOS();
  703. $status['php'] = phpversion();
  704. $status['userConfigPath'] = $this->userConfigPath;
  705. return $status;
  706. }
  707. public function hasDB()
  708. {
  709. return (file_exists($this->userConfigPath)) ?? false;
  710. }
  711. public function hasCookie()
  712. {
  713. return ($_COOKIE[$this->cookieName]) ?? false;
  714. }
  715. public function getGuest()
  716. {
  717. $guest = array(
  718. 'group' => 'Guest',
  719. 'group_id' => 999,
  720. 'image' => 'plugins/images/groups/guest.png'
  721. );
  722. $response = [
  723. array(
  724. 'function' => 'fetch',
  725. 'query' => 'SELECT * FROM groups WHERE `group_id` = 999'
  726. ),
  727. ];
  728. return $this->hasDB() ? $this->processQueries($response) : $guest;
  729. }
  730. public function getSchema()
  731. {
  732. $response = [
  733. array(
  734. 'function' => 'fetchAll',
  735. 'query' => 'SELECT name, sql FROM sqlite_master WHERE type=\'table\' ORDER BY name'
  736. ),
  737. ];
  738. return $this->hasDB() ? $this->processQueries($response) : 'Database not setup yet';
  739. }
  740. public function guestUser()
  741. {
  742. if ($this->hasDB()) {
  743. if ($this->getUserLevel() !== 999) {
  744. $guest = array(
  745. "token" => null,
  746. "tokenDate" => null,
  747. "tokenExpire" => null,
  748. "username" => "Organizr API",
  749. "uid" => $this->guestHash(0, 5),
  750. "group" => 'Admin',
  751. "groupID" => 0,
  752. "email" => null,
  753. //"groupImage"=>getGuest()['image'],
  754. "image" => $this->getGuest()['image'],
  755. "userID" => null,
  756. "loggedin" => false,
  757. "locked" => false,
  758. "tokenList" => null,
  759. "authService" => null
  760. );
  761. }
  762. }
  763. $guest = $guest ?? array(
  764. "token" => null,
  765. "tokenDate" => null,
  766. "tokenExpire" => null,
  767. "username" => "Guest",
  768. "uid" => $this->guestHash(0, 5),
  769. "group" => $this->getGuest()['group'],
  770. "groupID" => $this->getGuest()['group_id'],
  771. "email" => null,
  772. //"groupImage"=>getGuest()['image'],
  773. "image" => $this->getGuest()['image'],
  774. "userID" => null,
  775. "loggedin" => false,
  776. "locked" => false,
  777. "tokenList" => null,
  778. "authService" => null
  779. );
  780. return $guest;
  781. }
  782. public function getAllUserTokens($id)
  783. {
  784. $response = [
  785. array(
  786. 'function' => 'fetchAll',
  787. 'query' => array(
  788. 'SELECT * FROM `tokens` WHERE user_id = ? AND expires > ?',
  789. [$id],
  790. [$this->currentTime]
  791. )
  792. ),
  793. ];
  794. return $this->processQueries($response);
  795. }
  796. public function getUserById($id)
  797. {
  798. $response = [
  799. array(
  800. 'function' => 'fetch',
  801. 'query' => array(
  802. 'SELECT * FROM users WHERE id = ?',
  803. $id
  804. )
  805. )
  806. ];
  807. return $this->processQueries($response);
  808. }
  809. public function getUserByEmail($email)
  810. {
  811. $response = [
  812. array(
  813. 'function' => 'fetch',
  814. 'query' => array(
  815. 'SELECT * FROM users WHERE email = ? COLLATE NOCASE',
  816. $email
  817. )
  818. )
  819. ];
  820. return $this->processQueries($response);
  821. }
  822. protected function invalidToken()
  823. {
  824. $this->coookie('delete', $this->cookieName);
  825. $this->user = null;
  826. }
  827. public function validateToken($token)
  828. {
  829. // Validate script
  830. $userInfo = $this->jwtParse($token);
  831. $validated = $userInfo ? true : false;
  832. if ($validated == true) {
  833. $allTokens = $this->getAllUserTokens($userInfo['userID']);
  834. $user = $this->getUserById($userInfo['userID']);
  835. $tokenCheck = ($this->searchArray($allTokens, 'token', $token) !== false);
  836. if (!$tokenCheck) {
  837. $this->invalidToken();
  838. return false;
  839. } else {
  840. return array(
  841. "token" => $token,
  842. "tokenDate" => $userInfo['tokenDate'],
  843. "tokenExpire" => $userInfo['tokenExpire'],
  844. "username" => $user['username'],
  845. "uid" => $this->guestHash(0, 5),
  846. "group" => $user['group'],
  847. "groupID" => $user['group_id'],
  848. "email" => $user['email'],
  849. "image" => $user['image'],
  850. "userID" => $user['id'],
  851. "loggedin" => true,
  852. "locked" => $user['locked'],
  853. "tokenList" => $allTokens,
  854. "authService" => explode('::', $user['auth_service'])[0]
  855. );
  856. }
  857. } else {
  858. $this->invalidToken();
  859. }
  860. return false;
  861. }
  862. public function defaultUserGroup()
  863. {
  864. $response = [
  865. array(
  866. 'function' => 'fetch',
  867. 'query' => 'SELECT * FROM groups WHERE `default` = 1'
  868. )
  869. ];
  870. return $this->processQueries($response);
  871. }
  872. public function getAllTabs()
  873. {
  874. $response = [
  875. array(
  876. 'function' => 'fetchAll',
  877. 'query' => 'SELECT * FROM tabs ORDER BY `order` ASC',
  878. 'key' => 'tabs'
  879. ),
  880. array(
  881. 'function' => 'fetchAll',
  882. 'query' => 'SELECT * FROM categories ORDER BY `order` ASC',
  883. 'key' => 'categories'
  884. ),
  885. array(
  886. 'function' => 'fetchAll',
  887. 'query' => 'SELECT * FROM groups ORDER BY `group_id` ASC',
  888. 'key' => 'groups'
  889. ),
  890. ];
  891. return $this->processQueries($response);
  892. }
  893. public function getUsers()
  894. {
  895. $response = [
  896. array(
  897. 'function' => 'fetchAll',
  898. 'query' => 'SELECT * FROM users'
  899. ),
  900. array(
  901. 'function' => 'fetchAll',
  902. 'query' => 'SELECT * FROM groups ORDER BY group_id ASC'
  903. ),
  904. ];
  905. return $this->processQueries($response);
  906. }
  907. public function usernameTaken($username, $email, $id = null)
  908. {
  909. if ($id) {
  910. $response = [
  911. array(
  912. 'function' => 'fetch',
  913. 'query' => array(
  914. 'SELECT * FROM users WHERE `id` != ? AND (username = ? COLLATE NOCASE or email = ? COLLATE NOCASE)',
  915. $id,
  916. $username,
  917. $email
  918. )
  919. ),
  920. ];
  921. } else {
  922. $response = [
  923. array(
  924. 'function' => 'fetch',
  925. 'query' => array(
  926. 'SELECT * FROM users WHERE username = ? COLLATE NOCASE or email = ? COLLATE NOCASE',
  927. [$username],
  928. [$email]
  929. )
  930. ),
  931. ];
  932. }
  933. return $this->processQueries($response);
  934. }
  935. public function cleanPageName($page)
  936. {
  937. return ($page) ? strtolower(str_replace(array('%20', ' ', '-', '_'), '_', $page)) : '';
  938. }
  939. public function cleanClassName($name)
  940. {
  941. return ($name) ? (str_replace(array('%20', ' ', '-', '_'), '-', $name)) : '';
  942. }
  943. public function reverseCleanClassName($name)
  944. {
  945. return ($name) ? (str_replace(array('%20', '-', '_'), ' ', $name)) : '';
  946. }
  947. public function getPageList()
  948. {
  949. return $GLOBALS['organizrPages'];
  950. }
  951. public function getPage($page)
  952. {
  953. if (!$page) {
  954. $this->setAPIResponse('error', 'Page not setup', 409);
  955. return null;
  956. }
  957. $pageFunction = 'get_page_' . $this->cleanPageName($page);
  958. if (function_exists($pageFunction)) {
  959. return $pageFunction($this);
  960. } else {
  961. $this->setAPIResponse('error', 'Page not setup', 409);
  962. return null;
  963. }
  964. }
  965. public function getUserLevel()
  966. {
  967. // Grab token
  968. $requesterToken = isset($this->getallheaders()['Token']) ? $this->getallheaders()['Token'] : (isset($_GET['apikey']) ? $_GET['apikey'] : false);
  969. $apiKey = ($this->config['organizrAPI']) ?? null;
  970. // Check token or API key
  971. // If API key, return 0 for admin
  972. if (strlen($requesterToken) == 20 && $requesterToken == $apiKey) {
  973. //DO API CHECK
  974. return 0;
  975. } elseif (isset($this->user)) {
  976. return $this->user['groupID'];
  977. }
  978. // All else fails? return guest id
  979. return 999;
  980. }
  981. public function qualifyRequest($accessLevelNeeded, $api = false)
  982. {
  983. if ($this->getUserLevel() <= $accessLevelNeeded && $this->getUserLevel() !== null) {
  984. return true;
  985. } else {
  986. if ($api) {
  987. $this->setAPIResponse('error', 'Not Authorized', 401);
  988. }
  989. return false;
  990. }
  991. }
  992. public function getImages()
  993. {
  994. $allIconsPrep = array();
  995. $allIcons = array();
  996. $ignore = array(".", "..", "._.DS_Store", ".DS_Store", ".pydio_id", "index.html");
  997. $dirname = dirname(__DIR__, 2) . DIRECTORY_SEPARATOR . 'plugins' . DIRECTORY_SEPARATOR . 'images' . DIRECTORY_SEPARATOR . 'tabs' . DIRECTORY_SEPARATOR;
  998. $path = 'plugins/images/tabs/';
  999. $images = scandir($dirname);
  1000. foreach ($images as $image) {
  1001. if (!in_array($image, $ignore)) {
  1002. $allIconsPrep[$image] = array(
  1003. 'path' => $path,
  1004. 'name' => $image
  1005. );
  1006. }
  1007. }
  1008. $dirname = dirname(__DIR__, 2) . DIRECTORY_SEPARATOR . 'plugins' . DIRECTORY_SEPARATOR . 'images' . DIRECTORY_SEPARATOR . 'userTabs' . DIRECTORY_SEPARATOR;
  1009. $path = 'plugins/images/userTabs/';
  1010. $images = scandir($dirname);
  1011. foreach ($images as $image) {
  1012. if (!in_array($image, $ignore)) {
  1013. $allIconsPrep[$image] = array(
  1014. 'path' => $path,
  1015. 'name' => $image
  1016. );
  1017. }
  1018. }
  1019. ksort($allIconsPrep);
  1020. foreach ($allIconsPrep as $item) {
  1021. $allIcons[] = $item['path'] . $item['name'];
  1022. }
  1023. return $allIcons;
  1024. }
  1025. public function removeImage($image = null)
  1026. {
  1027. if (!$image) {
  1028. $this->setAPIResponse('error', 'No image supplied', 422);
  1029. return false;
  1030. }
  1031. $approvedPath = 'plugins/images/userTabs/';
  1032. $removeImage = $approvedPath . pathinfo($image, PATHINFO_BASENAME);
  1033. if ($this->approvedFileExtension($removeImage)) {
  1034. if (file_exists(dirname(__DIR__, 2) . DIRECTORY_SEPARATOR . $removeImage)) {
  1035. $this->writeLog('success', 'Image Manager Function - Deleted Image [' . pathinfo($image, PATHINFO_BASENAME) . ']', $this->user['username']);
  1036. $this->setAPIResponse(null, pathinfo($image, PATHINFO_BASENAME) . ' has been deleted', null);
  1037. return (unlink(dirname(__DIR__, 2) . DIRECTORY_SEPARATOR . $removeImage));
  1038. } else {
  1039. $this->setAPIResponse('error', $removeImage . ' does not exist', 404);
  1040. return false;
  1041. }
  1042. } else {
  1043. $this->setAPIResponse('error', $removeImage . ' is not approved to be deleted', 409);
  1044. return false;
  1045. }
  1046. }
  1047. public function uploadImage()
  1048. {
  1049. $filesCheck = array_filter($_FILES);
  1050. if (!empty($filesCheck) && $this->approvedFileExtension($_FILES['file']['name']) && strpos($_FILES['file']['type'], 'image/') !== false) {
  1051. ini_set('upload_max_filesize', '10M');
  1052. ini_set('post_max_size', '10M');
  1053. $tempFile = $_FILES['file']['tmp_name'];
  1054. $targetPath = dirname(__DIR__, 2) . DIRECTORY_SEPARATOR . 'plugins' . DIRECTORY_SEPARATOR . 'images' . DIRECTORY_SEPARATOR . 'userTabs' . DIRECTORY_SEPARATOR;
  1055. $targetFile = $targetPath . $_FILES['file']['name'];
  1056. $this->setAPIResponse(null, pathinfo($_FILES['file']['name'], PATHINFO_BASENAME) . ' has been uploaded', null);
  1057. return move_uploaded_file($tempFile, $targetFile);
  1058. }
  1059. }
  1060. public function ping($pings)
  1061. {
  1062. if ($this->qualifyRequest($this->config['pingAuth'], true)) {
  1063. if (!$pings['list']) {
  1064. $this->setAPIResponse('error', 'No ping hostname/IP\'s entered', 409);
  1065. return null;
  1066. }
  1067. $pings = $pings['list'];
  1068. $type = gettype($pings);
  1069. $ping = new Ping("");
  1070. $ping->setTtl(128);
  1071. $ping->setTimeout(2);
  1072. switch ($type) {
  1073. case "array":
  1074. $results = [];
  1075. foreach ($pings as $k => $v) {
  1076. if (strpos($v, ':') !== false) {
  1077. $domain = explode(':', $v)[0];
  1078. $port = explode(':', $v)[1];
  1079. $ping->setHost($domain);
  1080. $ping->setPort($port);
  1081. $latency = $ping->ping('fsockopen');
  1082. } else {
  1083. $ping->setHost($v);
  1084. $latency = $ping->ping();
  1085. }
  1086. if ($latency || $latency === 0) {
  1087. $results[$v] = $latency;
  1088. } else {
  1089. $results[$v] = false;
  1090. }
  1091. }
  1092. break;
  1093. case "string":
  1094. if (strpos($pings, ':') !== false) {
  1095. $domain = explode(':', $pings)[0];
  1096. $port = explode(':', $pings)[1];
  1097. $ping->setHost($domain);
  1098. $ping->setPort($port);
  1099. $latency = $ping->ping('fsockopen');
  1100. } else {
  1101. $ping->setHost($pings);
  1102. $latency = $ping->ping();
  1103. }
  1104. if ($latency || $latency === 0) {
  1105. $results = $latency;
  1106. } else {
  1107. $results = null;
  1108. }
  1109. break;
  1110. }
  1111. return ($results) ?? null;
  1112. }
  1113. return null;
  1114. }
  1115. public function getCustomizeAppearance()
  1116. {
  1117. return array(
  1118. 'Top Bar' => array(
  1119. array(
  1120. 'type' => 'input',
  1121. 'name' => 'logo',
  1122. 'label' => 'Logo',
  1123. 'value' => $this->config['logo']
  1124. ),
  1125. array(
  1126. 'type' => 'input',
  1127. 'name' => 'title',
  1128. 'label' => 'Title',
  1129. 'value' => $this->config['title']
  1130. ),
  1131. array(
  1132. 'type' => 'switch',
  1133. 'name' => 'useLogo',
  1134. 'label' => 'Use Logo instead of Title',
  1135. 'value' => $this->config['useLogo'],
  1136. 'help' => 'Also sets the title of your site'
  1137. ),
  1138. array(
  1139. 'type' => 'input',
  1140. 'name' => 'description',
  1141. 'label' => 'Meta Description',
  1142. 'value' => $this->config['description'],
  1143. 'help' => 'Used to set the description for SEO meta tags'
  1144. ),
  1145. ),
  1146. 'Login Page' => array(
  1147. array(
  1148. 'type' => 'input',
  1149. 'name' => 'loginLogo',
  1150. 'label' => 'Login Logo',
  1151. 'value' => $this->config['loginLogo'],
  1152. ),
  1153. array(
  1154. 'type' => 'input',
  1155. 'name' => 'loginWallpaper',
  1156. 'label' => 'Login Wallpaper',
  1157. 'value' => $this->config['loginWallpaper'],
  1158. 'help' => 'You may enter multiple URL\'s using the CSV format. i.e. link#1,link#2,link#3'
  1159. ),
  1160. array(
  1161. 'type' => 'switch',
  1162. 'name' => 'useLogoLogin',
  1163. 'label' => 'Use Logo instead of Title on Login Page',
  1164. 'value' => $this->config['useLogoLogin']
  1165. ),
  1166. array(
  1167. 'type' => 'switch',
  1168. 'name' => 'minimalLoginScreen',
  1169. 'label' => 'Minimal Login Screen',
  1170. 'value' => $this->config['minimalLoginScreen']
  1171. )
  1172. ),
  1173. 'Options' => array(
  1174. array(
  1175. 'type' => 'switch',
  1176. 'name' => 'alternateHomepageHeaders',
  1177. 'label' => 'Alternate Homepage Titles',
  1178. 'value' => $this->config['alternateHomepageHeaders']
  1179. ),
  1180. array(
  1181. 'type' => 'switch',
  1182. 'name' => 'debugErrors',
  1183. 'label' => 'Show Debug Errors',
  1184. 'value' => $this->config['debugErrors']
  1185. ),
  1186. array(
  1187. 'type' => 'switch',
  1188. 'name' => 'githubMenuLink',
  1189. 'label' => 'Show GitHub Repo Link',
  1190. 'value' => $this->config['githubMenuLink']
  1191. ),
  1192. array(
  1193. 'type' => 'switch',
  1194. 'name' => 'organizrSupportMenuLink',
  1195. 'label' => 'Show Organizr Support Link',
  1196. 'value' => $this->config['organizrSupportMenuLink']
  1197. ),
  1198. array(
  1199. 'type' => 'switch',
  1200. 'name' => 'organizrDocsMenuLink',
  1201. 'label' => 'Show Organizr Docs Link',
  1202. 'value' => $this->config['organizrDocsMenuLink']
  1203. ),
  1204. array(
  1205. 'type' => 'switch',
  1206. 'name' => 'organizrSignoutMenuLink',
  1207. 'label' => 'Show Organizr Sign out & in Button on Sidebar',
  1208. 'value' => $this->config['organizrSignoutMenuLink']
  1209. ),
  1210. array(
  1211. 'type' => 'select',
  1212. 'name' => 'unsortedTabs',
  1213. 'label' => 'Unsorted Tab Placement',
  1214. 'value' => $this->config['unsortedTabs'],
  1215. 'options' => array(
  1216. array(
  1217. 'name' => 'Top',
  1218. 'value' => 'top'
  1219. ),
  1220. array(
  1221. 'name' => 'Bottom',
  1222. 'value' => 'bottom'
  1223. )
  1224. )
  1225. ),
  1226. array(
  1227. 'type' => 'input',
  1228. 'name' => 'gaTrackingID',
  1229. 'label' => 'Google Analytics Tracking ID',
  1230. 'placeholder' => 'e.g. UA-XXXXXXXXX-X',
  1231. 'value' => $this->config['gaTrackingID']
  1232. )
  1233. ),
  1234. 'Colors & Themes' => array(
  1235. array(
  1236. 'type' => 'html',
  1237. 'override' => 12,
  1238. 'label' => 'Custom CSS [Can replace colors from above]',
  1239. 'html' => '
  1240. <div class="row">
  1241. <div class="col-lg-12">
  1242. <div class="panel panel-info">
  1243. <div class="panel-heading">
  1244. <span lang="en">Notice</span>
  1245. </div>
  1246. <div class="panel-wrapper collapse in" aria-expanded="true">
  1247. <div class="panel-body">
  1248. <span lang="en">The value of #987654 is just a placeholder, you can change to any value you like.</span>
  1249. <span lang="en">To revert back to default, save with no value defined in the relevant field.</span>
  1250. </div>
  1251. </div>
  1252. </div>
  1253. </div>
  1254. </div>
  1255. ',
  1256. ),
  1257. array(
  1258. 'type' => 'blank',
  1259. 'label' => ''
  1260. ),
  1261. array(
  1262. 'type' => 'input',
  1263. 'name' => 'headerColor',
  1264. 'label' => 'Nav Bar Color',
  1265. 'value' => $this->config['headerColor'],
  1266. 'class' => 'pick-a-color',
  1267. 'attr' => 'data-original="' . $this->config['headerColor'] . '"'
  1268. ),
  1269. array(
  1270. 'type' => 'input',
  1271. 'name' => 'headerTextColor',
  1272. 'label' => 'Nav Bar Text Color',
  1273. 'value' => $this->config['headerTextColor'],
  1274. 'class' => 'pick-a-color',
  1275. 'attr' => 'data-original="' . $this->config['headerTextColor'] . '"'
  1276. ),
  1277. array(
  1278. 'type' => 'input',
  1279. 'name' => 'sidebarColor',
  1280. 'label' => 'Side Bar Color',
  1281. 'value' => $this->config['sidebarColor'],
  1282. 'class' => 'pick-a-color',
  1283. 'attr' => 'data-original="' . $this->config['sidebarColor'] . '"'
  1284. ),
  1285. array(
  1286. 'type' => 'input',
  1287. 'name' => 'sidebarTextColor',
  1288. 'label' => 'Side Bar Text Color',
  1289. 'value' => $this->config['sidebarTextColor'],
  1290. 'class' => 'pick-a-color',
  1291. 'attr' => 'data-original="' . $this->config['sidebarTextColor'] . '"'
  1292. ),
  1293. array(
  1294. 'type' => 'input',
  1295. 'name' => 'accentColor',
  1296. 'label' => 'Accent Color',
  1297. 'value' => $this->config['accentColor'],
  1298. 'class' => 'pick-a-color',
  1299. 'attr' => 'data-original="' . $this->config['accentColor'] . '"'
  1300. ),
  1301. array(
  1302. 'type' => 'input',
  1303. 'name' => 'accentTextColor',
  1304. 'label' => 'Accent Text Color',
  1305. 'value' => $this->config['accentTextColor'],
  1306. 'class' => 'pick-a-color',
  1307. 'attr' => 'data-original="' . $this->config['accentTextColor'] . '"'
  1308. ),
  1309. array(
  1310. 'type' => 'input',
  1311. 'name' => 'buttonColor',
  1312. 'label' => 'Button Color',
  1313. 'value' => $this->config['buttonColor'],
  1314. 'class' => 'pick-a-color',
  1315. 'attr' => 'data-original="' . $this->config['buttonColor'] . '"'
  1316. ),
  1317. array(
  1318. 'type' => 'input',
  1319. 'name' => 'buttonTextColor',
  1320. 'label' => 'Button Text Color',
  1321. 'value' => $this->config['buttonTextColor'],
  1322. 'class' => 'pick-a-color',
  1323. 'attr' => 'data-original="' . $this->config['buttonTextColor'] . '"'
  1324. ),
  1325. array(
  1326. 'type' => 'select',
  1327. 'name' => 'theme',
  1328. 'label' => 'Theme',
  1329. 'class' => 'themeChanger',
  1330. 'value' => $this->config['theme'],
  1331. 'options' => $this->getThemes()
  1332. ),
  1333. array(
  1334. 'type' => 'select',
  1335. 'name' => 'style',
  1336. 'label' => 'Style',
  1337. 'class' => 'styleChanger',
  1338. 'value' => $this->config['style'],
  1339. 'options' => array(
  1340. array(
  1341. 'name' => 'Light',
  1342. 'value' => 'light'
  1343. ),
  1344. array(
  1345. 'name' => 'Dark',
  1346. 'value' => 'dark'
  1347. ),
  1348. array(
  1349. 'name' => 'Horizontal',
  1350. 'value' => 'horizontal'
  1351. )
  1352. )
  1353. )
  1354. ),
  1355. 'Notifications' => array(
  1356. array(
  1357. 'type' => 'select',
  1358. 'name' => 'notificationBackbone',
  1359. 'class' => 'notifyChanger',
  1360. 'label' => 'Type',
  1361. 'value' => $this->config['notificationBackbone'],
  1362. 'options' => $this->optionNotificationTypes()
  1363. ),
  1364. array(
  1365. 'type' => 'select',
  1366. 'name' => 'notificationPosition',
  1367. 'class' => 'notifyPositionChanger',
  1368. 'label' => 'Position',
  1369. 'value' => $this->config['notificationPosition'],
  1370. 'options' => $this->optionNotificationPositions()
  1371. ),
  1372. array(
  1373. 'type' => 'html',
  1374. 'label' => 'Test Message',
  1375. 'html' => '
  1376. <div class="btn-group m-r-10 dropup">
  1377. <button aria-expanded="false" data-toggle="dropdown" class="btn btn-info btn-outline dropdown-toggle waves-effect waves-light" type="button">
  1378. <i class="fa fa-comment m-r-5"></i>
  1379. <span>Test </span>
  1380. </button>
  1381. <ul role="menu" class="dropdown-menu">
  1382. <li><a onclick="message(\'Test Message\',\'This is a success Message\',activeInfo.settings.notifications.position,\'#FFF\',\'success\',\'5000\');">Success</a></li>
  1383. <li><a onclick="message(\'Test Message\',\'This is a info Message\',activeInfo.settings.notifications.position,\'#FFF\',\'info\',\'5000\');">Info</a></li>
  1384. <li><a onclick="message(\'Test Message\',\'This is a warning Message\',activeInfo.settings.notifications.position,\'#FFF\',\'warning\',\'5000\');">Warning</a></li>
  1385. <li><a onclick="message(\'Test Message\',\'This is a error Message\',activeInfo.settings.notifications.position,\'#FFF\',\'error\',\'5000\');">Error</a></li>
  1386. </ul>
  1387. </div>
  1388. '
  1389. )
  1390. ),
  1391. 'FavIcon' => array(
  1392. array(
  1393. 'type' => 'textbox',
  1394. 'name' => 'favIcon',
  1395. 'class' => '',
  1396. 'label' => 'Fav Icon Code',
  1397. 'value' => $this->config['favIcon'],
  1398. 'placeholder' => 'Paste Contents from https://realfavicongenerator.net/',
  1399. 'attr' => 'rows="10"',
  1400. ),
  1401. array(
  1402. 'type' => 'html',
  1403. 'label' => 'Instructions',
  1404. 'html' => '
  1405. <div class="panel panel-default">
  1406. <div class="panel-heading">
  1407. <a href="https://realfavicongenerator.net/" target="_blank"><span class="label label-info m-l-5">Visit FavIcon Site</span></a>
  1408. </div>
  1409. <div class="panel-wrapper collapse in">
  1410. <div class="panel-body">
  1411. <ul class="list-icons">
  1412. <li lang="en"><i class="fa fa-caret-right text-info"></i> Click [Select your Favicon picture]</li>
  1413. <li lang="en"><i class="fa fa-caret-right text-info"></i> Choose your image to use</li>
  1414. <li lang="en"><i class="fa fa-caret-right text-info"></i> Edit settings to your liking</li>
  1415. <li lang="en"><i class="fa fa-caret-right text-info"></i> At bottom of page on [Favicon Generator Options] under [Path] choose [I cannot or I do not want to place favicon files at the root of my web site.]</li>
  1416. <li lang="en"><i class="fa fa-caret-right text-info"></i> Enter this path <code>plugins/images/faviconCustom</code></li>
  1417. <li lang="en"><i class="fa fa-caret-right text-info"></i> Click [Generate your Favicons and HTML code]</li>
  1418. <li lang="en"><i class="fa fa-caret-right text-info"></i> Download and unzip file and place in <code>plugins/images/faviconCustom</code></li>
  1419. <li lang="en"><i class="fa fa-caret-right text-info"></i> Copy code and paste inside left box</li>
  1420. </ul>
  1421. </div>
  1422. </div>
  1423. </div>
  1424. '
  1425. ),
  1426. ),
  1427. 'Custom CSS' => array(
  1428. array(
  1429. 'type' => 'html',
  1430. 'override' => 12,
  1431. 'label' => 'Custom CSS [Can replace colors from above]',
  1432. 'html' => '<button type="button" class="hidden saveCss btn btn-info btn-circle pull-right m-r-5 m-l-10"><i class="fa fa-save"></i> </button><div id="customCSSEditor" style="height:300px">' . htmlentities($this->config['customCss']) . '</div>'
  1433. ),
  1434. array(
  1435. 'type' => 'textbox',
  1436. 'name' => 'customCss',
  1437. 'class' => 'hidden cssTextarea',
  1438. 'label' => '',
  1439. 'value' => $this->config['customCss'],
  1440. 'placeholder' => 'No &lt;style&gt; tags needed',
  1441. 'attr' => 'rows="10"',
  1442. ),
  1443. ),
  1444. 'Theme CSS' => array(
  1445. array(
  1446. 'type' => 'html',
  1447. 'override' => 12,
  1448. 'label' => 'Theme CSS [Can replace colors from above]',
  1449. 'html' => '<button type="button" class="hidden saveCssTheme btn btn-info btn-circle pull-right m-r-5 m-l-10"><i class="fa fa-save"></i> </button><div id="customThemeCSSEditor" style="height:300px">' . htmlentities($this->config['customThemeCss']) . '</div>'
  1450. ),
  1451. array(
  1452. 'type' => 'textbox',
  1453. 'name' => 'customThemeCss',
  1454. 'class' => 'hidden cssThemeTextarea',
  1455. 'label' => '',
  1456. 'value' => $this->config['customThemeCss'],
  1457. 'placeholder' => 'No &lt;style&gt; tags needed',
  1458. 'attr' => 'rows="10"',
  1459. ),
  1460. ),
  1461. 'Custom Javascript' => array(
  1462. array(
  1463. 'type' => 'html',
  1464. 'override' => 12,
  1465. 'label' => 'Custom Javascript',
  1466. 'html' => '<button type="button" class="hidden saveJava btn btn-info btn-circle pull-right m-r-5 m-l-10"><i class="fa fa-save"></i> </button><div id="customJavaEditor" style="height:300px">' . htmlentities($this->config['customJava']) . '</div>'
  1467. ),
  1468. array(
  1469. 'type' => 'textbox',
  1470. 'name' => 'customJava',
  1471. 'class' => 'hidden javaTextarea',
  1472. 'label' => '',
  1473. 'value' => $this->config['customJava'],
  1474. 'placeholder' => 'No &lt;script&gt; tags needed',
  1475. 'attr' => 'rows="10"',
  1476. ),
  1477. ),
  1478. 'Theme Javascript' => array(
  1479. array(
  1480. 'type' => 'html',
  1481. 'override' => 12,
  1482. 'label' => 'Theme Javascript',
  1483. 'html' => '<button type="button" class="hidden saveJavaTheme btn btn-info btn-circle pull-right m-r-5 m-l-10"><i class="fa fa-save"></i> </button><div id="customThemeJavaEditor" style="height:300px">' . htmlentities($this->config['customThemeJava']) . '</div>'
  1484. ),
  1485. array(
  1486. 'type' => 'textbox',
  1487. 'name' => 'customThemeJava',
  1488. 'class' => 'hidden javaThemeTextarea',
  1489. 'label' => '',
  1490. 'value' => $this->config['customThemeJava'],
  1491. 'placeholder' => 'No &lt;script&gt; tags needed',
  1492. 'attr' => 'rows="10"',
  1493. ),
  1494. ),
  1495. );
  1496. }
  1497. public function loadAppearance()
  1498. {
  1499. $appearance['logo'] = $this->config['logo'];
  1500. $appearance['title'] = $this->config['title'];
  1501. $appearance['useLogo'] = $this->config['useLogo'];
  1502. $appearance['useLogoLogin'] = $this->config['useLogoLogin'];
  1503. $appearance['headerColor'] = $this->config['headerColor'];
  1504. $appearance['headerTextColor'] = $this->config['headerTextColor'];
  1505. $appearance['sidebarColor'] = $this->config['sidebarColor'];
  1506. $appearance['headerTextColor'] = $this->config['headerTextColor'];
  1507. $appearance['sidebarTextColor'] = $this->config['sidebarTextColor'];
  1508. $appearance['accentColor'] = $this->config['accentColor'];
  1509. $appearance['accentTextColor'] = $this->config['accentTextColor'];
  1510. $appearance['buttonColor'] = $this->config['buttonColor'];
  1511. $appearance['buttonTextColor'] = $this->config['buttonTextColor'];
  1512. $appearance['buttonTextHoverColor'] = $this->config['buttonTextHoverColor'];
  1513. $appearance['buttonHoverColor'] = $this->config['buttonHoverColor'];
  1514. $appearance['loginWallpaper'] = $this->config['loginWallpaper'];
  1515. $appearance['loginLogo'] = $this->config['loginLogo'];
  1516. $appearance['customCss'] = $this->config['customCss'];
  1517. $appearance['customThemeCss'] = $this->config['customThemeCss'];
  1518. $appearance['customJava'] = $this->config['customJava'];
  1519. $appearance['customThemeJava'] = $this->config['customThemeJava'];
  1520. return $appearance;
  1521. }
  1522. public function getSettingsMain()
  1523. {
  1524. return array(
  1525. 'Github' => array(
  1526. array(
  1527. 'type' => 'select',
  1528. 'name' => 'branch',
  1529. 'label' => 'Branch',
  1530. 'value' => $this->config['branch'],
  1531. 'options' => $this->getBranches(),
  1532. 'disabled' => $this->docker,
  1533. 'help' => ($this->docker) ? 'Since you are using the Official Docker image, Change the image to change the branch' : 'Choose which branch to download from'
  1534. ),
  1535. array(
  1536. 'type' => 'button',
  1537. 'name' => 'force-install-branch',
  1538. 'label' => 'Force Install Branch',
  1539. 'class' => 'updateNow',
  1540. 'icon' => 'fa fa-download',
  1541. 'text' => 'Retrieve',
  1542. 'attr' => ($this->docker) ? 'title="You can just restart your docker to update"' : '',
  1543. 'help' => ($this->docker) ? 'Since you are using the official Docker image, you can just restart your Docker container to update Organizr' : 'This will re-download all of the source files for Organizr'
  1544. )
  1545. ),
  1546. 'API' => array(
  1547. array(
  1548. 'type' => 'password-alt',
  1549. 'name' => 'organizrAPI',
  1550. 'label' => 'Organizr API',
  1551. 'value' => $this->config['organizrAPI']
  1552. ),
  1553. array(
  1554. 'type' => 'button',
  1555. 'label' => 'Generate New API Key',
  1556. 'class' => 'newAPIKey',
  1557. 'icon' => 'fa fa-refresh',
  1558. 'text' => 'Generate'
  1559. )
  1560. ),
  1561. 'Authentication' => array(
  1562. array(
  1563. 'type' => 'select',
  1564. 'name' => 'authType',
  1565. 'id' => 'authSelect',
  1566. 'label' => 'Authentication Type',
  1567. 'value' => $this->config['authType'],
  1568. 'options' => $this->getAuthTypes()
  1569. ),
  1570. array(
  1571. 'type' => 'select',
  1572. 'name' => 'authBackend',
  1573. 'id' => 'authBackendSelect',
  1574. 'label' => 'Authentication Backend',
  1575. 'class' => 'backendAuth switchAuth',
  1576. 'value' => $this->config['authBackend'],
  1577. 'options' => $this->getAuthBackends()
  1578. ),
  1579. array(
  1580. 'type' => 'password-alt',
  1581. 'name' => 'plexToken',
  1582. 'class' => 'plexAuth switchAuth',
  1583. 'label' => 'Plex Token',
  1584. 'value' => $this->config['plexToken'],
  1585. 'placeholder' => 'Use Get Token Button'
  1586. ),
  1587. array(
  1588. 'type' => 'button',
  1589. 'label' => 'Get Plex Token',
  1590. 'class' => 'popup-with-form getPlexTokenAuth plexAuth switchAuth',
  1591. 'icon' => 'fa fa-ticket',
  1592. 'text' => 'Retrieve',
  1593. 'href' => '#auth-plex-token-form',
  1594. 'attr' => 'data-effect="mfp-3d-unfold"'
  1595. ),
  1596. array(
  1597. 'type' => 'password-alt',
  1598. 'name' => 'plexID',
  1599. 'class' => 'plexAuth switchAuth',
  1600. 'label' => 'Plex Machine',
  1601. 'value' => $this->config['plexID'],
  1602. 'placeholder' => 'Use Get Plex Machine Button'
  1603. ),
  1604. array(
  1605. 'type' => 'button',
  1606. 'label' => 'Get Plex Machine',
  1607. 'class' => 'popup-with-form getPlexMachineAuth plexAuth switchAuth',
  1608. 'icon' => 'fa fa-id-badge',
  1609. 'text' => 'Retrieve',
  1610. 'href' => '#auth-plex-machine-form',
  1611. 'attr' => 'data-effect="mfp-3d-unfold"'
  1612. ),
  1613. array(
  1614. 'type' => 'input',
  1615. 'name' => 'plexAdmin',
  1616. 'label' => 'Plex Admin Username',
  1617. 'class' => 'plexAuth switchAuth',
  1618. 'value' => $this->config['plexAdmin'],
  1619. 'placeholder' => 'Admin username for Plex'
  1620. ),
  1621. array(
  1622. 'type' => 'switch',
  1623. 'name' => 'plexoAuth',
  1624. 'label' => 'Enable Plex oAuth',
  1625. 'class' => 'plexAuth switchAuth',
  1626. 'value' => $this->config['plexoAuth']
  1627. ),
  1628. array(
  1629. 'type' => 'switch',
  1630. 'name' => 'plexStrictFriends',
  1631. 'label' => 'Strict Plex Friends ',
  1632. 'class' => 'plexAuth switchAuth',
  1633. 'value' => $this->config['plexStrictFriends'],
  1634. 'help' => 'Enabling this will only allow Friends that have shares to the Machine ID entered above to login, Having this disabled will allow all Friends on your Friends list to login'
  1635. ),
  1636. array(
  1637. 'type' => 'switch',
  1638. 'name' => 'ignoreTFALocal',
  1639. 'label' => 'Ignore External 2FA on Local Subnet',
  1640. 'value' => $this->config['ignoreTFALocal'],
  1641. 'help' => 'Enabling this will bypass external 2FA security if user is on local Subnet'
  1642. ),
  1643. array(
  1644. 'type' => 'input',
  1645. 'name' => 'authBackendHost',
  1646. 'class' => 'ldapAuth ftpAuth switchAuth',
  1647. 'label' => 'Host Address',
  1648. 'value' => $this->config['authBackendHost'],
  1649. 'placeholder' => 'http{s) | ftp(s) | ldap(s)://hostname:port'
  1650. ),
  1651. array(
  1652. 'type' => 'input',
  1653. 'name' => 'authBaseDN',
  1654. 'class' => 'ldapAuth switchAuth',
  1655. 'label' => 'Host Base DN',
  1656. 'value' => $this->config['authBaseDN'],
  1657. 'placeholder' => 'cn=%s,dc=sub,dc=domain,dc=com'
  1658. ),
  1659. array(
  1660. 'type' => 'select',
  1661. 'name' => 'ldapType',
  1662. 'id' => 'ldapType',
  1663. 'label' => 'LDAP Backend Type',
  1664. 'class' => 'ldapAuth switchAuth',
  1665. 'value' => $this->config['ldapType'],
  1666. 'options' => $this->getLDAPOptions()
  1667. ),
  1668. array(
  1669. 'type' => 'input',
  1670. 'name' => 'authBackendHostPrefix',
  1671. 'class' => 'ldapAuth switchAuth',
  1672. 'label' => 'Account Prefix',
  1673. 'id' => 'authBackendHostPrefix-input',
  1674. 'value' => $this->config['authBackendHostPrefix'],
  1675. 'placeholder' => 'Account prefix - i.e. Controller\ from Controller\Username for AD - uid= for OpenLDAP'
  1676. ),
  1677. array(
  1678. 'type' => 'input',
  1679. 'name' => 'authBackendHostSuffix',
  1680. 'class' => 'ldapAuth switchAuth',
  1681. 'label' => 'Account Suffix',
  1682. 'id' => 'authBackendHostSuffix-input',
  1683. 'value' => $this->config['authBackendHostSuffix'],
  1684. 'placeholder' => 'Account suffix - start with comma - ,ou=people,dc=domain,dc=tld'
  1685. ),
  1686. array(
  1687. 'type' => 'input',
  1688. 'name' => 'ldapBindUsername',
  1689. 'class' => 'ldapAuth switchAuth',
  1690. 'label' => 'Bind Username',
  1691. 'value' => $this->config['ldapBindUsername'],
  1692. 'placeholder' => ''
  1693. ),
  1694. array(
  1695. 'type' => 'password',
  1696. 'name' => 'ldapBindPassword',
  1697. 'class' => 'ldapAuth switchAuth',
  1698. 'label' => 'Password',
  1699. 'value' => $this->config['ldapBindPassword']
  1700. ),
  1701. array(
  1702. 'type' => 'html',
  1703. 'class' => 'ldapAuth switchAuth',
  1704. 'label' => 'Account DN',
  1705. 'html' => '<span id="accountDN" class="ldapAuth switchAuth">' . $this->config['authBackendHostPrefix'] . 'TestAcct' . $this->config['authBackendHostSuffix'] . '</span>'
  1706. ),
  1707. array(
  1708. 'type' => 'switch',
  1709. 'name' => 'ldapSSL',
  1710. 'class' => 'ldapAuth switchAuth',
  1711. 'label' => 'Enable LDAP SSL',
  1712. 'value' => $this->config['ldapSSL'],
  1713. 'help' => 'This will enable the use of SSL for LDAP connections'
  1714. ),
  1715. array(
  1716. 'type' => 'switch',
  1717. 'name' => 'ldapSSL',
  1718. 'class' => 'ldapAuth switchAuth',
  1719. 'label' => 'Enable LDAP TLS',
  1720. 'value' => $this->config['ldapTLS'],
  1721. 'help' => 'This will enable the use of TLS for LDAP connections'
  1722. ),
  1723. array(
  1724. 'type' => 'button',
  1725. 'name' => 'test-button-ldap',
  1726. 'label' => 'Test Connection',
  1727. 'icon' => 'fa fa-flask',
  1728. 'class' => 'ldapAuth switchAuth',
  1729. 'text' => 'Test Connection',
  1730. 'attr' => 'onclick="testAPIConnection(\'ldap\')"',
  1731. 'help' => 'Remember! Please save before using the test button!'
  1732. ),
  1733. array(
  1734. 'type' => 'button',
  1735. 'name' => 'test-button-ldap-login',
  1736. 'label' => 'Test Login',
  1737. 'icon' => 'fa fa-flask',
  1738. 'class' => 'ldapAuth switchAuth',
  1739. 'text' => 'Test Login',
  1740. 'attr' => 'onclick="showLDAPLoginTest()"'
  1741. ),
  1742. array(
  1743. 'type' => 'input',
  1744. 'name' => 'embyURL',
  1745. 'class' => 'embyAuth switchAuth',
  1746. 'label' => 'Emby URL',
  1747. 'value' => $this->config['embyURL'],
  1748. 'help' => 'Please make sure to use local IP address and port - You also may use local dns name too.',
  1749. 'placeholder' => 'http(s)://hostname:port'
  1750. ),
  1751. array(
  1752. 'type' => 'password-alt',
  1753. 'name' => 'embyToken',
  1754. 'class' => 'embyAuth switchAuth',
  1755. 'label' => 'Emby Token',
  1756. 'value' => $this->config['embyToken'],
  1757. 'placeholder' => ''
  1758. ),
  1759. array(
  1760. 'type' => 'input',
  1761. 'name' => 'jellyfinURL',
  1762. 'class' => 'jellyfinAuth switchAuth',
  1763. 'label' => 'Jellyfin URL',
  1764. 'value' => $this->config['jellyfinURL'],
  1765. 'help' => 'Please make sure to use local IP address and port - You also may use local dns name too.',
  1766. 'placeholder' => 'http(s)://hostname:port'
  1767. ),
  1768. array(
  1769. 'type' => 'password-alt',
  1770. 'name' => 'jellyfinToken',
  1771. 'class' => 'jellyfinAuth switchAuth',
  1772. 'label' => 'Jellyfin Token',
  1773. 'value' => $this->config['jellyfinToken'],
  1774. 'placeholder' => ''
  1775. ),
  1776. ),
  1777. 'Security' => array(
  1778. array(
  1779. 'type' => 'number',
  1780. 'name' => 'loginAttempts',
  1781. 'label' => 'Max Login Attempts',
  1782. 'value' => $this->config['loginAttempts'],
  1783. 'placeholder' => ''
  1784. ),
  1785. array(
  1786. 'type' => 'select',
  1787. 'name' => 'loginLockout',
  1788. 'label' => 'Login Lockout Seconds',
  1789. 'value' => $this->config['loginLockout'],
  1790. 'options' => $this->optionTime()
  1791. ),
  1792. array(
  1793. 'type' => 'number',
  1794. 'name' => 'lockoutTimeout',
  1795. 'label' => 'Inactivity Timer [Minutes]',
  1796. 'value' => $this->config['lockoutTimeout'],
  1797. 'placeholder' => ''
  1798. ),
  1799. array(
  1800. 'type' => 'select',
  1801. 'name' => 'lockoutMinAuth',
  1802. 'label' => 'Lockout Groups From',
  1803. 'value' => $this->config['lockoutMinAuth'],
  1804. 'options' => $this->groupSelect()
  1805. ),
  1806. array(
  1807. 'type' => 'select',
  1808. 'name' => 'lockoutMaxAuth',
  1809. 'label' => 'Lockout Groups To',
  1810. 'value' => $this->config['lockoutMaxAuth'],
  1811. 'options' => $this->groupSelect()
  1812. ),
  1813. array(
  1814. 'type' => 'switch',
  1815. 'name' => 'lockoutSystem',
  1816. 'label' => 'Inactivity Lock',
  1817. 'value' => $this->config['lockoutSystem']
  1818. ),
  1819. array(
  1820. 'type' => 'select',
  1821. 'name' => 'debugAreaAuth',
  1822. 'label' => 'Minimum Authentication for Debug Area',
  1823. 'value' => $this->config['debugAreaAuth'],
  1824. 'options' => $this->groupSelect()
  1825. ),
  1826. array(
  1827. 'type' => 'switch',
  1828. 'name' => 'authDebug',
  1829. 'label' => 'Nginx Auth Debug',
  1830. 'help' => 'Important! Do not keep this enabled for too long as this opens up Authentication while testing.',
  1831. 'value' => $this->config['authDebug'],
  1832. 'class' => 'authDebug'
  1833. ),
  1834. array(
  1835. 'type' => 'select2',
  1836. 'class' => 'select2-multiple',
  1837. 'id' => 'sandbox-select',
  1838. 'name' => 'sandbox',
  1839. 'label' => 'iFrame Sandbox',
  1840. 'value' => $this->config['sandbox'],
  1841. 'help' => 'WARNING! This can potentially mess up your iFrames',
  1842. 'options' => array(
  1843. array(
  1844. 'name' => 'Allow Presentation',
  1845. 'value' => 'allow-presentation'
  1846. ),
  1847. array(
  1848. 'name' => 'Allow Forms',
  1849. 'value' => 'allow-forms'
  1850. ),
  1851. array(
  1852. 'name' => 'Allow Same Origin',
  1853. 'value' => 'allow-same-origin'
  1854. ),
  1855. array(
  1856. 'name' => 'Allow Pointer Lock',
  1857. 'value' => 'allow-pointer-lock'
  1858. ),
  1859. array(
  1860. 'name' => 'Allow Scripts',
  1861. 'value' => 'allow-scripts'
  1862. ), array(
  1863. 'name' => 'Allow Popups',
  1864. 'value' => 'allow-popups'
  1865. ),
  1866. array(
  1867. 'name' => 'Allow Modals',
  1868. 'value' => 'allow-modals'
  1869. ),
  1870. array(
  1871. 'name' => 'Allow Top Navigation',
  1872. 'value' => 'allow-top-navigation'
  1873. ),
  1874. array(
  1875. 'name' => 'Allow Downloads',
  1876. 'value' => 'allow-downloads'
  1877. ),
  1878. )
  1879. ),
  1880. array(
  1881. 'type' => 'switch',
  1882. 'name' => 'traefikAuthEnable',
  1883. 'label' => 'Enable Traefik Auth Redirect',
  1884. 'help' => 'This will enable the webserver to forward errors so traefik will accept them',
  1885. 'value' => $this->config['traefikAuthEnable']
  1886. ),
  1887. ),
  1888. 'Performance' => array(
  1889. array(
  1890. 'type' => 'switch',
  1891. 'name' => 'performanceDisableIconDropdown',
  1892. 'label' => 'Disable Icon Dropdown',
  1893. 'help' => 'Disable select dropdown boxes on new and edit tab forms',
  1894. 'value' => $this->config['performanceDisableIconDropdown'],
  1895. ),
  1896. array(
  1897. 'type' => 'switch',
  1898. 'name' => 'performanceDisableImageDropdown',
  1899. 'label' => 'Disable Image Dropdown',
  1900. 'help' => 'Disable select dropdown boxes on new and edit tab forms',
  1901. 'value' => $this->config['performanceDisableImageDropdown'],
  1902. ),
  1903. ),
  1904. 'Login' => array(
  1905. array(
  1906. 'type' => 'password-alt',
  1907. 'name' => 'registrationPassword',
  1908. 'label' => 'Registration Password',
  1909. 'help' => 'Sets the password for the Registration form on the login screen',
  1910. 'value' => $this->config['registrationPassword'],
  1911. ),
  1912. array(
  1913. 'type' => 'switch',
  1914. 'name' => 'hideRegistration',
  1915. 'label' => 'Hide Registration',
  1916. 'help' => 'Enable this to hide the Registration button on the login screen',
  1917. 'value' => $this->config['hideRegistration'],
  1918. ),
  1919. array(
  1920. 'type' => 'number',
  1921. 'name' => 'rememberMeDays',
  1922. 'label' => 'Remember Me Length',
  1923. 'help' => 'Number of days cookies and tokens will be valid for',
  1924. 'value' => $this->config['rememberMeDays'],
  1925. 'placeholder' => '',
  1926. 'attr' => 'min="1"'
  1927. ),
  1928. array(
  1929. 'type' => 'switch',
  1930. 'name' => 'rememberMe',
  1931. 'label' => 'Remember Me',
  1932. 'help' => 'Default status of Remember Me button on login screen',
  1933. 'value' => $this->config['rememberMe'],
  1934. ),
  1935. array(
  1936. 'type' => 'input',
  1937. 'name' => 'localIPFrom',
  1938. 'label' => 'Override Local IP From',
  1939. 'value' => $this->config['localIPFrom'],
  1940. 'placeholder' => 'i.e. 123.123.123.123',
  1941. 'help' => 'IPv4 only at the moment - This will set your login as local if your IP falls within the From and To'
  1942. ),
  1943. array(
  1944. 'type' => 'input',
  1945. 'name' => 'localIPTo',
  1946. 'label' => 'Override Local IP To',
  1947. 'value' => $this->config['localIPTo'],
  1948. 'placeholder' => 'i.e. 123.123.123.123',
  1949. 'help' => 'IPv4 only at the moment - This will set your login as local if your IP falls within the From and To'
  1950. ),
  1951. array(
  1952. 'type' => 'input',
  1953. 'name' => 'wanDomain',
  1954. 'label' => 'WAN Domain',
  1955. 'value' => $this->config['wanDomain'],
  1956. 'placeholder' => 'only domain and tld - i.e. domain.com',
  1957. 'help' => 'Enter domain if you wish to be forwarded to a local address - Local Address filled out on next item'
  1958. ),
  1959. array(
  1960. 'type' => 'input',
  1961. 'name' => 'localAddress',
  1962. 'label' => 'Local Address',
  1963. 'value' => $this->config['localAddress'],
  1964. 'placeholder' => 'http://home.local',
  1965. 'help' => 'Full local address of organizr install - i.e. http://home.local or http://192.168.0.100'
  1966. ),
  1967. array(
  1968. 'type' => 'switch',
  1969. 'name' => 'enableLocalAddressForward',
  1970. 'label' => 'Enable Local Address Forward',
  1971. 'help' => 'Enables the local address forward if on local address and accessed from WAN Domain',
  1972. 'value' => $this->config['enableLocalAddressForward'],
  1973. ),
  1974. ),
  1975. 'Auth Proxy' => array(
  1976. array(
  1977. 'type' => 'switch',
  1978. 'name' => 'authProxyEnabled',
  1979. 'label' => 'Auth Proxy',
  1980. 'help' => 'Enable option to set Auth Proxy Header Login',
  1981. 'value' => $this->config['authProxyEnabled'],
  1982. ),
  1983. array(
  1984. 'type' => 'input',
  1985. 'name' => 'authProxyHeaderName',
  1986. 'label' => 'Auth Proxy Header Name',
  1987. 'value' => $this->config['authProxyHeaderName'],
  1988. 'placeholder' => 'i.e. X-Forwarded-User',
  1989. 'help' => 'Please choose a unique value for added security'
  1990. ),
  1991. array(
  1992. 'type' => 'input',
  1993. 'name' => 'authProxyWhitelist',
  1994. 'label' => 'Auth Proxy Whitelist',
  1995. 'value' => $this->config['authProxyWhitelist'],
  1996. 'placeholder' => 'i.e. 10.0.0.0/24 or 10.0.0.20',
  1997. 'help' => 'IPv4 only at the moment - This must be set to work, will accept subnet or IP address'
  1998. ),
  1999. ),
  2000. 'Ping' => array(
  2001. array(
  2002. 'type' => 'select',
  2003. 'name' => 'pingAuth',
  2004. 'label' => 'Minimum Authentication',
  2005. 'value' => $this->config['pingAuth'],
  2006. 'options' => $this->groupSelect()
  2007. ),
  2008. array(
  2009. 'type' => 'select',
  2010. 'name' => 'pingAuthMessage',
  2011. 'label' => 'Minimum Authentication for Message and Sound',
  2012. 'value' => $this->config['pingAuthMessage'],
  2013. 'options' => $this->groupSelect()
  2014. ),
  2015. array(
  2016. 'type' => 'select',
  2017. 'name' => 'pingOnlineSound',
  2018. 'label' => 'Online Sound',
  2019. 'value' => $this->config['pingOnlineSound'],
  2020. 'options' => $this->getSounds()
  2021. ),
  2022. array(
  2023. 'type' => 'select',
  2024. 'name' => 'pingOfflineSound',
  2025. 'label' => 'Offline Sound',
  2026. 'value' => $this->config['pingOfflineSound'],
  2027. 'options' => $this->getSounds()
  2028. ),
  2029. array(
  2030. 'type' => 'switch',
  2031. 'name' => 'pingMs',
  2032. 'label' => 'Show Ping Time',
  2033. 'value' => $this->config['pingMs']
  2034. ),
  2035. array(
  2036. 'type' => 'switch',
  2037. 'name' => 'statusSounds',
  2038. 'label' => 'Enable Notify Sounds',
  2039. 'value' => $this->config['statusSounds'],
  2040. 'help' => 'Will play a sound if the server goes down and will play sound if comes back up.',
  2041. ),
  2042. array(
  2043. 'type' => 'select',
  2044. 'name' => 'pingAuthMs',
  2045. 'label' => 'Minimum Authentication for Time Display',
  2046. 'value' => $this->config['pingAuthMs'],
  2047. 'options' => $this->groupSelect()
  2048. ),
  2049. array(
  2050. 'type' => 'select',
  2051. 'name' => 'adminPingRefresh',
  2052. 'label' => 'Admin Refresh Seconds',
  2053. 'value' => $this->config['adminPingRefresh'],
  2054. 'options' => $this->optionTime()
  2055. ),
  2056. array(
  2057. 'type' => 'select',
  2058. 'name' => 'otherPingRefresh',
  2059. 'label' => 'Everyone Refresh Seconds',
  2060. 'value' => $this->config['otherPingRefresh'],
  2061. 'options' => $this->optionTime()
  2062. ),
  2063. )
  2064. );
  2065. }
  2066. public function getSettingsSSO()
  2067. {
  2068. return array(
  2069. 'FYI' => array(
  2070. array(
  2071. 'type' => 'html',
  2072. 'label' => 'Important Information',
  2073. 'override' => 12,
  2074. 'html' => '
  2075. <div class="row">
  2076. <div class="col-lg-12">
  2077. <div class="panel panel-info">
  2078. <div class="panel-heading">
  2079. <span lang="en">Notice</span>
  2080. </div>
  2081. <div class="panel-wrapper collapse in" aria-expanded="true">
  2082. <div class="panel-body">
  2083. <span lang="en">This is not the same as database authentication - i.e. Plex Authentication | Emby Authentication | FTP Authentication<br/>Click Main on the sub-menu above.</span>
  2084. </div>
  2085. </div>
  2086. </div>
  2087. </div>
  2088. </div>
  2089. '
  2090. )
  2091. ),
  2092. 'Plex' => array(
  2093. array(
  2094. 'type' => 'password-alt',
  2095. 'name' => 'plexToken',
  2096. 'label' => 'Plex Token',
  2097. 'value' => $this->config['plexToken'],
  2098. 'placeholder' => 'Use Get Token Button'
  2099. ),
  2100. array(
  2101. 'type' => 'button',
  2102. 'label' => 'Get Plex Token',
  2103. 'class' => 'popup-with-form getPlexTokenSSO',
  2104. 'icon' => 'fa fa-ticket',
  2105. 'text' => 'Retrieve',
  2106. 'href' => '#sso-plex-token-form',
  2107. 'attr' => 'data-effect="mfp-3d-unfold"'
  2108. ),
  2109. array(
  2110. 'type' => 'password-alt',
  2111. 'name' => 'plexID',
  2112. 'label' => 'Plex Machine',
  2113. 'value' => $this->config['plexID'],
  2114. 'placeholder' => 'Use Get Plex Machine Button'
  2115. ),
  2116. array(
  2117. 'type' => 'button',
  2118. 'label' => 'Get Plex Machine',
  2119. 'class' => 'popup-with-form getPlexMachineSSO',
  2120. 'icon' => 'fa fa-id-badge',
  2121. 'text' => 'Retrieve',
  2122. 'href' => '#sso-plex-machine-form',
  2123. 'attr' => 'data-effect="mfp-3d-unfold"'
  2124. ),
  2125. array(
  2126. 'type' => 'input',
  2127. 'name' => 'plexAdmin',
  2128. 'label' => 'Admin Username',
  2129. 'value' => $this->config['plexAdmin'],
  2130. 'placeholder' => 'Admin username for Plex'
  2131. ),
  2132. array(
  2133. 'type' => 'blank',
  2134. 'label' => ''
  2135. ),
  2136. array(
  2137. 'type' => 'html',
  2138. 'label' => 'Plex Note',
  2139. 'html' => '<span lang="en">Please make sure both Token and Machine are filled in</span>'
  2140. ),
  2141. array(
  2142. 'type' => 'switch',
  2143. 'name' => 'ssoPlex',
  2144. 'label' => 'Enable',
  2145. 'value' => $this->config['ssoPlex']
  2146. )
  2147. ),
  2148. 'Tautulli' => array(
  2149. array(
  2150. 'type' => 'input',
  2151. 'name' => 'tautulliURL',
  2152. 'label' => 'Tautulli URL',
  2153. 'value' => $this->config['tautulliURL'],
  2154. 'help' => 'Please make sure to use local IP address and port - You also may use local dns name too.',
  2155. 'placeholder' => 'http(s)://hostname:port'
  2156. ),
  2157. array(
  2158. 'type' => 'switch',
  2159. 'name' => 'ssoTautulli',
  2160. 'label' => 'Enable',
  2161. 'value' => $this->config['ssoTautulli']
  2162. )
  2163. ),
  2164. 'Ombi' => array(
  2165. array(
  2166. 'type' => 'input',
  2167. 'name' => 'ombiURL',
  2168. 'label' => 'Ombi URL',
  2169. 'value' => $this->config['ombiURL'],
  2170. 'help' => 'Please make sure to use local IP address and port - You also may use local dns name too.',
  2171. 'placeholder' => 'http(s)://hostname:port'
  2172. ),
  2173. array(
  2174. 'type' => 'password-alt',
  2175. 'name' => 'ombiToken',
  2176. 'label' => 'Token',
  2177. 'value' => $this->config['ombiToken']
  2178. ),
  2179. array(
  2180. 'type' => 'switch',
  2181. 'name' => 'ssoOmbi',
  2182. 'label' => 'Enable',
  2183. 'value' => $this->config['ssoOmbi']
  2184. )
  2185. )
  2186. );
  2187. }
  2188. public function updateConfigMultiple($array)
  2189. {
  2190. return ($this->updateConfig($array)) ? true : false;
  2191. }
  2192. public function updateConfigItems($array)
  2193. {
  2194. if (!count($array)) {
  2195. $this->setAPIResponse('error', 'No data submitted', 409);
  2196. return false;
  2197. }
  2198. $newItem = array();
  2199. foreach ($array as $k => $v) {
  2200. $v = $v ?? '';
  2201. switch ($v) {
  2202. case 'true':
  2203. $v = (bool)true;
  2204. break;
  2205. case 'false':
  2206. $v = (bool)false;
  2207. break;
  2208. }
  2209. // Hash
  2210. if ((stripos($k, 'password') !== false)) {
  2211. if (!$this->isEncrypted($v)) {
  2212. if ($v !== '') {
  2213. $v = $this->encrypt($v);
  2214. }
  2215. }
  2216. }
  2217. if (strtolower($k) !== 'formkey') {
  2218. $newItem[$k] = $v;
  2219. }
  2220. }
  2221. $this->setAPIResponse('success', 'Config items updated', 200);
  2222. return ($this->updateConfig($newItem)) ? true : false;
  2223. }
  2224. public function updateConfigItem($array)
  2225. {
  2226. $array['value'] = $array['value'] ?? '';
  2227. switch ($array['value']) {
  2228. case 'true':
  2229. $array['value'] = (bool)true;
  2230. break;
  2231. case 'false':
  2232. $array['value'] = (bool)false;
  2233. break;
  2234. }
  2235. // Hash
  2236. if ($array['type'] == 'password') {
  2237. $array['value'] = $this->encrypt($array['value']);
  2238. }
  2239. $newItem = array(
  2240. $array['name'] => $array['value']
  2241. );
  2242. return ($this->updateConfig($newItem)) ? true : false;
  2243. }
  2244. public function testWizardPath($array)
  2245. {
  2246. if ($this->hasDB()) {
  2247. $this->setAPIResponse('error', 'Endpoint disabled as database already exists', 401);
  2248. return false;
  2249. }
  2250. $path = $array['path'] ?? null;
  2251. if (file_exists($path)) {
  2252. if (is_writable($path)) {
  2253. $this->setAPIResponse('success', 'Path exists and is writable', 200);
  2254. return true;
  2255. }
  2256. } else {
  2257. if (is_writable(dirname($path, 1))) {
  2258. if (mkdir($path, 0760, true)) {
  2259. $this->setAPIResponse('success', 'Path is writable - Creating now', 200);
  2260. return true;
  2261. }
  2262. }
  2263. }
  2264. $this->setAPIResponse('error', 'Path is not writable', 401);
  2265. return false;
  2266. }
  2267. public function wizardConfig($array)
  2268. {
  2269. $dbName = $array['dbName'] ?? null;
  2270. $path = $array['dbPath'] ?? null;
  2271. $license = $array['license'] ?? null;
  2272. $hashKey = $array['hashKey'] ?? null;
  2273. $api = $array['api'] ?? null;
  2274. $registrationPassword = $array['registrationPassword'] ?? null;
  2275. $username = $array['username'] ?? null;
  2276. $password = $array['password'] ?? null;
  2277. $email = $array['email'] ?? null;
  2278. $validation = array(
  2279. 'dbName' => $dbName,
  2280. 'dbPath' => $path,
  2281. 'license' => $license,
  2282. 'hashKey' => $hashKey,
  2283. 'api' => $api,
  2284. 'registrationPassword' => $registrationPassword,
  2285. 'username' => $username,
  2286. 'password' => $password,
  2287. 'email' => $email,
  2288. );
  2289. foreach ($validation as $k => $v) {
  2290. if ($v == null) {
  2291. $this->setAPIResponse('error', '[' . $k . '] cannot be empty', 422);
  2292. return false;
  2293. }
  2294. }
  2295. $path = $this->cleanDirectory($path);
  2296. if (file_exists($path)) {
  2297. if (!is_writable($path)) {
  2298. $this->setAPIResponse('error', '[' . $path . '] is not writable', 422);
  2299. return false;
  2300. }
  2301. } else {
  2302. if (is_writable(dirname($path, 1))) {
  2303. if (!mkdir($path, 0760, true)) {
  2304. $this->setAPIResponse('error', '[' . $path . '] is not writable', 422);
  2305. return false;
  2306. }
  2307. } else {
  2308. $this->setAPIResponse('error', '[' . $path . '] is not writable', 422);
  2309. return false;
  2310. }
  2311. }
  2312. $dbName = $this->dbExtension($dbName);
  2313. $configVersion = $this->version;
  2314. $configArray = array(
  2315. 'dbName' => $dbName,
  2316. 'dbLocation' => $path,
  2317. 'license' => $license,
  2318. 'organizrHash' => $hashKey,
  2319. 'organizrAPI' => $api,
  2320. 'registrationPassword' => $registrationPassword,
  2321. 'uuid' => $this->gen_uuid()
  2322. );
  2323. // Create Config
  2324. if ($this->createConfig($configArray)) {
  2325. $this->config = $this->config();
  2326. $this->refreshCookieName();
  2327. $this->connectDB();
  2328. // Call DB Create
  2329. if ($this->createDB($path)) {
  2330. // Add in first user
  2331. if ($this->createFirstAdmin($username, $password, $email)) {
  2332. if ($this->createToken($username, $email, 1)) {
  2333. return true;
  2334. } else {
  2335. $this->setAPIResponse('error', 'error creating token', 500);
  2336. }
  2337. } else {
  2338. $this->setAPIResponse('error', 'error creating admin', 500);
  2339. }
  2340. } else {
  2341. $this->setAPIResponse('error', 'error creating database', 500);
  2342. }
  2343. } else {
  2344. $this->setAPIResponse('error', 'error creating config', 500);
  2345. }
  2346. return false;
  2347. }
  2348. public function createDB($path, $migration = false)
  2349. {
  2350. if (!file_exists($path)) {
  2351. mkdir($path, 0777, true);
  2352. }
  2353. $response = [
  2354. array(
  2355. 'function' => 'query',
  2356. 'query' => 'CREATE TABLE `users` (
  2357. `id` INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE,
  2358. `username` TEXT UNIQUE,
  2359. `password` TEXT,
  2360. `email` TEXT,
  2361. `plex_token` TEXT,
  2362. `group` TEXT,
  2363. `group_id` INTEGER,
  2364. `locked` INTEGER,
  2365. `image` TEXT,
  2366. `register_date` DATE,
  2367. `auth_service` TEXT DEFAULT \'internal\'
  2368. );'
  2369. ),
  2370. array(
  2371. 'function' => 'query',
  2372. 'query' => 'CREATE TABLE `chatroom` (
  2373. `id` INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE,
  2374. `username` TEXT,
  2375. `gravatar` TEXT,
  2376. `uid` TEXT,
  2377. `date` DATE,
  2378. `ip` TEXT,
  2379. `message` TEXT
  2380. );'
  2381. ),
  2382. array(
  2383. 'function' => 'query',
  2384. 'query' => 'CREATE TABLE `tokens` (
  2385. `id` INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE,
  2386. `token` TEXT UNIQUE,
  2387. `user_id` INTEGER,
  2388. `browser` TEXT,
  2389. `ip` TEXT,
  2390. `created` DATE,
  2391. `expires` DATE
  2392. );'
  2393. ),
  2394. array(
  2395. 'function' => 'query',
  2396. 'query' => 'CREATE TABLE `groups` (
  2397. `id` INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE,
  2398. `group` TEXT UNIQUE,
  2399. `group_id` INTEGER,
  2400. `image` TEXT,
  2401. `default` INTEGER
  2402. );'
  2403. ),
  2404. array(
  2405. 'function' => 'query',
  2406. 'query' => 'CREATE TABLE `categories` (
  2407. `id` INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE,
  2408. `order` INTEGER,
  2409. `category` TEXT UNIQUE,
  2410. `category_id` INTEGER,
  2411. `image` TEXT,
  2412. `default` INTEGER
  2413. );'
  2414. ),
  2415. array(
  2416. 'function' => 'query',
  2417. 'query' => 'CREATE TABLE `tabs` (
  2418. `id` INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE,
  2419. `order` INTEGER,
  2420. `category_id` INTEGER,
  2421. `name` TEXT,
  2422. `url` TEXT,
  2423. `url_local` TEXT,
  2424. `default` INTEGER,
  2425. `enabled` INTEGER,
  2426. `group_id` INTEGER,
  2427. `image` TEXT,
  2428. `type` INTEGER,
  2429. `splash` INTEGER,
  2430. `ping` INTEGER,
  2431. `ping_url` TEXT,
  2432. `timeout` INTEGER,
  2433. `timeout_ms` INTEGER,
  2434. `preload` INTEGER
  2435. );'
  2436. ),
  2437. array(
  2438. 'function' => 'query',
  2439. 'query' => 'CREATE TABLE `options` (
  2440. `id` INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE,
  2441. `name` TEXT UNIQUE,
  2442. `value` TEXT
  2443. );'
  2444. ),
  2445. array(
  2446. 'function' => 'query',
  2447. 'query' => 'CREATE TABLE `invites` (
  2448. `id` INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE,
  2449. `code` TEXT UNIQUE,
  2450. `date` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  2451. `email` TEXT,
  2452. `username` TEXT,
  2453. `dateused` TIMESTAMP,
  2454. `usedby` TEXT,
  2455. `ip` TEXT,
  2456. `valid` TEXT,
  2457. `type` TEXT
  2458. );'
  2459. ),
  2460. ];
  2461. return $this->processQueries($response, $migration);
  2462. }
  2463. public function createFirstAdmin($username, $password, $email)
  2464. {
  2465. $userInfo = [
  2466. 'username' => $username,
  2467. 'password' => password_hash($password, PASSWORD_BCRYPT),
  2468. 'email' => $email,
  2469. 'group' => 'Admin',
  2470. 'group_id' => 0,
  2471. 'image' => $this->gravatar($email),
  2472. 'register_date' => $this->currentTime,
  2473. ];
  2474. $groupInfo0 = [
  2475. 'group' => 'Admin',
  2476. 'group_id' => 0,
  2477. 'default' => false,
  2478. 'image' => 'plugins/images/groups/admin.png',
  2479. ];
  2480. $groupInfo1 = [
  2481. 'group' => 'Co-Admin',
  2482. 'group_id' => 1,
  2483. 'default' => false,
  2484. 'image' => 'plugins/images/groups/coadmin.png',
  2485. ];
  2486. $groupInfo2 = [
  2487. 'group' => 'Super User',
  2488. 'group_id' => 2,
  2489. 'default' => false,
  2490. 'image' => 'plugins/images/groups/superuser.png',
  2491. ];
  2492. $groupInfo3 = [
  2493. 'group' => 'Power User',
  2494. 'group_id' => 3,
  2495. 'default' => false,
  2496. 'image' => 'plugins/images/groups/poweruser.png',
  2497. ];
  2498. $groupInfo4 = [
  2499. 'group' => 'User',
  2500. 'group_id' => 4,
  2501. 'default' => true,
  2502. 'image' => 'plugins/images/groups/user.png',
  2503. ];
  2504. $groupInfoGuest = [
  2505. 'group' => 'Guest',
  2506. 'group_id' => 999,
  2507. 'default' => false,
  2508. 'image' => 'plugins/images/groups/guest.png',
  2509. ];
  2510. $settingsInfo = [
  2511. 'order' => 1,
  2512. 'category_id' => 0,
  2513. 'name' => 'Settings',
  2514. 'url' => 'api/v2/page/settings',
  2515. 'default' => false,
  2516. 'enabled' => true,
  2517. 'group_id' => 1,
  2518. 'image' => 'fontawesome::cog',
  2519. 'type' => 0
  2520. ];
  2521. $homepageInfo = [
  2522. 'order' => 2,
  2523. 'category_id' => 0,
  2524. 'name' => 'Homepage',
  2525. 'url' => 'api/v2/page/homepage',
  2526. 'default' => false,
  2527. 'enabled' => false,
  2528. 'group_id' => 4,
  2529. 'image' => 'fontawesome::home',
  2530. 'type' => 0
  2531. ];
  2532. $unsortedInfo = [
  2533. 'order' => 1,
  2534. 'category' => 'Unsorted',
  2535. 'category_id' => 0,
  2536. 'image' => 'plugins/images/categories/unsorted.png',
  2537. 'default' => true
  2538. ];
  2539. $response = [
  2540. array(
  2541. 'function' => 'query',
  2542. 'query' => array(
  2543. 'INSERT INTO [users]',
  2544. $userInfo
  2545. )
  2546. ),
  2547. array(
  2548. 'function' => 'query',
  2549. 'query' => array(
  2550. 'INSERT INTO [groups]',
  2551. $groupInfo0
  2552. )
  2553. ),
  2554. array(
  2555. 'function' => 'query',
  2556. 'query' => array(
  2557. 'INSERT INTO [groups]',
  2558. $groupInfo1
  2559. )
  2560. ),
  2561. array(
  2562. 'function' => 'query',
  2563. 'query' => array(
  2564. 'INSERT INTO [groups]',
  2565. $groupInfo2
  2566. )
  2567. ),
  2568. array(
  2569. 'function' => 'query',
  2570. 'query' => array(
  2571. 'INSERT INTO [groups]',
  2572. $groupInfo3
  2573. )
  2574. ),
  2575. array(
  2576. 'function' => 'query',
  2577. 'query' => array(
  2578. 'INSERT INTO [groups]',
  2579. $groupInfo4
  2580. )
  2581. ),
  2582. array(
  2583. 'function' => 'query',
  2584. 'query' => array(
  2585. 'INSERT INTO [groups]',
  2586. $groupInfoGuest
  2587. )
  2588. ),
  2589. array(
  2590. 'function' => 'query',
  2591. 'query' => array(
  2592. 'INSERT INTO [tabs]',
  2593. $settingsInfo
  2594. )
  2595. ),
  2596. array(
  2597. 'function' => 'query',
  2598. 'query' => array(
  2599. 'INSERT INTO [tabs]',
  2600. $homepageInfo
  2601. )
  2602. ),
  2603. array(
  2604. 'function' => 'query',
  2605. 'query' => array(
  2606. 'INSERT INTO [categories]',
  2607. $unsortedInfo
  2608. )
  2609. ),
  2610. ];
  2611. return $this->processQueries($response);
  2612. }
  2613. public function getUserByUsernameAndEmail($username, $email)
  2614. {
  2615. $response = [
  2616. array(
  2617. 'function' => 'fetch',
  2618. 'query' => array(
  2619. 'SELECT * FROM users WHERE username = ? COLLATE NOCASE OR email = ? COLLATE NOCASE',
  2620. [$username],
  2621. [$email]
  2622. )
  2623. ),
  2624. ];
  2625. return $this->processQueries($response);
  2626. }
  2627. public function createToken($username, $email, $days = 1)
  2628. {
  2629. $days = ($days > 365) ? 365 : $days;
  2630. //Quick get user ID
  2631. $result = $this->getUserByUsernameAndEmail($username, $email);
  2632. // Create JWT
  2633. // Set key
  2634. // SHA256 Encryption
  2635. $signer = new Lcobucci\JWT\Signer\Hmac\Sha256();
  2636. // Start Builder
  2637. $jwttoken = (new Lcobucci\JWT\Builder())->issuedBy('Organizr')// Configures the issuer (iss claim)
  2638. ->permittedFor('Organizr')// Configures the audience (aud claim)
  2639. ->identifiedBy('4f1g23a12aa', true)// Configures the id (jti claim), replicating as a header item
  2640. ->issuedAt(time())// Configures the time that the token was issue (iat claim)
  2641. ->expiresAt(time() + (86400 * $days))// Configures the expiration time of the token (exp claim)
  2642. ->withClaim('username', $result['username'])// Configures a new claim, called "username"
  2643. ->withClaim('group', $result['group'])// Configures a new claim, called "group"
  2644. ->withClaim('groupID', $result['group_id'])// Configures a new claim, called "groupID"
  2645. ->withClaim('email', $result['email'])// Configures a new claim, called "email"
  2646. ->withClaim('image', $result['image'])// Configures a new claim, called "image"
  2647. ->withClaim('userID', $result['id'])// Configures a new claim, called "image"
  2648. ->sign($signer, $this->config['organizrHash'])// creates a signature using "testing" as key
  2649. ->getToken(); // Retrieves the generated token
  2650. $jwttoken->getHeaders(); // Retrieves the token headers
  2651. $jwttoken->getClaims(); // Retrieves the token claims
  2652. $this->coookie('set', $this->cookieName, $jwttoken, $days);
  2653. // Add token to DB
  2654. $addToken = [
  2655. 'token' => (string)$jwttoken,
  2656. 'user_id' => $result['id'],
  2657. 'created' => $this->currentTime,
  2658. 'browser' => isset($_SERVER ['HTTP_USER_AGENT']) ? $_SERVER ['HTTP_USER_AGENT'] : null,
  2659. 'ip' => $this->userIP(),
  2660. 'expires' => gmdate("Y-m-d\TH:i:s\Z", time() + (86400 * $days))
  2661. ];
  2662. $response = [
  2663. array(
  2664. 'function' => 'query',
  2665. 'query' => array(
  2666. 'INSERT INTO [tokens]',
  2667. $addToken
  2668. )
  2669. ),
  2670. ];
  2671. $token = $this->processQueries($response);
  2672. return $jwttoken;
  2673. }
  2674. public function login($array)
  2675. {
  2676. // Grab username, Password & other optional items from api call
  2677. $username = $array['username'] ?? null;
  2678. $password = $array['password'] ?? null;
  2679. $oAuth = $array['oAuth'] ?? null;
  2680. $oAuthType = $array['oAuthType'] ?? null;
  2681. $remember = $array['remember'] ?? null;
  2682. $tfaCode = $array['tfaCode'] ?? null;
  2683. $loginAttempts = $array['loginAttempts'] ?? null;
  2684. $output = $array['output'] ?? null;
  2685. $username = (strpos($this->config['authBackend'], 'emby') !== false) ? $username : strtolower($username);
  2686. $days = (isset($remember)) ? $this->config['rememberMeDays'] : 1;
  2687. // Set other variables
  2688. $function = 'plugin_auth_' . $this->config['authBackend'];
  2689. $authSuccess = false;
  2690. $authProxy = false;
  2691. // Check Login attempts and kill if over limit
  2692. if ($loginAttempts > $this->config['loginAttempts'] || isset($_COOKIE['lockout'])) {
  2693. $this->coookieSeconds('set', 'lockout', $this->config['loginLockout'], $this->config['loginLockout']);
  2694. $this->setAPIResponse('error', 'User is locked out', 403);
  2695. return false;
  2696. }
  2697. // Check if Auth Proxy is enabled
  2698. if ($this->config['authProxyEnabled'] && $this->config['authProxyHeaderName'] !== '' && $this->config['authProxyWhitelist'] !== '') {
  2699. if (isset($this->getallheaders()[$this->config['authProxyHeaderName']])) {
  2700. $usernameHeader = isset($this->getallheaders()[$this->config['authProxyHeaderName']]) ? $this->getallheaders()[$this->config['authProxyHeaderName']] : $username;
  2701. $this->writeLog('success', 'Auth Proxy Function - Starting Verification for IP: ' . $this->userIP() . ' for request on: ' . $_SERVER['REMOTE_ADDR'] . ' against IP/Subnet: ' . $this->config['authProxyWhitelist'], $usernameHeader);
  2702. $whitelistRange = $this->analyzeIP($this->config['authProxyWhitelist']);
  2703. $authProxy = $this->authProxyRangeCheck($whitelistRange['from'], $whitelistRange['to']);
  2704. $username = ($authProxy) ? $usernameHeader : $username;
  2705. if ($authProxy) {
  2706. $this->writeLog('success', 'Auth Proxy Function - IP: ' . $this->userIP() . ' has been verified', $usernameHeader);
  2707. } else {
  2708. $this->writeLog('error', 'Auth Proxy Function - IP: ' . $this->userIP() . ' has failed verification', $usernameHeader);
  2709. }
  2710. }
  2711. }
  2712. // Check if Login method was an oAuth login
  2713. if (!$oAuth) {
  2714. $result = $this->getUserByUsernameAndEmail($username, $username);
  2715. $result['password'] = $result['password'] ?? '';
  2716. // Switch AuthType - internal - external - both
  2717. switch ($this->config['authType']) {
  2718. case 'external':
  2719. if (method_exists($this, $function)) {
  2720. $authSuccess = $this->$function($username, $password);
  2721. }
  2722. break;
  2723. /** @noinspection PhpMissingBreakStatementInspection */
  2724. case 'both':
  2725. if (method_exists($this, $function)) {
  2726. $authSuccess = $this->$function($username, $password);
  2727. }
  2728. // no break
  2729. default: // Internal
  2730. if (!$authSuccess) {
  2731. // perform the internal authentication step
  2732. if (password_verify($password, $result['password'])) {
  2733. $authSuccess = true;
  2734. }
  2735. }
  2736. }
  2737. $authSuccess = ($authProxy) ? true : $authSuccess;
  2738. } else {
  2739. // Has oAuth Token!
  2740. switch ($oAuthType) {
  2741. case 'plex':
  2742. if ($this->config['plexoAuth']) {
  2743. $tokenInfo = $this->checkPlexToken($oAuth);
  2744. if ($tokenInfo) {
  2745. $authSuccess = array(
  2746. 'username' => $tokenInfo['user']['username'],
  2747. 'email' => $tokenInfo['user']['email'],
  2748. 'image' => $tokenInfo['user']['thumb'],
  2749. 'token' => $tokenInfo['user']['authToken']
  2750. );
  2751. $this->coookie('set', 'oAuth', 'true', $this->config['rememberMeDays']);
  2752. $authSuccess = ((!empty($this->config['plexAdmin']) && strtolower($this->config['plexAdmin']) == strtolower($tokenInfo['user']['username'])) || (!empty($this->config['plexAdmin']) && strtolower($this->config['plexAdmin']) == strtolower($tokenInfo['user']['email'])) || $this->checkPlexUser($tokenInfo['user']['username'])) ? $authSuccess : false;
  2753. }
  2754. } else {
  2755. $this->setAPIResponse('error', 'Plex oAuth is not setup', 422);
  2756. return false;
  2757. }
  2758. break;
  2759. default:
  2760. return ($output) ? 'No oAuthType defined' : 'error';
  2761. }
  2762. $result = ($authSuccess) ? $this->getUserByUsernameAndEmail($authSuccess['username'], $authSuccess['email']) : '';
  2763. }
  2764. if ($authSuccess) {
  2765. // Make sure user exists in database
  2766. $userExists = false;
  2767. $passwordMatches = $oAuth || $authProxy;
  2768. $token = (is_array($authSuccess) && isset($authSuccess['token']) ? $authSuccess['token'] : '');
  2769. if (isset($result['username'])) {
  2770. $userExists = true;
  2771. $username = $result['username'];
  2772. if ($passwordMatches == false) {
  2773. $passwordMatches = password_verify($password, $result['password']);
  2774. }
  2775. }
  2776. if ($userExists) {
  2777. //does org password need to be updated
  2778. if (!$passwordMatches) {
  2779. $this->updateUserPassword($password, $result['id']);
  2780. $this->writeLog('success', 'Login Function - User Password updated from backend', $username);
  2781. }
  2782. if ($token !== '') {
  2783. if ($token !== $result['plex_token']) {
  2784. $this->updateUserPlexToken($token, $result['id']);
  2785. $this->writeLog('success', 'Login Function - User Plex Token updated from backend', $username);
  2786. }
  2787. }
  2788. // 2FA might go here
  2789. if ($result['auth_service'] !== 'internal' && strpos($result['auth_service'], '::') !== false) {
  2790. $tfaProceed = true;
  2791. // Add check for local or not
  2792. if ($this->config['ignoreTFALocal'] !== false) {
  2793. $tfaProceed = ($this->isLocal()) ? false : true;
  2794. }
  2795. if ($tfaProceed) {
  2796. $TFA = explode('::', $result['auth_service']);
  2797. // Is code with login info?
  2798. if ($tfaCode == '') {
  2799. $this->setAPIResponse('warning', '2FA Code Needed', 422);
  2800. return false;
  2801. } else {
  2802. if (!$this->verify2FA($TFA[1], $tfaCode, $TFA[0])) {
  2803. $this->writeLoginLog($username, 'error');
  2804. $this->writeLog('error', 'Login Function - Wrong 2FA', $username);
  2805. $this->setAPIResponse('error', 'Wrong 2FA', 422);
  2806. return false;
  2807. }
  2808. }
  2809. }
  2810. }
  2811. // End 2FA
  2812. // authentication passed - 1) mark active and update token
  2813. $createToken = $this->createToken($result['username'], $result['email'], $days);
  2814. if ($createToken) {
  2815. $this->writeLoginLog($username, 'success');
  2816. $this->writeLog('success', 'Login Function - A User has logged in', $username);
  2817. $ssoUser = ((empty($result['email'])) ? $result['username'] : (strpos($result['email'], 'placeholder') !== false)) ? $result['username'] : $result['email'];
  2818. $this->ssoCheck($ssoUser, $password, $token); //need to work on this
  2819. return ($output) ? array('name' => $this->cookieName, 'token' => (string)$createToken) : true;
  2820. } else {
  2821. $this->setAPIResponse('error', 'Token creation error', 500);
  2822. return false;
  2823. }
  2824. } else {
  2825. // Create User
  2826. return $this->authRegister((is_array($authSuccess) && isset($authSuccess['username']) ? $authSuccess['username'] : $username), $password, (is_array($authSuccess) && isset($authSuccess['email']) ? $authSuccess['email'] : ''), $token);
  2827. }
  2828. } else {
  2829. // authentication failed
  2830. $this->writeLoginLog($username, 'error');
  2831. $this->writeLog('error', 'Login Function - Wrong Password', $username);
  2832. if ($loginAttempts >= $this->config['loginAttempts']) {
  2833. $this->coookieSeconds('set', 'lockout', $this->config['loginLockout'], $this->config['loginLockout']);
  2834. $this->setAPIResponse('error', 'User is locked out', 403);
  2835. return false;
  2836. } else {
  2837. $this->setAPIResponse('error', 'User credentials incorrect', 401);
  2838. return false;
  2839. }
  2840. }
  2841. }
  2842. public function logout()
  2843. {
  2844. $this->coookie('delete', $this->cookieName);
  2845. $this->coookie('delete', 'mpt');
  2846. $this->coookie('delete', 'Auth');
  2847. $this->coookie('delete', 'oAuth');
  2848. $this->clearTautulliTokens();
  2849. $this->revokeTokenCurrentUser($this->user['token']);
  2850. $this->user = null;
  2851. return true;
  2852. }
  2853. public function recover($array)
  2854. {
  2855. $email = $array['email'] ?? null;
  2856. if (!$email) {
  2857. $this->setAPIResponse('error', 'Email was not supplied', 422);
  2858. return false;
  2859. }
  2860. $newPassword = $this->randString(10);
  2861. $isUser = $this->getUserByEmail($email);
  2862. if ($isUser) {
  2863. $this->updateUserPassword($newPassword, $isUser['id']);
  2864. $this->setAPIResponse('success', 'User password has been reset', 200);
  2865. $this->writeLog('success', 'User Management Function - User: ' . $isUser['username'] . '\'s password was reset', $isUser['username']);
  2866. if ($this->config['PHPMAILER-enabled']) {
  2867. $PhpMailer = new PhpMailer();
  2868. $emailTemplate = array(
  2869. 'type' => 'reset',
  2870. 'body' => $this->config['PHPMAILER-emailTemplateReset'],
  2871. 'subject' => $this->config['PHPMAILER-emailTemplateResetSubject'],
  2872. 'user' => $isUser['username'],
  2873. 'password' => $newPassword,
  2874. 'inviteCode' => null,
  2875. );
  2876. $emailTemplate = $PhpMailer->_phpMailerPluginEmailTemplate($emailTemplate);
  2877. $sendEmail = array(
  2878. 'to' => $email,
  2879. 'user' => $isUser['username'],
  2880. 'subject' => $emailTemplate['subject'],
  2881. 'body' => $PhpMailer->_phpMailerPluginBuildEmail($emailTemplate),
  2882. );
  2883. $PhpMailer->_phpMailerPluginSendEmail($sendEmail);
  2884. $this->setAPIResponse('success', 'User password has been reset and email has been sent', 200);
  2885. }
  2886. return true;
  2887. } else {
  2888. $this->setAPIResponse('error', 'User not found', 404);
  2889. return false;
  2890. }
  2891. }
  2892. public function register($array)
  2893. {
  2894. $email = $array['email'] ?? null;
  2895. $username = $array['username'] ?? null;
  2896. $password = $array['password'] ?? null;
  2897. $registrationPassword = $array['registrationPassword'] ?? null;
  2898. if (!$email) {
  2899. $this->setAPIResponse('error', 'Email was not supplied', 422);
  2900. return false;
  2901. }
  2902. if (!$username) {
  2903. $this->setAPIResponse('error', 'Username was not supplied', 422);
  2904. return false;
  2905. }
  2906. if (!$password) {
  2907. $this->setAPIResponse('error', 'Password was not supplied', 422);
  2908. return false;
  2909. }
  2910. if (!$registrationPassword) {
  2911. $this->setAPIResponse('error', 'Registration Password was not supplied', 422);
  2912. return false;
  2913. }
  2914. if ($registrationPassword == $this->decrypt($this->config['registrationPassword'])) {
  2915. $this->writeLog('success', 'Registration Function - Registration Password Verified', $username);
  2916. if ($this->createUser($username, $password, $email)) {
  2917. $this->writeLog('success', 'Registration Function - A User has registered', $username);
  2918. if ($this->createToken($username, $email, $this->config['rememberMeDays'])) {
  2919. $this->writeLoginLog($username, 'success');
  2920. $this->writeLog('success', 'Login Function - A User has logged in', $username);
  2921. return true;
  2922. }
  2923. } else {
  2924. return false;
  2925. }
  2926. } else {
  2927. $this->writeLog('warning', 'Registration Function - Wrong Password', $username);
  2928. $this->setAPIResponse('error', 'Registration Password was incorrect', 401);
  2929. return false;
  2930. }
  2931. }
  2932. public function authRegister($username, $password, $email, $token = null)
  2933. {
  2934. if ($this->config['authBackend'] !== '') {
  2935. $this->ombiImport($this->config['authBackend']);
  2936. }
  2937. $this->ssoCheck($username, $password, $token);
  2938. if ($token && (!$password || $password == '')) {
  2939. $password = $this->random_ascii_string(10);
  2940. }
  2941. if ($this->createUser($username, $password, $email)) {
  2942. $this->writeLog('success', 'Registration Function - A User has registered', $username);
  2943. if ($this->config['PHPMAILER-enabled'] && $email !== '') {
  2944. $PhpMailer = new PhpMailer();
  2945. $emailTemplate = array(
  2946. 'type' => 'registration',
  2947. 'body' => $this->config['PHPMAILER-emailTemplateRegisterUser'],
  2948. 'subject' => $this->config['PHPMAILER-emailTemplateRegisterUserSubject'],
  2949. 'user' => $username,
  2950. 'password' => null,
  2951. 'inviteCode' => null,
  2952. );
  2953. $emailTemplate = $PhpMailer->_phpMailerPluginEmailTemplate($emailTemplate);
  2954. $sendEmail = array(
  2955. 'to' => $email,
  2956. 'user' => $username,
  2957. 'subject' => $emailTemplate['subject'],
  2958. 'body' => $PhpMailer->_phpMailerPluginBuildEmail($emailTemplate),
  2959. );
  2960. $PhpMailer->_phpMailerPluginSendEmail($sendEmail);
  2961. }
  2962. if ($this->createToken($username, $email, $this->gravatar($email), $this->config['rememberMeDays'])) {
  2963. $this->writeLoginLog($username, 'success');
  2964. $this->writeLog('success', 'Login Function - A User has logged in', $username);
  2965. return true;
  2966. } else {
  2967. return false;
  2968. }
  2969. } else {
  2970. $this->writeLog('error', 'Registration Function - An error occurred', $username);
  2971. return false;
  2972. }
  2973. }
  2974. public function revokeTokenCurrentUser($token)
  2975. {
  2976. $response = [
  2977. array(
  2978. 'function' => 'query',
  2979. 'query' => array(
  2980. 'DELETE FROM tokens WHERE user_id = ? or token = ?',
  2981. [$this->user['userID']],
  2982. [$token]
  2983. )
  2984. ),
  2985. ];
  2986. return $this->processQueries($response);
  2987. }
  2988. public function revokeToken($token = null)
  2989. {
  2990. if (!$token) {
  2991. $this->setAPIResponse('error', 'Token was not supplied', 422);
  2992. return false;
  2993. }
  2994. $response = [
  2995. array(
  2996. 'function' => 'query',
  2997. 'query' => array(
  2998. 'DELETE FROM tokens WHERE token = ?',
  2999. [$token]
  3000. )
  3001. ),
  3002. ];
  3003. $this->setAPIResponse('success', 'Token revoked', 204);
  3004. return $this->processQueries($response);
  3005. }
  3006. public function revokeTokenByIdCurrentUser($id = null)
  3007. {
  3008. if (!$id) {
  3009. $this->setAPIResponse('error', 'Id was not supplied', 422);
  3010. return false;
  3011. }
  3012. $response = [
  3013. array(
  3014. 'function' => 'query',
  3015. 'query' => array(
  3016. 'DELETE FROM tokens WHERE id = ? AND user_id = ?',
  3017. $id,
  3018. $this->user['userID']
  3019. )
  3020. ),
  3021. ];
  3022. $this->setAPIResponse('success', 'Token revoked', 204);
  3023. return $this->processQueries($response);
  3024. }
  3025. public function updateUserPassword($password, $id)
  3026. {
  3027. $response = [
  3028. array(
  3029. 'function' => 'query',
  3030. 'query' => array(
  3031. 'UPDATE users SET',
  3032. ['password' => password_hash($password, PASSWORD_BCRYPT)],
  3033. 'WHERE id = ?',
  3034. $id
  3035. )
  3036. ),
  3037. ];
  3038. return $this->processQueries($response);
  3039. }
  3040. public function updateUserPlexToken($token, $id)
  3041. {
  3042. $response = [
  3043. array(
  3044. 'function' => 'query',
  3045. 'query' => array(
  3046. 'UPDATE users SET',
  3047. ['plex_token' => $token],
  3048. 'WHERE id = ?',
  3049. $id
  3050. )
  3051. ),
  3052. ];
  3053. return $this->processQueries($response);
  3054. }
  3055. public function getUserTabsAndCategories($type = null)
  3056. {
  3057. if (!$this->hasDB()) {
  3058. return false;
  3059. }
  3060. $sort = ($this->config['unsortedTabs'] == 'top') ? 'DESC' : 'ASC';
  3061. $response = [
  3062. array(
  3063. 'function' => 'fetchAll',
  3064. 'query' => array(
  3065. 'SELECT * FROM tabs WHERE `group_id` >= ? AND `enabled` = 1 ORDER BY `order` ' . $sort,
  3066. $this->user['groupID']
  3067. ),
  3068. 'key' => 'tabs'
  3069. ),
  3070. array(
  3071. 'function' => 'fetchAll',
  3072. 'query' => array(
  3073. 'SELECT * FROM categories ORDER BY `order` ASC',
  3074. ),
  3075. 'key' => 'categories'
  3076. ),
  3077. ];
  3078. $queries = $this->processQueries($response);
  3079. $all['tabs'] = $queries['tabs'];
  3080. foreach ($queries['tabs'] as $k => $v) {
  3081. $v['access_url'] = (!empty($v['url_local']) && ($v['url_local'] !== null) && ($v['url_local'] !== 'null') && $this->isLocal() && $v['type'] !== 0) ? $v['url_local'] : $v['url'];
  3082. }
  3083. $count = array_map(function ($element) {
  3084. return $element['category_id'];
  3085. }, $queries['tabs']);
  3086. $count = (array_count_values($count));
  3087. foreach ($queries['categories'] as $k => $v) {
  3088. $v['count'] = isset($count[$v['category_id']]) ? $count[$v['category_id']] : 0;
  3089. }
  3090. $all['categories'] = $queries['categories'];
  3091. switch ($type) {
  3092. case 'categories':
  3093. return $all['categories'];
  3094. case 'tabs':
  3095. return $all['tabs'];
  3096. default:
  3097. return $all;
  3098. }
  3099. }
  3100. public function refreshList()
  3101. {
  3102. $searchTerm = "Refresh";
  3103. return array_filter($this->config, function ($k) use ($searchTerm) {
  3104. return stripos($k, $searchTerm) !== false;
  3105. }, ARRAY_FILTER_USE_KEY);
  3106. }
  3107. public function homepageOrderList()
  3108. {
  3109. $searchTerm = "homepageOrder";
  3110. $order = array_filter($this->config, function ($k) use ($searchTerm) {
  3111. return stripos($k, $searchTerm) !== false;
  3112. }, ARRAY_FILTER_USE_KEY);
  3113. asort($order);
  3114. return $order;
  3115. }
  3116. public function tautulliList()
  3117. {
  3118. $searchTerm = "tautulli_token";
  3119. return array_filter($this->config, function ($k) use ($searchTerm) {
  3120. return stripos($k, $searchTerm) !== false;
  3121. }, ARRAY_FILTER_USE_KEY);
  3122. }
  3123. public function checkPlexAdminFilled()
  3124. {
  3125. if ($this->config['plexAdmin'] == '') {
  3126. return false;
  3127. } else {
  3128. if ((strpos($this->config['plexAdmin'], '@') !== false)) {
  3129. return 'email';
  3130. } else {
  3131. return 'username';
  3132. }
  3133. }
  3134. }
  3135. public function organizrSpecialSettings()
  3136. {
  3137. return array(
  3138. 'homepage' => array(
  3139. 'refresh' => $this->refreshList(),
  3140. 'order' => $this->homepageOrderList(),
  3141. 'search' => array(
  3142. 'enabled' => $this->qualifyRequest($this->config['mediaSearchAuth']) && $this->config['mediaSearch'] == true && $this->config['plexToken'],
  3143. 'type' => $this->config['mediaSearchType'],
  3144. ),
  3145. 'ombi' => array(
  3146. 'enabled' => $this->qualifyRequest($this->config['homepageOmbiAuth']) && $this->qualifyRequest($this->config['homepageOmbiRequestAuth']) && $this->config['homepageOmbiEnabled'] == true && $this->config['ssoOmbi'] && isset($_COOKIE['Auth']),
  3147. 'authView' => $this->qualifyRequest($this->config['homepageOmbiAuth']),
  3148. 'authRequest' => $this->qualifyRequest($this->config['homepageOmbiRequestAuth']),
  3149. 'sso' => ($this->config['ssoOmbi']) ? true : false,
  3150. 'cookie' => isset($_COOKIE['Auth']),
  3151. 'alias' => ($this->config['ombiAlias']) ? true : false,
  3152. 'ombiDefaultFilterAvailable' => $this->config['ombiDefaultFilterAvailable'] ? true : false,
  3153. 'ombiDefaultFilterUnavailable' => $this->config['ombiDefaultFilterUnavailable'] ? true : false,
  3154. 'ombiDefaultFilterApproved' => $this->config['ombiDefaultFilterApproved'] ? true : false,
  3155. 'ombiDefaultFilterUnapproved' => $this->config['ombiDefaultFilterUnapproved'] ? true : false,
  3156. 'ombiDefaultFilterDenied' => $this->config['ombiDefaultFilterDenied'] ? true : false
  3157. ),
  3158. 'options' => array(
  3159. 'alternateHomepageHeaders' => $this->config['alternateHomepageHeaders'],
  3160. 'healthChecksTags' => $this->config['healthChecksTags'],
  3161. 'titles' => array(
  3162. 'tautulli' => $this->config['tautulliHeader']
  3163. )
  3164. ),
  3165. 'media' => array(
  3166. 'jellyfin' => $this->config['homepageJellyfinInstead']
  3167. )
  3168. ),
  3169. 'sso' => array(
  3170. 'misc' => array(
  3171. 'oAuthLogin' => isset($_COOKIE['oAuth']),
  3172. 'rememberMe' => $this->config['rememberMe'],
  3173. 'rememberMeDays' => $this->config['rememberMeDays']
  3174. ),
  3175. 'plex' => array(
  3176. 'enabled' => ($this->config['ssoPlex']) ? true : false,
  3177. 'cookie' => isset($_COOKIE['mpt']),
  3178. 'machineID' => strlen($this->config['plexID']) == 40,
  3179. 'token' => $this->config['plexToken'] !== '',
  3180. 'plexAdmin' => $this->checkPlexAdminFilled(),
  3181. 'strict' => ($this->config['plexStrictFriends']) ? true : false,
  3182. 'oAuthEnabled' => ($this->config['plexoAuth']) ? true : false,
  3183. 'backend' => $this->config['authBackend'] == 'plex',
  3184. ),
  3185. 'ombi' => array(
  3186. 'enabled' => ($this->config['ssoOmbi']) ? true : false,
  3187. 'cookie' => isset($_COOKIE['Auth']),
  3188. 'url' => ($this->config['ombiURL'] !== '') ? $this->config['ombiURL'] : false,
  3189. 'api' => $this->config['ombiToken'] !== '',
  3190. ),
  3191. 'tautulli' => array(
  3192. 'enabled' => ($this->config['ssoTautulli']) ? true : false,
  3193. 'cookie' => !empty($this->tautulliList()),
  3194. 'url' => ($this->config['tautulliURL'] !== '') ? $this->config['tautulliURL'] : false,
  3195. ),
  3196. ),
  3197. 'ping' => array(
  3198. 'onlineSound' => $this->config['pingOnlineSound'],
  3199. 'offlineSound' => $this->config['pingOfflineSound'],
  3200. 'statusSounds' => $this->config['statusSounds'],
  3201. 'auth' => $this->config['pingAuth'],
  3202. 'authMessage' => $this->config['pingAuthMessage'],
  3203. 'authMs' => $this->config['pingAuthMs'],
  3204. 'ms' => $this->config['pingMs'],
  3205. 'adminRefresh' => $this->config['adminPingRefresh'],
  3206. 'everyoneRefresh' => $this->config['otherPingRefresh'],
  3207. ),
  3208. 'notifications' => array(
  3209. 'backbone' => $this->config['notificationBackbone'],
  3210. 'position' => $this->config['notificationPosition']
  3211. ),
  3212. 'lockout' => array(
  3213. 'enabled' => $this->config['lockoutSystem'],
  3214. 'timer' => $this->config['lockoutTimeout'],
  3215. 'minGroup' => $this->config['lockoutMinAuth'],
  3216. 'maxGroup' => $this->config['lockoutMaxAuth']
  3217. ),
  3218. 'user' => array(
  3219. 'agent' => isset($_SERVER ['HTTP_USER_AGENT']) ? $_SERVER ['HTTP_USER_AGENT'] : null,
  3220. 'oAuthLogin' => isset($_COOKIE['oAuth']),
  3221. 'local' => $this->isLocal(),
  3222. 'ip' => $this->userIP()
  3223. ),
  3224. 'login' => array(
  3225. 'rememberMe' => $this->config['rememberMe'],
  3226. 'rememberMeDays' => $this->config['rememberMeDays'],
  3227. 'wanDomain' => $this->config['wanDomain'],
  3228. 'localAddress' => $this->config['localAddress'],
  3229. 'enableLocalAddressForward' => $this->config['enableLocalAddressForward'],
  3230. ),
  3231. 'misc' => array(
  3232. 'installedPlugins' => $this->qualifyRequest(1) ? $this->config['installedPlugins'] : '',
  3233. 'installedThemes' => $this->qualifyRequest(1) ? $this->config['installedThemes'] : '',
  3234. 'return' => isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : false,
  3235. 'authDebug' => $this->config['authDebug'],
  3236. 'minimalLoginScreen' => $this->config['minimalLoginScreen'],
  3237. 'unsortedTabs' => $this->config['unsortedTabs'],
  3238. 'authType' => $this->config['authType'],
  3239. 'authBackend' => $this->config['authBackend'],
  3240. 'newMessageSound' => (isset($this->config['CHAT-newMessageSound-include'])) ? $this->config['CHAT-newMessageSound-include'] : '',
  3241. 'uuid' => ($this->config['uuid']) ?? null,
  3242. 'docker' => $this->qualifyRequest(1) ? $this->docker : '',
  3243. 'githubCommit' => $this->qualifyRequest(1) ? $this->commit : '',
  3244. 'schema' => $this->qualifyRequest(1) ? $this->getSchema() : '',
  3245. 'debugArea' => $this->qualifyRequest($this->config['debugAreaAuth']),
  3246. 'debugErrors' => $this->config['debugErrors'],
  3247. 'sandbox' => $this->config['sandbox'],
  3248. ),
  3249. 'menuLink' => array(
  3250. 'githubMenuLink' => $this->config['githubMenuLink'],
  3251. 'organizrSupportMenuLink' => $this->config['organizrSupportMenuLink'],
  3252. 'organizrDocsMenuLink' => $this->config['organizrDocsMenuLink'],
  3253. 'organizrSignoutMenuLink' => $this->config['organizrSignoutMenuLink']
  3254. )
  3255. );
  3256. }
  3257. public function getLog($log, $reverse = true)
  3258. {
  3259. switch ($log) {
  3260. case 'login':
  3261. case 'loginLog':
  3262. case 'loginlog':
  3263. $file = $this->organizrLoginLog;
  3264. $parent = 'auth';
  3265. break;
  3266. case 'org':
  3267. case 'organizr':
  3268. case 'organizrLog':
  3269. case 'orglog':
  3270. $file = $this->organizrLog;
  3271. $parent = 'log_items';
  3272. break;
  3273. default:
  3274. $this->setAPIResponse('error', 'Log not defined', 404);
  3275. return null;
  3276. }
  3277. if (!file_exists($file)) {
  3278. $this->setAPIResponse('error', 'Log does not exist', 404);
  3279. return null;
  3280. }
  3281. $getLog = str_replace("\r\ndate", "date", file_get_contents($file));
  3282. $gotLog = json_decode($getLog, true);
  3283. return ($reverse) ? array_reverse($gotLog[$parent]) : $gotLog[$parent];
  3284. }
  3285. public function purgeLog($log)
  3286. {
  3287. switch ($log) {
  3288. case 'login':
  3289. case 'loginLog':
  3290. case 'loginlog':
  3291. $file = $this->organizrLoginLog;
  3292. break;
  3293. case 'org':
  3294. case 'organizr':
  3295. case 'organizrLog':
  3296. case 'orgLog':
  3297. case 'orglog':
  3298. $file = $this->organizrLog;
  3299. break;
  3300. default:
  3301. $this->setAPIResponse('error', 'Log not defined', 404);
  3302. return null;
  3303. }
  3304. if (file_exists($file)) {
  3305. if (unlink($file)) {
  3306. $this->writeLog('success', 'Log Management Function - Log: ' . $log . ' has been purged/deleted', 'SYSTEM');
  3307. $this->setAPIResponse(null, 'Log purged');
  3308. return true;
  3309. } else {
  3310. $this->writeLog('error', 'Log Management Function - Log: ' . $log . ' - Error Occurred', 'SYSTEM');
  3311. $this->setAPIResponse('error', 'Log could not be purged', 500);
  3312. return false;
  3313. }
  3314. } else {
  3315. $this->setAPIResponse('error', 'Log does not exist', 404);
  3316. return false;
  3317. }
  3318. }
  3319. public function checkLog($path)
  3320. {
  3321. if (file_exists($path)) {
  3322. if (filesize($path) > 500000) {
  3323. rename($path, $path . '[' . date('Y-m-d') . '].json');
  3324. return false;
  3325. }
  3326. return true;
  3327. } else {
  3328. return false;
  3329. }
  3330. }
  3331. public function writeLoginLog($username, $authType)
  3332. {
  3333. $username = htmlspecialchars($username, ENT_QUOTES);
  3334. if ($this->checkLog($this->organizrLoginLog)) {
  3335. $getLog = str_replace("\r\ndate", "date", file_get_contents($this->organizrLoginLog));
  3336. $gotLog = json_decode($getLog, true);
  3337. }
  3338. $logEntryFirst = array('logType' => 'login_log', 'auth' => array(array('date' => date("Y-m-d H:i:s"), 'utc_date' => $this->currentTime, 'username' => $username, 'ip' => $this->userIP(), 'auth_type' => $authType)));
  3339. $logEntry = array('date' => date("Y-m-d H:i:s"), 'utc_date' => $this->currentTime, 'username' => $username, 'ip' => $this->userIP(), 'auth_type' => $authType);
  3340. if (isset($gotLog)) {
  3341. array_push($gotLog["auth"], $logEntry);
  3342. $writeFailLog = str_replace("date", "\r\ndate", json_encode($gotLog));
  3343. } else {
  3344. $writeFailLog = str_replace("date", "\r\ndate", json_encode($logEntryFirst));
  3345. }
  3346. file_put_contents($this->organizrLoginLog, $writeFailLog);
  3347. }
  3348. public function writeLog($type = 'error', $message, $username = null)
  3349. {
  3350. $this->timeExecution = $this->timeExecution($this->timeExecution);
  3351. $message = $message . ' [Execution Time: ' . $this->formatSeconds($this->timeExecution) . ']';
  3352. $username = ($username) ? htmlspecialchars($username, ENT_QUOTES) : $this->user['username'];
  3353. if ($this->checkLog($this->organizrLog)) {
  3354. $getLog = str_replace("\r\ndate", "date", file_get_contents($this->organizrLog));
  3355. $gotLog = json_decode($getLog, true);
  3356. }
  3357. $logEntryFirst = array('logType' => 'organizr_log', 'log_items' => array(array('date' => date("Y-m-d H:i:s"), 'utc_date' => $this->currentTime, 'type' => $type, 'username' => $username, 'ip' => $this->userIP(), 'message' => $message)));
  3358. $logEntry = array('date' => date("Y-m-d H:i:s"), 'utc_date' => $this->currentTime, 'type' => $type, 'username' => $username, 'ip' => $this->userIP(), 'message' => $message);
  3359. if (isset($gotLog)) {
  3360. array_push($gotLog["log_items"], $logEntry);
  3361. $writeFailLog = str_replace("date", "\r\ndate", json_encode($gotLog));
  3362. } else {
  3363. $writeFailLog = str_replace("date", "\r\ndate", json_encode($logEntryFirst));
  3364. }
  3365. file_put_contents($this->organizrLog, $writeFailLog);
  3366. }
  3367. public function isApprovedRequest($method, $data)
  3368. {
  3369. $requesterToken = isset($this->getallheaders()['Token']) ? $this->getallheaders()['Token'] : (isset($_GET['apikey']) ? $_GET['apikey'] : false);
  3370. $apiKey = ($this->config['organizrAPI']) ?? null;
  3371. if (isset($data['formKey'])) {
  3372. $formKey = $data['formKey'];
  3373. } elseif (isset($this->getallheaders()['Formkey'])) {
  3374. $formKey = $this->getallheaders()['Formkey'];
  3375. } elseif (isset($this->getallheaders()['formkey'])) {
  3376. $formKey = $this->getallheaders()['formkey'];
  3377. } elseif (isset($this->getallheaders()['formKey'])) {
  3378. $formKey = $this->getallheaders()['formKey'];
  3379. } elseif (isset($this->getallheaders()['FormKey'])) {
  3380. $formKey = $this->getallheaders()['FormKey'];
  3381. } else {
  3382. $formKey = false;
  3383. }
  3384. // Check token or API key
  3385. // If API key, return 0 for admin
  3386. if (strlen($requesterToken) == 20 && $requesterToken == $apiKey) {
  3387. //DO API CHECK
  3388. return true;
  3389. } elseif ($method == 'POST') {
  3390. if ($this->checkFormKey($formKey)) {
  3391. return true;
  3392. } else {
  3393. $this->writeLog('error', 'API ERROR: Unable to authenticate Form Key: ' . $formKey, $this->user['username']);
  3394. return false;
  3395. }
  3396. } else {
  3397. return true;
  3398. }
  3399. return false;
  3400. }
  3401. public function checkFormKey($formKey = '')
  3402. {
  3403. return password_verify(substr($this->config['organizrHash'], 2, 10), $formKey);
  3404. }
  3405. public function buildHomepage()
  3406. {
  3407. $homepageOrder = $this->homepageOrderList();
  3408. $homepageBuilt = '';
  3409. foreach ($homepageOrder as $key => $value) {
  3410. $homepageBuilt .= $this->buildHomepageItem($key);
  3411. }
  3412. return $homepageBuilt;
  3413. }
  3414. public function buildHomepageItem($homepageItem)
  3415. {
  3416. $item = '<div id="' . $homepageItem . '">';
  3417. switch ($homepageItem) {
  3418. case 'homepageOrdercustomhtml':
  3419. if ($this->config['homepageCustomHTMLoneEnabled'] && $this->qualifyRequest($this->config['homepageCustomHTMLoneAuth'])) {
  3420. $item .= ($this->config['customHTMLone'] !== '') ? $this->config['customHTMLone'] : '';
  3421. }
  3422. break;
  3423. case 'homepageOrdercustomhtmlTwo':
  3424. if ($this->config['homepageCustomHTMLtwoEnabled'] && $this->qualifyRequest($this->config['homepageCustomHTMLtwoAuth'])) {
  3425. $item .= ($this->config['customHTMLtwo'] !== '') ? $this->config['customHTMLtwo'] : '';
  3426. }
  3427. break;
  3428. case 'homepageOrdernotice':
  3429. break;
  3430. case 'homepageOrdernoticeguest':
  3431. break;
  3432. case 'homepageOrderqBittorrent':
  3433. if ($this->config['homepageqBittorrentEnabled'] && $this->qualifyRequest($this->config['homepageqBittorrentAuth'])) {
  3434. if ($this->config['qBittorrentCombine']) {
  3435. $item .= '
  3436. <script>
  3437. // homepageOrderqBittorrent
  3438. buildDownloaderCombined(\'qBittorrent\');
  3439. homepageDownloader("qBittorrent", "' . $this->config['homepageDownloadRefresh'] . '");
  3440. // End homepageOrderqBittorrent
  3441. </script>
  3442. ';
  3443. } else {
  3444. $item .= '<div class="white-box homepage-loading-box"><h2 class="text-center" lang="en">Loading Download Queue...</h2></div>';
  3445. $item .= '
  3446. <script>
  3447. // homepageOrderqBittorrent
  3448. $("#' . $homepageItem . '").html(buildDownloader("qBittorrent"));
  3449. homepageDownloader("qBittorrent", "' . $this->config['homepageDownloadRefresh'] . '");
  3450. // End homepageOrderqBittorrent
  3451. </script>
  3452. ';
  3453. }
  3454. }
  3455. break;
  3456. case 'homepageOrderrTorrent':
  3457. if ($this->config['homepagerTorrentEnabled'] && $this->qualifyRequest($this->config['homepagerTorrentAuth'])) {
  3458. if ($this->config['rTorrentCombine']) {
  3459. $item .= '
  3460. <script>
  3461. // homepageOrderrTorrent
  3462. buildDownloaderCombined(\'rTorrent\');
  3463. homepageDownloader("rTorrent", "' . $this->config['homepageDownloadRefresh'] . '");
  3464. // End homepageOrderrTorrent
  3465. </script>
  3466. ';
  3467. } else {
  3468. $item .= '<div class="white-box homepage-loading-box"><h2 class="text-center" lang="en">Loading Download Queue...</h2></div>';
  3469. $item .= '
  3470. <script>
  3471. // homepageOrderrTorrent
  3472. $("#' . $homepageItem . '").html(buildDownloader("rTorrent"));
  3473. homepageDownloader("rTorrent", "' . $this->config['homepageDownloadRefresh'] . '");
  3474. // End homepageOrderrTorrent
  3475. </script>
  3476. ';
  3477. }
  3478. }
  3479. break;
  3480. case 'homepageOrderdeluge':
  3481. if ($this->config['homepageDelugeEnabled'] && $this->qualifyRequest($this->config['homepageDelugeAuth'])) {
  3482. if ($this->config['delugeCombine']) {
  3483. $item .= '
  3484. <script>
  3485. // Deluge
  3486. buildDownloaderCombined(\'deluge\');
  3487. homepageDownloader("deluge", "' . $this->config['homepageDownloadRefresh'] . '");
  3488. // End Deluge
  3489. </script>
  3490. ';
  3491. } else {
  3492. $item .= '<div class="white-box homepage-loading-box"><h2 class="text-center" lang="en">Loading Download Queue...</h2></div>';
  3493. $item .= '
  3494. <script>
  3495. // Deluge
  3496. $("#' . $homepageItem . '").html(buildDownloader("deluge"));
  3497. homepageDownloader("deluge", "' . $this->config['homepageDownloadRefresh'] . '");
  3498. // End Deluge
  3499. </script>
  3500. ';
  3501. }
  3502. }
  3503. break;
  3504. case 'homepageOrdertransmission':
  3505. if ($this->config['homepageTransmissionEnabled'] && $this->qualifyRequest($this->config['homepageTransmissionAuth'])) {
  3506. if ($this->config['transmissionCombine']) {
  3507. $item .= '
  3508. <script>
  3509. // Transmission
  3510. buildDownloaderCombined(\'transmission\');
  3511. homepageDownloader("transmission", "' . $this->config['homepageDownloadRefresh'] . '");
  3512. // End Transmission
  3513. </script>
  3514. ';
  3515. } else {
  3516. $item .= '<div class="white-box homepage-loading-box"><h2 class="text-center" lang="en">Loading Download Queue...</h2></div>';
  3517. $item .= '
  3518. <script>
  3519. // Transmission
  3520. $("#' . $homepageItem . '").html(buildDownloader("transmission"));
  3521. homepageDownloader("transmission", "' . $this->config['homepageDownloadRefresh'] . '");
  3522. // End Transmission
  3523. </script>
  3524. ';
  3525. }
  3526. }
  3527. break;
  3528. case 'homepageOrdernzbget':
  3529. if ($this->config['homepageNzbgetEnabled'] && $this->qualifyRequest($this->config['homepageNzbgetAuth'])) {
  3530. if ($this->config['nzbgetCombine']) {
  3531. $item .= '
  3532. <script>
  3533. // NZBGet
  3534. buildDownloaderCombined(\'nzbget\');
  3535. homepageDownloader("nzbget", "' . $this->config['homepageDownloadRefresh'] . '");
  3536. // End NZBGet
  3537. </script>
  3538. ';
  3539. } else {
  3540. $item .= '<div class="white-box homepage-loading-box"><h2 class="text-center" lang="en">Loading Download Queue...</h2></div>';
  3541. $item .= '
  3542. <script>
  3543. // NZBGet
  3544. $("#' . $homepageItem . '").html(buildDownloader("nzbget"));
  3545. homepageDownloader("nzbget", "' . $this->config['homepageDownloadRefresh'] . '");
  3546. // End NZBGet
  3547. </script>
  3548. ';
  3549. }
  3550. }
  3551. break;
  3552. case 'homepageOrderjdownloader':
  3553. if ($this->config['homepageJdownloaderEnabled'] && $this->qualifyRequest($this->config['homepageJdownloaderAuth'])) {
  3554. if ($this->config['jdownloaderCombine']) {
  3555. $item .= '
  3556. <script>
  3557. // JDownloader
  3558. buildDownloaderCombined(\'jdownloader\');
  3559. homepageDownloader("jdownloader", "' . $this->config['homepageDownloadRefresh'] . '");
  3560. // End JDownloader
  3561. </script>
  3562. ';
  3563. } else {
  3564. $item .= '<div class="white-box homepage-loading-box"><h2 class="text-center" lang="en">Loading Download Queue...</h2></div>';
  3565. $item .= '
  3566. <script>
  3567. // JDownloader
  3568. $("#' . $homepageItem . '").html(buildDownloader("jdownloader"));
  3569. homepageDownloader("jdownloader", "' . $this->config['homepageDownloadRefresh'] . '");
  3570. // End JDownloader
  3571. </script>
  3572. ';
  3573. }
  3574. }
  3575. break;
  3576. case 'homepageOrdersabnzbd':
  3577. if ($this->config['homepageSabnzbdEnabled'] && $this->qualifyRequest($this->config['homepageSabnzbdAuth'])) {
  3578. if ($this->config['sabnzbdCombine']) {
  3579. $item .= '
  3580. <script>
  3581. // SabNZBd
  3582. buildDownloaderCombined(\'sabnzbd\');
  3583. homepageDownloader("sabnzbd", "' . $this->config['homepageDownloadRefresh'] . '");
  3584. // End SabNZBd
  3585. </script>
  3586. ';
  3587. } else {
  3588. $item .= '<div class="white-box homepage-loading-box"><h2 class="text-center" lang="en">Loading Download Queue...</h2></div>';
  3589. $item .= '
  3590. <script>
  3591. // SabNZBd
  3592. $("#' . $homepageItem . '").html(buildDownloader("sabnzbd"));
  3593. homepageDownloader("sabnzbd", "' . $this->config['homepageDownloadRefresh'] . '");
  3594. // End SabNZBd
  3595. </script>
  3596. ';
  3597. }
  3598. }
  3599. break;
  3600. case 'homepageOrderplexnowplaying':
  3601. if ($this->config['homepagePlexStreams']) {
  3602. $item .= '<div class="white-box homepage-loading-box"><h2 class="text-center" lang="en">Loading Now Playing...</h2></div>';
  3603. $item .= '
  3604. <script>
  3605. // Plex Stream
  3606. homepageStream("plex", "' . $this->config['homepageStreamRefresh'] . '");
  3607. // End Plex Stream
  3608. </script>
  3609. ';
  3610. }
  3611. break;
  3612. case 'homepageOrderplexrecent':
  3613. if ($this->config['homepagePlexRecent']) {
  3614. $item .= '<div class="white-box homepage-loading-box"><h2 class="text-center" lang="en">Loading Recent...</h2></div>';
  3615. $item .= '
  3616. <script>
  3617. // Plex Recent
  3618. homepageRecent("plex", "' . $this->config['homepageRecentRefresh'] . '");
  3619. // End Plex Recent
  3620. </script>
  3621. ';
  3622. }
  3623. break;
  3624. case 'homepageOrderplexplaylist':
  3625. if ($this->config['homepagePlexPlaylist']) {
  3626. $item .= '<div class="white-box homepage-loading-box"><h2 class="text-center" lang="en">Loading Playlists...</h2></div>';
  3627. $item .= '
  3628. <script>
  3629. // Plex Playlist
  3630. homepagePlaylist("plex");
  3631. // End Plex Playlist
  3632. </script>
  3633. ';
  3634. }
  3635. break;
  3636. case 'homepageOrderembynowplaying':
  3637. if ($this->config['homepageEmbyStreams'] && $this->config['homepageEmbyEnabled']) {
  3638. $item .= '<div class="white-box homepage-loading-box"><h2 class="text-center" lang="en">Loading Now Playing...</h2></div>';
  3639. $item .= '
  3640. <script>
  3641. // Emby Stream
  3642. homepageStream("emby", "' . $this->config['homepageStreamRefresh'] . '");
  3643. // End Emby Stream
  3644. </script>
  3645. ';
  3646. }
  3647. break;
  3648. case 'homepageOrderembyrecent':
  3649. if ($this->config['homepageEmbyRecent'] && $this->config['homepageEmbyEnabled']) {
  3650. $item .= '<div class="white-box homepage-loading-box"><h2 class="text-center" lang="en">Loading Recent...</h2></div>';
  3651. $item .= '
  3652. <script>
  3653. // Emby Recent
  3654. homepageRecent("emby", "' . $this->config['homepageRecentRefresh'] . '");
  3655. // End Emby Recent
  3656. </script>
  3657. ';
  3658. }
  3659. break;
  3660. case 'homepageOrderjellyfinnowplaying':
  3661. if ($this->config['homepageJellyfinStreams'] && $this->config['homepageJellyfinEnabled']) {
  3662. $item .= '<div class="white-box homepage-loading-box"><h2 class="text-center" lang="en">Loading Now Playing...</h2></div>';
  3663. $item .= '
  3664. <script>
  3665. // Jellyfin Stream
  3666. homepageStream("jellyfin", "' . $this->config['homepageStreamRefresh'] . '");
  3667. // End Jellyfin Stream
  3668. </script>
  3669. ';
  3670. }
  3671. break;
  3672. case 'homepageOrderjellyfinrecent':
  3673. if ($this->config['homepageJellyfinRecent'] && $this->config['homepageJellyfinEnabled']) {
  3674. $item .= '<div class="white-box homepage-loading-box"><h2 class="text-center" lang="en">Loading Recent...</h2></div>';
  3675. $item .= '
  3676. <script>
  3677. // Jellyfin Recent
  3678. homepageRecent("jellyfin", "' . $this->config['homepageRecentRefresh'] . '");
  3679. // End Jellyfin Recent
  3680. </script>
  3681. ';
  3682. }
  3683. break;
  3684. case 'homepageOrderombi':
  3685. if ($this->config['homepageOmbiEnabled']) {
  3686. $item .= '<div class="white-box homepage-loading-box"><h2 class="text-center" lang="en">Loading Requests...</h2></div>';
  3687. $item .= '
  3688. <script>
  3689. // Ombi Requests
  3690. homepageRequests("' . $this->config['ombiRefresh'] . '");
  3691. // End Ombi Requests
  3692. </script>
  3693. ';
  3694. }
  3695. break;
  3696. case 'homepageOrdercalendar':
  3697. if (
  3698. ($this->config['homepageLidarrEnabled'] && $this->qualifyRequest($this->config['homepageLidarrAuth'])) ||
  3699. ($this->config['homepageSonarrEnabled'] && $this->qualifyRequest($this->config['homepageSonarrAuth'])) ||
  3700. ($this->config['homepageRadarrEnabled'] && $this->qualifyRequest($this->config['homepageRadarrAuth'])) ||
  3701. ($this->config['homepageSickrageEnabled'] && $this->qualifyRequest($this->config['homepageSickrageAuth'])) ||
  3702. ($this->config['homepageCouchpotatoEnabled'] && $this->qualifyRequest($this->config['homepageCouchpotatoAuth'])) ||
  3703. ($this->config['homepageCalendarEnabled'] && $this->qualifyRequest($this->config['homepageCalendarAuth']) && $this->config['calendariCal'] !== '')
  3704. ) {
  3705. $item .= '
  3706. <div id="calendar" class="fc fc-ltr m-b-30"></div>
  3707. <script>
  3708. // Calendar
  3709. homepageCalendar("' . $this->config['calendarRefresh'] . '");
  3710. // End Calendar
  3711. </script>
  3712. ';
  3713. }
  3714. break;
  3715. case 'homepageOrderhealthchecks':
  3716. if ($this->config['homepageHealthChecksEnabled'] && $this->qualifyRequest($this->config['homepageHealthChecksAuth'])) {
  3717. $item .= '<div class="white-box homepage-loading-box"><h2 class="text-center" lang="en">Loading Health Checks...</h2></div>';
  3718. $item .= '
  3719. <script>
  3720. // Health Checks
  3721. homepageHealthChecks("' . $this->config['healthChecksTags'] . '","' . $this->config['homepageHealthChecksRefresh'] . '");
  3722. // End Health Checks
  3723. </script>
  3724. ';
  3725. }
  3726. break;
  3727. case 'homepageOrderunifi':
  3728. if ($this->config['homepageUnifiEnabled'] && $this->qualifyRequest($this->config['homepageUnifiAuth'])) {
  3729. $item .= '<div class="white-box homepage-loading-box"><h2 class="text-center" lang="en">Loading Unifi...</h2></div>';
  3730. $item .= '
  3731. <script>
  3732. // Unifi
  3733. homepageUnifi("' . $this->config['homepageHealthChecksRefresh'] . '");
  3734. // End Unifi
  3735. </script>
  3736. ';
  3737. }
  3738. break;
  3739. case 'homepageOrdertautulli':
  3740. if ($this->config['homepageTautulliEnabled'] && $this->qualifyRequest($this->config['homepageTautulliAuth'])) {
  3741. $item .= '<div class="white-box homepage-loading-box"><h2 class="text-center" lang="en">Loading Tautulli...</h2></div>';
  3742. $item .= '
  3743. <script>
  3744. // Tautulli
  3745. homepageTautulli("' . $this->config['homepageTautulliRefresh'] . '");
  3746. // End Tautulli
  3747. </script>
  3748. ';
  3749. }
  3750. break;
  3751. case 'homepageOrderPihole':
  3752. if ($this->config['homepagePiholeEnabled']) {
  3753. $item .= '<div class="white-box homepage-loading-box"><h2 class="text-center" lang="en">Loading Pi-hole Stats...</h2></div>';
  3754. $item .= '
  3755. <script>
  3756. // Pi-hole Stats
  3757. homepagePihole("' . $this->config['homepagePiholeRefresh'] . '");
  3758. // End Pi-hole Stats
  3759. </script>
  3760. ';
  3761. }
  3762. break;
  3763. case 'homepageOrderMonitorr':
  3764. if ($this->config['homepageMonitorrEnabled']) {
  3765. $item .= '<div class="white-box homepage-loading-box"><h2 class="text-center" lang="en">Loading Monitorr...</h2></div>';
  3766. $item .= '
  3767. <script>
  3768. // Monitorr
  3769. homepageMonitorr("' . $this->config['homepageMonitorrRefresh'] . '");
  3770. // End Monitorr
  3771. </script>
  3772. ';
  3773. }
  3774. break;
  3775. case 'homepageOrderWeatherAndAir':
  3776. if ($this->config['homepageWeatherAndAirEnabled']) {
  3777. $item .= '<div class="white-box homepage-loading-box"><h2 class="text-center" lang="en">Loading Weather And Air...</h2></div>';
  3778. $item .= '
  3779. <script>
  3780. // Weather And Air
  3781. homepageWeatherAndAir("' . $this->config['homepageWeatherAndAirRefresh'] . '");
  3782. // End Weather And Air
  3783. </script>
  3784. ';
  3785. }
  3786. break;
  3787. case 'homepageOrderSpeedtest':
  3788. if ($this->config['homepageSpeedtestEnabled']) {
  3789. $item .= '<div class="white-box homepage-loading-box"><h2 class="text-center" lang="en">Loading Speedtest...</h2></div>';
  3790. $item .= '
  3791. <script>
  3792. // Speedtest
  3793. homepageSpeedtest("' . $this->config['homepageSpeedtestRefresh'] . '");
  3794. // End Speedtest
  3795. </script>
  3796. ';
  3797. }
  3798. break;
  3799. case 'homepageOrderNetdata':
  3800. if ($this->config['homepageNetdataEnabled']) {
  3801. $item .= '<div class="white-box homepage-loading-box"><h2 class="text-center" lang="en">Loading Netdata...</h2></div>';
  3802. $item .= '
  3803. <script>
  3804. // Netdata
  3805. homepageNetdata("' . $this->config['homepageNetdataRefresh'] . '");
  3806. // End Netdata
  3807. </script>
  3808. ';
  3809. }
  3810. break;
  3811. case 'homepageOrderOctoprint':
  3812. if ($this->config['homepageOctoprintEnabled']) {
  3813. $item .= '<div class="white-box homepage-loading-box"><h2 class="text-center" lang="en">Loading Octoprint...</h2></div>';
  3814. $item .= '
  3815. <script>
  3816. // Octoprint
  3817. homepageOctoprint("' . $this->config['homepageOctoprintRefresh'] . '");
  3818. // End Octoprint
  3819. </script>
  3820. ';
  3821. }
  3822. break;
  3823. case 'homepageOrderSonarrQueue':
  3824. if ($this->config['homepageSonarrQueueEnabled'] && $this->qualifyRequest($this->config['homepageSonarrQueueAuth'])) {
  3825. if ($this->config['homepageSonarrQueueCombine']) {
  3826. $item .= '
  3827. <script>
  3828. // Sonarr Queue
  3829. buildDownloaderCombined(\'sonarr\');
  3830. homepageDownloader("sonarr", "' . $this->config['homepageSonarrQueueRefresh'] . '");
  3831. // End Sonarr Queue
  3832. </script>
  3833. ';
  3834. } else {
  3835. $item .= '<div class="white-box homepage-loading-box"><h2 class="text-center" lang="en">Loading Download Queue...</h2></div>';
  3836. $item .= '
  3837. <script>
  3838. // Sonarr Queue
  3839. $("#' . $homepageItem . '").html(buildDownloader("sonarr"));
  3840. homepageDownloader("sonarr", "' . $this->config['homepageSonarrQueueRefresh'] . '");
  3841. // End Sonarr Queue
  3842. </script>
  3843. ';
  3844. }
  3845. }
  3846. break;
  3847. case 'homepageOrderRadarrQueue':
  3848. if ($this->config['homepageRadarrQueueEnabled'] && $this->qualifyRequest($this->config['homepageRadarrQueueAuth'])) {
  3849. if ($this->config['homepageRadarrQueueCombine']) {
  3850. $item .= '
  3851. <script>
  3852. // Radarr Queue
  3853. buildDownloaderCombined(\'radarr\');
  3854. homepageDownloader("radarr", "' . $this->config['homepageRadarrQueueRefresh'] . '");
  3855. // End Radarr Queue
  3856. </script>
  3857. ';
  3858. } else {
  3859. $item .= '<div class="white-box homepage-loading-box"><h2 class="text-center" lang="en">Loading Download Queue...</h2></div>';
  3860. $item .= '
  3861. <script>
  3862. // Radarr Queue
  3863. $("#' . $homepageItem . '").html(buildDownloader("radarr"));
  3864. homepageDownloader("radarr", "' . $this->config['homepageRadarrQueueRefresh'] . '");
  3865. // End Radarr Queue
  3866. </script>
  3867. ';
  3868. }
  3869. }
  3870. break;
  3871. default:
  3872. # code...
  3873. break;
  3874. }
  3875. return $item . '</div>';
  3876. }
  3877. public function buildHomepageSettings()
  3878. {
  3879. $homepageOrder = $this->homepageOrderList();
  3880. $homepageList = '<h4>Drag Homepage Items to Order Them</h4><div id="homepage-items-sort" class="external-events">';
  3881. $inputList = '<form id="homepage-values" class="row">';
  3882. foreach ($homepageOrder as $key => $val) {
  3883. switch ($key) {
  3884. case 'homepageOrdercustomhtml':
  3885. $class = 'bg-info';
  3886. $image = 'plugins/images/tabs/custom1.png';
  3887. if (!$this->config['homepageCustomHTMLoneEnabled']) {
  3888. $class .= ' faded';
  3889. }
  3890. break;
  3891. case 'homepageOrdercustomhtmlTwo':
  3892. $class = 'bg-info';
  3893. $image = 'plugins/images/tabs/custom2.png';
  3894. if (!$this->config['homepageCustomHTMLtwoEnabled']) {
  3895. $class .= ' faded';
  3896. }
  3897. break;
  3898. case 'homepageOrdertransmission':
  3899. $class = 'bg-transmission';
  3900. $image = 'plugins/images/tabs/transmission.png';
  3901. if (!$this->config['homepageTransmissionEnabled']) {
  3902. $class .= ' faded';
  3903. }
  3904. break;
  3905. case 'homepageOrdernzbget':
  3906. $class = 'bg-nzbget';
  3907. $image = 'plugins/images/tabs/nzbget.png';
  3908. if (!$this->config['homepageNzbgetEnabled']) {
  3909. $class .= ' faded';
  3910. }
  3911. break;
  3912. case 'homepageOrderjdownloader':
  3913. $class = 'bg-sab';
  3914. $image = 'plugins/images/tabs/jdownloader.png';
  3915. if (!$this->config['homepageJdownloaderEnabled']) {
  3916. $class .= ' faded';
  3917. }
  3918. break;
  3919. case 'homepageOrdersabnzbd':
  3920. $class = 'bg-sab';
  3921. $image = 'plugins/images/tabs/sabnzbd.png';
  3922. if (!$this->config['homepageSabnzbdEnabled']) {
  3923. $class .= ' faded';
  3924. }
  3925. break;
  3926. case 'homepageOrderdeluge':
  3927. $class = 'bg-deluge';
  3928. $image = 'plugins/images/tabs/deluge.png';
  3929. if (!$this->config['homepageDelugeEnabled']) {
  3930. $class .= ' faded';
  3931. }
  3932. break;
  3933. case 'homepageOrderqBittorrent':
  3934. $class = 'bg-qbit';
  3935. $image = 'plugins/images/tabs/qBittorrent.png';
  3936. if (!$this->config['homepageqBittorrentEnabled']) {
  3937. $class .= ' faded';
  3938. }
  3939. break;
  3940. case 'homepageOrderrTorrent':
  3941. $class = 'bg-qbit';
  3942. $image = 'plugins/images/tabs/rTorrent.png';
  3943. if (!$this->config['homepagerTorrentEnabled']) {
  3944. $class .= ' faded';
  3945. }
  3946. break;
  3947. case 'homepageOrderplexnowplaying':
  3948. case 'homepageOrderplexrecent':
  3949. case 'homepageOrderplexplaylist':
  3950. $class = 'bg-plex';
  3951. $image = 'plugins/images/tabs/plex.png';
  3952. if (!$this->config['homepagePlexEnabled']) {
  3953. $class .= ' faded';
  3954. }
  3955. break;
  3956. case 'homepageOrderembynowplaying':
  3957. case 'homepageOrderembyrecent':
  3958. $class = 'bg-emby';
  3959. $image = 'plugins/images/tabs/emby.png';
  3960. if (!$this->config['homepageEmbyEnabled']) {
  3961. $class .= ' faded';
  3962. }
  3963. break;
  3964. case 'homepageOrderombi':
  3965. $class = 'bg-inverse';
  3966. $image = 'plugins/images/tabs/ombi.png';
  3967. if (!$this->config['homepageOmbiEnabled']) {
  3968. $class .= ' faded';
  3969. }
  3970. break;
  3971. case 'homepageOrdercalendar':
  3972. $class = 'bg-primary';
  3973. $image = 'plugins/images/tabs/calendar.png';
  3974. if (!$this->config['homepageSonarrEnabled'] && !$this->config['homepageRadarrEnabled'] && !$this->config['homepageSickrageEnabled'] && !$this->config['homepageCouchpotatoEnabled']) {
  3975. $class .= ' faded';
  3976. }
  3977. break;
  3978. case 'homepageOrderdownloader':
  3979. $class = 'bg-inverse';
  3980. $image = 'plugins/images/tabs/downloader.png';
  3981. if (!$this->config['jdownloaderCombine'] && !$this->config['sabnzbdCombine'] && !$this->config['nzbgetCombine'] && !$this->config['rTorrentCombine'] && !$this->config['delugeCombine'] && !$this->config['transmissionCombine'] && !$this->config['qBittorrentCombine']) {
  3982. $class .= ' faded';
  3983. }
  3984. break;
  3985. case 'homepageOrderhealthchecks':
  3986. $class = 'bg-healthchecks';
  3987. $image = 'plugins/images/tabs/healthchecks.png';
  3988. if (!$this->config['homepageHealthChecksEnabled']) {
  3989. $class .= ' faded';
  3990. }
  3991. break;
  3992. case 'homepageOrderunifi':
  3993. $class = 'bg-info';
  3994. $image = 'plugins/images/tabs/ubnt.png';
  3995. if (!$this->config['homepageUnifiEnabled']) {
  3996. $class .= ' faded';
  3997. }
  3998. break;
  3999. case 'homepageOrdertautulli':
  4000. $class = 'bg-info';
  4001. $image = 'plugins/images/tabs/tautulli.png';
  4002. if (!$this->config['homepageTautulliEnabled']) {
  4003. $class .= ' faded';
  4004. }
  4005. break;
  4006. case 'homepageOrderPihole':
  4007. $class = 'bg-info';
  4008. $image = 'plugins/images/tabs/pihole.png';
  4009. if (!$this->config['homepagePiholeEnabled']) {
  4010. $class .= ' faded';
  4011. }
  4012. break;
  4013. case 'homepageOrderMonitorr':
  4014. $class = 'bg-info';
  4015. $image = 'plugins/images/tabs/monitorr.png';
  4016. if (!$this->config['homepageMonitorrEnabled']) {
  4017. $class .= ' faded';
  4018. }
  4019. break;
  4020. case 'homepageOrderWeatherAndAir':
  4021. $class = 'bg-success';
  4022. $image = 'plugins/images/tabs/wind.png';
  4023. if (!$this->config['homepageWeatherAndAirEnabled']) {
  4024. $class .= ' faded';
  4025. }
  4026. break;
  4027. case 'homepageOrderSpeedtest':
  4028. $class = 'bg-success';
  4029. $image = 'plugins/images/tabs/speedtest-icon.png';
  4030. if (!$this->config['homepageSpeedtestEnabled']) {
  4031. $class .= ' faded';
  4032. }
  4033. break;
  4034. case 'homepageOrderNetdata':
  4035. $class = 'bg-success';
  4036. $image = 'plugins/images/tabs/netdata.png';
  4037. if (!$this->config['homepageNetdataEnabled']) {
  4038. $class .= ' faded';
  4039. }
  4040. break;
  4041. case 'homepageOrderOctoprint':
  4042. $class = 'bg-success';
  4043. $image = 'plugins/images/tabs/octoprint.png';
  4044. if (!$this->config['homepageOctoprintEnabled']) {
  4045. $class .= ' faded';
  4046. }
  4047. break;
  4048. case 'homepageOrderSonarrQueue':
  4049. $class = 'bg-sonarr';
  4050. $image = 'plugins/images/tabs/sonarr.png';
  4051. if (!$this->config['homepageSonarrQueueEnabled']) {
  4052. $class .= ' faded';
  4053. }
  4054. break;
  4055. case 'homepageOrderRadarrQueue':
  4056. $class = 'bg-radarr';
  4057. $image = 'plugins/images/tabs/radarr.png';
  4058. if (!$this->config['homepageRadarrQueueEnabled']) {
  4059. $class .= ' faded';
  4060. }
  4061. break;
  4062. default:
  4063. $class = 'blue-bg';
  4064. $image = '';
  4065. break;
  4066. }
  4067. $homepageList .= '
  4068. <div class="col-md-3 col-xs-12 sort-homepage m-t-10 hvr-grow clearfix">
  4069. <div class="homepage-drag fc-event ' . $class . ' lazyload" data-src="' . $image . '">
  4070. <span class="ordinal-position text-uppercase badge bg-org homepage-number" data-link="' . $key . '" style="float:left;width: 30px;">' . $val . '</span>
  4071. <span class="homepage-text">&nbsp; ' . strtoupper(substr($key, 13)) . '</span>
  4072. </div>
  4073. </div>
  4074. ';
  4075. $inputList .= '<input type="hidden" name="' . $key . '">';
  4076. }
  4077. $homepageList .= '</div>';
  4078. $inputList .= '</form>';
  4079. return $homepageList . $inputList;
  4080. }
  4081. public function getSettingsHomepage()
  4082. {
  4083. $groups = $this->groupSelect();
  4084. $ombiTvOptions = array(
  4085. array(
  4086. 'name' => 'All Seasons',
  4087. 'value' => 'all'
  4088. ),
  4089. array(
  4090. 'name' => 'First Season Only',
  4091. 'value' => 'first'
  4092. ),
  4093. array(
  4094. 'name' => 'Last Season Only',
  4095. 'value' => 'last'
  4096. ),
  4097. );
  4098. $mediaServers = array(
  4099. array(
  4100. 'name' => 'N/A',
  4101. 'value' => ''
  4102. ),
  4103. array(
  4104. 'name' => 'Plex',
  4105. 'value' => 'plex'
  4106. ),
  4107. array(
  4108. 'name' => 'Emby [Not Available]',
  4109. 'value' => 'emby'
  4110. )
  4111. );
  4112. $limit = array(
  4113. array(
  4114. 'name' => '1 Item',
  4115. 'value' => '1'
  4116. ),
  4117. array(
  4118. 'name' => '2 Items',
  4119. 'value' => '2'
  4120. ),
  4121. array(
  4122. 'name' => '3 Items',
  4123. 'value' => '3'
  4124. ),
  4125. array(
  4126. 'name' => '4 Items',
  4127. 'value' => '4'
  4128. ),
  4129. array(
  4130. 'name' => '5 Items',
  4131. 'value' => '5'
  4132. ),
  4133. array(
  4134. 'name' => '6 Items',
  4135. 'value' => '6'
  4136. ),
  4137. array(
  4138. 'name' => '7 Items',
  4139. 'value' => '7'
  4140. ),
  4141. array(
  4142. 'name' => '8 Items',
  4143. 'value' => '8'
  4144. ),
  4145. array(
  4146. 'name' => 'Unlimited',
  4147. 'value' => '1000'
  4148. ),
  4149. );
  4150. $day = array(
  4151. array(
  4152. 'name' => 'Sunday',
  4153. 'value' => '0'
  4154. ),
  4155. array(
  4156. 'name' => 'Monday',
  4157. 'value' => '1'
  4158. ),
  4159. array(
  4160. 'name' => 'Tueday',
  4161. 'value' => '2'
  4162. ),
  4163. array(
  4164. 'name' => 'Wednesday',
  4165. 'value' => '3'
  4166. ),
  4167. array(
  4168. 'name' => 'Thursday',
  4169. 'value' => '4'
  4170. ),
  4171. array(
  4172. 'name' => 'Friday',
  4173. 'value' => '5'
  4174. ),
  4175. array(
  4176. 'name' => 'Saturday',
  4177. 'value' => '6'
  4178. )
  4179. );
  4180. $calendarDefault = array(
  4181. array(
  4182. 'name' => 'Month',
  4183. 'value' => 'month'
  4184. ),
  4185. array(
  4186. 'name' => 'Day',
  4187. 'value' => 'basicDay'
  4188. ),
  4189. array(
  4190. 'name' => 'Week',
  4191. 'value' => 'basicWeek'
  4192. ),
  4193. array(
  4194. 'name' => 'List',
  4195. 'value' => 'list'
  4196. )
  4197. );
  4198. $timeFormat = array(
  4199. array(
  4200. 'name' => '6p',
  4201. 'value' => 'h(:mm)t'
  4202. ),
  4203. array(
  4204. 'name' => '6:00p',
  4205. 'value' => 'h:mmt'
  4206. ),
  4207. array(
  4208. 'name' => '6:00',
  4209. 'value' => 'h:mm'
  4210. ),
  4211. array(
  4212. 'name' => '18',
  4213. 'value' => 'H(:mm)'
  4214. ),
  4215. array(
  4216. 'name' => '18:00',
  4217. 'value' => 'H:mm'
  4218. )
  4219. );
  4220. $rTorrentSortOptions = array(
  4221. array(
  4222. 'name' => 'Date Desc',
  4223. 'value' => 'dated'
  4224. ),
  4225. array(
  4226. 'name' => 'Date Asc',
  4227. 'value' => 'datea'
  4228. ),
  4229. array(
  4230. 'name' => 'Hash Desc',
  4231. 'value' => 'hashd'
  4232. ),
  4233. array(
  4234. 'name' => 'Hash Asc',
  4235. 'value' => 'hasha'
  4236. ),
  4237. array(
  4238. 'name' => 'Name Desc',
  4239. 'value' => 'named'
  4240. ),
  4241. array(
  4242. 'name' => 'Name Asc',
  4243. 'value' => 'namea'
  4244. ),
  4245. array(
  4246. 'name' => 'Size Desc',
  4247. 'value' => 'sized'
  4248. ),
  4249. array(
  4250. 'name' => 'Size Asc',
  4251. 'value' => 'sizea'
  4252. ),
  4253. array(
  4254. 'name' => 'Label Desc',
  4255. 'value' => 'labeld'
  4256. ),
  4257. array(
  4258. 'name' => 'Label Asc',
  4259. 'value' => 'labela'
  4260. ),
  4261. array(
  4262. 'name' => 'Status Desc',
  4263. 'value' => 'statusd'
  4264. ),
  4265. array(
  4266. 'name' => 'Status Asc',
  4267. 'value' => 'statusa'
  4268. ),
  4269. );
  4270. $qBittorrentApiOptions = array(
  4271. array(
  4272. 'name' => 'V1',
  4273. 'value' => '1'
  4274. ),
  4275. array(
  4276. 'name' => 'V2',
  4277. 'value' => '2'
  4278. ),
  4279. );
  4280. $qBittorrentSortOptions = array(
  4281. array(
  4282. 'name' => 'Hash',
  4283. 'value' => 'hash'
  4284. ),
  4285. array(
  4286. 'name' => 'Name',
  4287. 'value' => 'name'
  4288. ),
  4289. array(
  4290. 'name' => 'Size',
  4291. 'value' => 'size'
  4292. ),
  4293. array(
  4294. 'name' => 'Progress',
  4295. 'value' => 'progress'
  4296. ),
  4297. array(
  4298. 'name' => 'Download Speed',
  4299. 'value' => 'dlspeed'
  4300. ),
  4301. array(
  4302. 'name' => 'Upload Speed',
  4303. 'value' => 'upspeed'
  4304. ),
  4305. array(
  4306. 'name' => 'Priority',
  4307. 'value' => 'priority'
  4308. ),
  4309. array(
  4310. 'name' => 'Number of Seeds',
  4311. 'value' => 'num_seeds'
  4312. ),
  4313. array(
  4314. 'name' => 'Number of Seeds in Swarm',
  4315. 'value' => 'num_complete'
  4316. ),
  4317. array(
  4318. 'name' => 'Number of Leechers',
  4319. 'value' => 'num_leechs'
  4320. ),
  4321. array(
  4322. 'name' => 'Number of Leechers in Swarm',
  4323. 'value' => 'num_incomplete'
  4324. ),
  4325. array(
  4326. 'name' => 'Ratio',
  4327. 'value' => 'ratio'
  4328. ),
  4329. array(
  4330. 'name' => 'ETA',
  4331. 'value' => 'eta'
  4332. ),
  4333. array(
  4334. 'name' => 'State',
  4335. 'value' => 'state'
  4336. ),
  4337. array(
  4338. 'name' => 'Category',
  4339. 'value' => 'category'
  4340. )
  4341. );
  4342. $xmlStatus = (extension_loaded('xmlrpc')) ? 'Installed' : 'Not Installed';
  4343. return array(
  4344. array(
  4345. 'name' => 'Calendar',
  4346. 'enabled' => strpos('personal', $this->config['license']) !== false,
  4347. 'image' => 'plugins/images/tabs/calendar.png',
  4348. 'category' => 'HOMEPAGE',
  4349. 'settings' => array(
  4350. 'Enable' => array(
  4351. array(
  4352. 'type' => 'switch',
  4353. 'name' => 'homepageCalendarEnabled',
  4354. 'label' => 'Enable iCal',
  4355. 'value' => $this->config['homepageCalendarEnabled']
  4356. ),
  4357. array(
  4358. 'type' => 'select',
  4359. 'name' => 'homepageCalendarAuth',
  4360. 'label' => 'Minimum Authentication',
  4361. 'value' => $this->config['homepageCalendarAuth'],
  4362. 'options' => $groups
  4363. ),
  4364. array(
  4365. 'type' => 'input',
  4366. 'name' => 'calendariCal',
  4367. 'label' => 'iCal URL\'s',
  4368. 'value' => $this->config['calendariCal'],
  4369. 'placeholder' => 'separate by comma\'s'
  4370. ),
  4371. ),
  4372. 'Misc Options' => array(
  4373. array(
  4374. 'type' => 'number',
  4375. 'name' => 'calendarStart',
  4376. 'label' => '# of Days Before',
  4377. 'value' => $this->config['calendarStart'],
  4378. 'placeholder' => ''
  4379. ),
  4380. array(
  4381. 'type' => 'number',
  4382. 'name' => 'calendarEnd',
  4383. 'label' => '# of Days After',
  4384. 'value' => $this->config['calendarEnd'],
  4385. 'placeholder' => ''
  4386. ),
  4387. array(
  4388. 'type' => 'select',
  4389. 'name' => 'calendarFirstDay',
  4390. 'label' => 'Start Day',
  4391. 'value' => $this->config['calendarFirstDay'],
  4392. 'options' => $day
  4393. ),
  4394. array(
  4395. 'type' => 'select',
  4396. 'name' => 'calendarDefault',
  4397. 'label' => 'Default View',
  4398. 'value' => $this->config['calendarDefault'],
  4399. 'options' => $calendarDefault
  4400. ),
  4401. array(
  4402. 'type' => 'select',
  4403. 'name' => 'calendarTimeFormat',
  4404. 'label' => 'Time Format',
  4405. 'value' => $this->config['calendarTimeFormat'],
  4406. 'options' => $timeFormat
  4407. ),
  4408. array(
  4409. 'type' => 'select',
  4410. 'name' => 'calendarLimit',
  4411. 'label' => 'Items Per Day',
  4412. 'value' => $this->config['calendarLimit'],
  4413. 'options' => $limit
  4414. ),
  4415. array(
  4416. 'type' => 'select',
  4417. 'name' => 'calendarRefresh',
  4418. 'label' => 'Refresh Seconds',
  4419. 'value' => $this->config['calendarRefresh'],
  4420. 'options' => $this->optionTime()
  4421. )
  4422. ),
  4423. )
  4424. ),
  4425. array(
  4426. 'name' => 'Plex',
  4427. 'enabled' => strpos('personal', $this->config['license']) !== false,
  4428. 'image' => 'plugins/images/tabs/plex.png',
  4429. 'category' => 'Media Server',
  4430. 'settings' => array(
  4431. 'Enable' => array(
  4432. array(
  4433. 'type' => 'switch',
  4434. 'name' => 'homepagePlexEnabled',
  4435. 'label' => 'Enable',
  4436. 'value' => $this->config['homepagePlexEnabled']
  4437. ),
  4438. array(
  4439. 'type' => 'select',
  4440. 'name' => 'homepagePlexAuth',
  4441. 'label' => 'Minimum Authentication',
  4442. 'value' => $this->config['homepagePlexAuth'],
  4443. 'options' => $groups
  4444. )
  4445. ),
  4446. 'Connection' => array(
  4447. array(
  4448. 'type' => 'input',
  4449. 'name' => 'plexURL',
  4450. 'label' => 'URL',
  4451. 'value' => $this->config['plexURL'],
  4452. 'help' => 'Please make sure to use local IP address and port - You also may use local dns name too.',
  4453. 'placeholder' => 'http(s)://hostname:port'
  4454. ),
  4455. array(
  4456. 'type' => 'password-alt',
  4457. 'name' => 'plexToken',
  4458. 'label' => 'Token',
  4459. 'value' => $this->config['plexToken']
  4460. ),
  4461. array(
  4462. 'type' => 'password-alt',
  4463. 'name' => 'plexID',
  4464. 'label' => 'Plex Machine',
  4465. 'value' => $this->config['plexID']
  4466. )
  4467. ),
  4468. 'Active Streams' => array(
  4469. array(
  4470. 'type' => 'switch',
  4471. 'name' => 'homepagePlexStreams',
  4472. 'label' => 'Enable',
  4473. 'value' => $this->config['homepagePlexStreams']
  4474. ),
  4475. array(
  4476. 'type' => 'select',
  4477. 'name' => 'homepagePlexStreamsAuth',
  4478. 'label' => 'Minimum Authorization',
  4479. 'value' => $this->config['homepagePlexStreamsAuth'],
  4480. 'options' => $groups
  4481. ),
  4482. array(
  4483. 'type' => 'switch',
  4484. 'name' => 'homepageShowStreamNames',
  4485. 'label' => 'User Information',
  4486. 'value' => $this->config['homepageShowStreamNames']
  4487. ),
  4488. array(
  4489. 'type' => 'select',
  4490. 'name' => 'homepageShowStreamNamesAuth',
  4491. 'label' => 'Minimum Authorization',
  4492. 'value' => $this->config['homepageShowStreamNamesAuth'],
  4493. 'options' => $groups
  4494. ),
  4495. array(
  4496. 'type' => 'select',
  4497. 'name' => 'homepageStreamRefresh',
  4498. 'label' => 'Refresh Seconds',
  4499. 'value' => $this->config['homepageStreamRefresh'],
  4500. 'options' => $this->optionTime()
  4501. ),
  4502. ),
  4503. 'Recent Items' => array(
  4504. array(
  4505. 'type' => 'switch',
  4506. 'name' => 'homepagePlexRecent',
  4507. 'label' => 'Enable',
  4508. 'value' => $this->config['homepagePlexRecent']
  4509. ),
  4510. array(
  4511. 'type' => 'select',
  4512. 'name' => 'homepagePlexRecentAuth',
  4513. 'label' => 'Minimum Authorization',
  4514. 'value' => $this->config['homepagePlexRecentAuth'],
  4515. 'options' => $groups
  4516. ),
  4517. array(
  4518. 'type' => 'number',
  4519. 'name' => 'homepageRecentLimit',
  4520. 'label' => 'Item Limit',
  4521. 'value' => $this->config['homepageRecentLimit'],
  4522. ),
  4523. array(
  4524. 'type' => 'select',
  4525. 'name' => 'homepageRecentRefresh',
  4526. 'label' => 'Refresh Seconds',
  4527. 'value' => $this->config['homepageRecentRefresh'],
  4528. 'options' => $this->optionTime()
  4529. ),
  4530. ),
  4531. 'Media Search' => array(
  4532. array(
  4533. 'type' => 'switch',
  4534. 'name' => 'mediaSearch',
  4535. 'label' => 'Enable',
  4536. 'value' => $this->config['mediaSearch']
  4537. ),
  4538. array(
  4539. 'type' => 'select',
  4540. 'name' => 'mediaSearchAuth',
  4541. 'label' => 'Minimum Authorization',
  4542. 'value' => $this->config['mediaSearchAuth'],
  4543. 'options' => $groups
  4544. ),
  4545. array(
  4546. 'type' => 'select',
  4547. 'name' => 'mediaSearchType',
  4548. 'label' => 'Media Server',
  4549. 'value' => $this->config['mediaSearchType'],
  4550. 'options' => $mediaServers
  4551. ),
  4552. ),
  4553. 'Playlists' => array(
  4554. array(
  4555. 'type' => 'switch',
  4556. 'name' => 'homepagePlexPlaylist',
  4557. 'label' => 'Enable',
  4558. 'value' => $this->config['homepagePlexPlaylist']
  4559. ),
  4560. array(
  4561. 'type' => 'select',
  4562. 'name' => 'homepagePlexPlaylistAuth',
  4563. 'label' => 'Minimum Authorization',
  4564. 'value' => $this->config['homepagePlexPlaylistAuth'],
  4565. 'options' => $groups
  4566. ),
  4567. ),
  4568. 'Misc Options' => array(
  4569. array(
  4570. 'type' => 'input',
  4571. 'name' => 'plexTabName',
  4572. 'label' => 'Plex Tab Name',
  4573. 'value' => $this->config['plexTabName'],
  4574. 'placeholder' => 'Only use if you have Plex in a reverse proxy'
  4575. ),
  4576. array(
  4577. 'type' => 'input',
  4578. 'name' => 'plexTabURL',
  4579. 'label' => 'Plex Tab WAN URL',
  4580. 'value' => $this->config['plexTabURL'],
  4581. 'placeholder' => 'http(s)://hostname:port'
  4582. ),
  4583. array(
  4584. 'type' => 'select',
  4585. 'name' => 'cacheImageSize',
  4586. 'label' => 'Image Cache Size',
  4587. 'value' => $this->config['cacheImageSize'],
  4588. 'options' => array(
  4589. array(
  4590. 'name' => 'Low',
  4591. 'value' => '.5'
  4592. ),
  4593. array(
  4594. 'name' => '1x',
  4595. 'value' => '1'
  4596. ),
  4597. array(
  4598. 'name' => '2x',
  4599. 'value' => '2'
  4600. ),
  4601. array(
  4602. 'name' => '3x',
  4603. 'value' => '3'
  4604. )
  4605. )
  4606. )
  4607. ),
  4608. 'Test Connection' => array(
  4609. array(
  4610. 'type' => 'blank',
  4611. 'label' => 'Please Save before Testing'
  4612. ),
  4613. array(
  4614. 'type' => 'button',
  4615. 'label' => '',
  4616. 'icon' => 'fa fa-flask',
  4617. 'class' => 'pull-right',
  4618. 'text' => 'Test Connection',
  4619. 'attr' => 'onclick="testAPIConnection(\'plex\')"'
  4620. ),
  4621. )
  4622. )
  4623. ),
  4624. array(
  4625. 'name' => 'Emby',
  4626. 'enabled' => strpos('personal', $this->config['license']) !== false,
  4627. 'image' => 'plugins/images/tabs/emby.png',
  4628. 'category' => 'Media Server',
  4629. 'settings' => array(
  4630. 'Enable' => array(
  4631. array(
  4632. 'type' => 'switch',
  4633. 'name' => 'homepageEmbyEnabled',
  4634. 'label' => 'Enable',
  4635. 'value' => $this->config['homepageEmbyEnabled']
  4636. ),
  4637. array(
  4638. 'type' => 'select',
  4639. 'name' => 'homepageEmbyAuth',
  4640. 'label' => 'Minimum Authentication',
  4641. 'value' => $this->config['homepageEmbyAuth'],
  4642. 'options' => $groups
  4643. )
  4644. ),
  4645. 'Connection' => array(
  4646. array(
  4647. 'type' => 'input',
  4648. 'name' => 'embyURL',
  4649. 'label' => 'URL',
  4650. 'value' => $this->config['embyURL'],
  4651. 'help' => 'Please make sure to use local IP address and port - You also may use local dns name too.',
  4652. 'placeholder' => 'http(s)://hostname:port'
  4653. ),
  4654. array(
  4655. 'type' => 'password-alt',
  4656. 'name' => 'embyToken',
  4657. 'label' => 'Token',
  4658. 'value' => $this->config['embyToken']
  4659. )
  4660. ),
  4661. 'Active Streams' => array(
  4662. array(
  4663. 'type' => 'switch',
  4664. 'name' => 'homepageEmbyStreams',
  4665. 'label' => 'Enable',
  4666. 'value' => $this->config['homepageEmbyStreams']
  4667. ),
  4668. array(
  4669. 'type' => 'select',
  4670. 'name' => 'homepageEmbyStreamsAuth',
  4671. 'label' => 'Minimum Authorization',
  4672. 'value' => $this->config['homepageEmbyStreamsAuth'],
  4673. 'options' => $groups
  4674. ),
  4675. array(
  4676. 'type' => 'switch',
  4677. 'name' => 'homepageShowStreamNames',
  4678. 'label' => 'User Information',
  4679. 'value' => $this->config['homepageShowStreamNames']
  4680. ),
  4681. array(
  4682. 'type' => 'select',
  4683. 'name' => 'homepageShowStreamNamesAuth',
  4684. 'label' => 'Minimum Authorization',
  4685. 'value' => $this->config['homepageShowStreamNamesAuth'],
  4686. 'options' => $groups
  4687. ),
  4688. array(
  4689. 'type' => 'select',
  4690. 'name' => 'homepageStreamRefresh',
  4691. 'label' => 'Refresh Seconds',
  4692. 'value' => $this->config['homepageStreamRefresh'],
  4693. 'options' => $this->optionTime()
  4694. ),
  4695. ),
  4696. 'Recent Items' => array(
  4697. array(
  4698. 'type' => 'switch',
  4699. 'name' => 'homepageEmbyRecent',
  4700. 'label' => 'Enable',
  4701. 'value' => $this->config['homepageEmbyRecent']
  4702. ),
  4703. array(
  4704. 'type' => 'select',
  4705. 'name' => 'homepageEmbyRecentAuth',
  4706. 'label' => 'Minimum Authorization',
  4707. 'value' => $this->config['homepageEmbyRecentAuth'],
  4708. 'options' => $groups
  4709. ),
  4710. array(
  4711. 'type' => 'number',
  4712. 'name' => 'homepageRecentLimit',
  4713. 'label' => 'Item Limit',
  4714. 'value' => $this->config['homepageRecentLimit'],
  4715. ),
  4716. array(
  4717. 'type' => 'select',
  4718. 'name' => 'homepageRecentRefresh',
  4719. 'label' => 'Refresh Seconds',
  4720. 'value' => $this->config['homepageRecentRefresh'],
  4721. 'options' => $this->optionTime()
  4722. ),
  4723. ),
  4724. 'Misc Options' => array(
  4725. array(
  4726. 'type' => 'input',
  4727. 'name' => 'embyTabName',
  4728. 'label' => 'Emby Tab Name',
  4729. 'value' => $this->config['embyTabName'],
  4730. 'placeholder' => 'Only use if you have Emby in a reverse proxy'
  4731. ),
  4732. array(
  4733. 'type' => 'input',
  4734. 'name' => 'embyTabURL',
  4735. 'label' => 'Emby Tab WAN URL',
  4736. 'value' => $this->config['embyTabURL'],
  4737. 'placeholder' => 'http(s)://hostname:port'
  4738. ),
  4739. array(
  4740. 'type' => 'select',
  4741. 'name' => 'cacheImageSize',
  4742. 'label' => 'Image Cache Size',
  4743. 'value' => $this->config['cacheImageSize'],
  4744. 'options' => array(
  4745. array(
  4746. 'name' => 'Low',
  4747. 'value' => '.5'
  4748. ),
  4749. array(
  4750. 'name' => '1x',
  4751. 'value' => '1'
  4752. ),
  4753. array(
  4754. 'name' => '2x',
  4755. 'value' => '2'
  4756. ),
  4757. array(
  4758. 'name' => '3x',
  4759. 'value' => '3'
  4760. )
  4761. )
  4762. )
  4763. ),
  4764. 'Test Connection' => array(
  4765. array(
  4766. 'type' => 'blank',
  4767. 'label' => 'Please Save before Testing'
  4768. ),
  4769. array(
  4770. 'type' => 'button',
  4771. 'label' => '',
  4772. 'icon' => 'fa fa-flask',
  4773. 'class' => 'pull-right',
  4774. 'text' => 'Test Connection',
  4775. 'attr' => 'onclick="testAPIConnection(\'emby\')"'
  4776. ),
  4777. )
  4778. )
  4779. ),
  4780. array(
  4781. 'name' => 'Jellyfin',
  4782. 'enabled' => strpos('personal', $this->config['license']) !== false,
  4783. 'image' => 'plugins/images/tabs/jellyfin.png',
  4784. 'category' => 'Media Server',
  4785. 'settings' => array(
  4786. 'Enable' => array(
  4787. array(
  4788. 'type' => 'switch',
  4789. 'name' => 'homepageJellyfinEnabled',
  4790. 'label' => 'Enable',
  4791. 'value' => $this->config['homepageJellyfinEnabled']
  4792. ),
  4793. array(
  4794. 'type' => 'select',
  4795. 'name' => 'homepageJellyfinAuth',
  4796. 'label' => 'Minimum Authentication',
  4797. 'value' => $this->config['homepageJellyfinAuth'],
  4798. 'options' => $groups
  4799. )
  4800. ),
  4801. 'Connection' => array(
  4802. array(
  4803. 'type' => 'input',
  4804. 'name' => 'jellyfinURL',
  4805. 'label' => 'URL',
  4806. 'value' => $this->config['jellyfinURL'],
  4807. 'help' => 'Please make sure to use local IP address and port - You also may use local dns name too.',
  4808. 'placeholder' => 'http(s)://hostname:port'
  4809. ),
  4810. array(
  4811. 'type' => 'password-alt',
  4812. 'name' => 'jellyfinToken',
  4813. 'label' => 'Token',
  4814. 'value' => $this->config['jellyfinToken']
  4815. )
  4816. ),
  4817. 'Active Streams' => array(
  4818. array(
  4819. 'type' => 'switch',
  4820. 'name' => 'homepageJellyfinStreams',
  4821. 'label' => 'Enable',
  4822. 'value' => $this->config['homepageJellyfinStreams']
  4823. ),
  4824. array(
  4825. 'type' => 'select',
  4826. 'name' => 'homepageJellyStreamsAuth',
  4827. 'label' => 'Minimum Authorization',
  4828. 'value' => $this->config['homepageJellyStreamsAuth'],
  4829. 'options' => $groups
  4830. ),
  4831. array(
  4832. 'type' => 'switch',
  4833. 'name' => 'homepageShowStreamNames',
  4834. 'label' => 'User Information',
  4835. 'value' => $this->config['homepageShowStreamNames']
  4836. ),
  4837. array(
  4838. 'type' => 'select',
  4839. 'name' => 'homepageShowStreamNamesAuth',
  4840. 'label' => 'Minimum Authorization',
  4841. 'value' => $this->config['homepageShowStreamNamesAuth'],
  4842. 'options' => $groups
  4843. ),
  4844. array(
  4845. 'type' => 'select',
  4846. 'name' => 'homepageStreamRefresh',
  4847. 'label' => 'Refresh Seconds',
  4848. 'value' => $this->config['homepageStreamRefresh'],
  4849. 'options' => $this->optionTime()
  4850. ),
  4851. ),
  4852. 'Recent Items' => array(
  4853. array(
  4854. 'type' => 'switch',
  4855. 'name' => 'homepageJellyfinRecent',
  4856. 'label' => 'Enable',
  4857. 'value' => $this->config['homepageJellyfinRecent']
  4858. ),
  4859. array(
  4860. 'type' => 'select',
  4861. 'name' => 'homepageJellyfinRecentAuth',
  4862. 'label' => 'Minimum Authorization',
  4863. 'value' => $this->config['homepageJellyfinRecentAuth'],
  4864. 'options' => $groups
  4865. ),
  4866. array(
  4867. 'type' => 'number',
  4868. 'name' => 'homepageRecentLimit',
  4869. 'label' => 'Item Limit',
  4870. 'value' => $this->config['homepageRecentLimit'],
  4871. ),
  4872. array(
  4873. 'type' => 'select',
  4874. 'name' => 'homepageRecentRefresh',
  4875. 'label' => 'Refresh Seconds',
  4876. 'value' => $this->config['homepageRecentRefresh'],
  4877. 'options' => $this->optionTime()
  4878. ),
  4879. ),
  4880. 'Misc Options' => array(
  4881. array(
  4882. 'type' => 'input',
  4883. 'name' => 'jellyfinTabName',
  4884. 'label' => 'Jellyfin Tab Name',
  4885. 'value' => $this->config['jellyfinTabName'],
  4886. 'placeholder' => 'Only use if you have Jellyfin in a reverse proxy'
  4887. ),
  4888. array(
  4889. 'type' => 'input',
  4890. 'name' => 'jellyfinTabURL',
  4891. 'label' => 'Jellyfin Tab WAN URL',
  4892. 'value' => $this->config['jellyfinTabURL'],
  4893. 'placeholder' => 'http(s)://hostname:port'
  4894. ),
  4895. array(
  4896. 'type' => 'select',
  4897. 'name' => 'cacheImageSize',
  4898. 'label' => 'Image Cache Size',
  4899. 'value' => $this->config['cacheImageSize'],
  4900. 'options' => array(
  4901. array(
  4902. 'name' => 'Low',
  4903. 'value' => '.5'
  4904. ),
  4905. array(
  4906. 'name' => '1x',
  4907. 'value' => '1'
  4908. ),
  4909. array(
  4910. 'name' => '2x',
  4911. 'value' => '2'
  4912. ),
  4913. array(
  4914. 'name' => '3x',
  4915. 'value' => '3'
  4916. )
  4917. )
  4918. )
  4919. ),
  4920. 'Test Connection' => array(
  4921. array(
  4922. 'type' => 'blank',
  4923. 'label' => 'Please Save before Testing'
  4924. ),
  4925. array(
  4926. 'type' => 'button',
  4927. 'label' => '',
  4928. 'icon' => 'fa fa-flask',
  4929. 'class' => 'pull-right',
  4930. 'text' => 'Test Connection',
  4931. 'attr' => 'onclick="testAPIConnection(\'jellyfin\')"'
  4932. ),
  4933. )
  4934. )
  4935. ),
  4936. array(
  4937. 'name' => 'JDownloader',
  4938. 'enabled' => strpos('personal', $this->config['license']) !== false,
  4939. 'image' => 'plugins/images/tabs/jdownloader.png',
  4940. 'category' => 'Downloader',
  4941. 'settings' => array(
  4942. 'custom' => '
  4943. <div class="row">
  4944. <div class="col-lg-12">
  4945. <div class="panel panel-info">
  4946. <div class="panel-heading">
  4947. <span lang="en">Notice</span>
  4948. </div>
  4949. <div class="panel-wrapper collapse in" aria-expanded="true">
  4950. <div class="panel-body">
  4951. <ul class="list-icons">
  4952. <li><i class="fa fa-chevron-right text-danger"></i> <a href="https://pypi.org/project/myjd-api/" target="_blank">Download [myjd-api] Module</a></li>
  4953. <li><i class="fa fa-chevron-right text-danger"></i> Add <b>/api/myjd</b> to the URL if you are using <a href="https://pypi.org/project/RSScrawler/" target="_blank">RSScrawler</a></li>
  4954. </ul>
  4955. </div>
  4956. </div>
  4957. </div>
  4958. </div>
  4959. </div>
  4960. ',
  4961. 'Enable' => array(
  4962. array(
  4963. 'type' => 'switch',
  4964. 'name' => 'homepageJdownloaderEnabled',
  4965. 'label' => 'Enable',
  4966. 'value' => $this->config['homepageJdownloaderEnabled']
  4967. ),
  4968. array(
  4969. 'type' => 'select',
  4970. 'name' => 'homepageJdownloaderAuth',
  4971. 'label' => 'Minimum Authentication',
  4972. 'value' => $this->config['homepageJdownloaderAuth'],
  4973. 'options' => $groups
  4974. )
  4975. ),
  4976. 'Connection' => array(
  4977. array(
  4978. 'type' => 'input',
  4979. 'name' => 'jdownloaderURL',
  4980. 'label' => 'URL',
  4981. 'value' => $this->config['jdownloaderURL'],
  4982. 'help' => 'Please make sure to use local IP address and port - You also may use local dns name too.',
  4983. 'placeholder' => 'http(s)://hostname:port'
  4984. )
  4985. ),
  4986. 'Misc Options' => array(
  4987. array(
  4988. 'type' => 'select',
  4989. 'name' => 'homepageDownloadRefresh',
  4990. 'label' => 'Refresh Seconds',
  4991. 'value' => $this->config['homepageDownloadRefresh'],
  4992. 'options' => $this->optionTime()
  4993. ),
  4994. array(
  4995. 'type' => 'switch',
  4996. 'name' => 'jdownloaderCombine',
  4997. 'label' => 'Add to Combined Downloader',
  4998. 'value' => $this->config['jdownloaderCombine']
  4999. ),
  5000. ),
  5001. 'Test Connection' => array(
  5002. array(
  5003. 'type' => 'blank',
  5004. 'label' => 'Please Save before Testing'
  5005. ),
  5006. array(
  5007. 'type' => 'button',
  5008. 'label' => '',
  5009. 'icon' => 'fa fa-flask',
  5010. 'class' => 'pull-right',
  5011. 'text' => 'Test Connection',
  5012. 'attr' => 'onclick="testAPIConnection(\'jdownloader\')"'
  5013. ),
  5014. )
  5015. )
  5016. ),
  5017. array(
  5018. 'name' => 'SabNZBD',
  5019. 'enabled' => strpos('personal', $this->config['license']) !== false,
  5020. 'image' => 'plugins/images/tabs/sabnzbd.png',
  5021. 'category' => 'Downloader',
  5022. 'settings' => array(
  5023. 'Enable' => array(
  5024. array(
  5025. 'type' => 'switch',
  5026. 'name' => 'homepageSabnzbdEnabled',
  5027. 'label' => 'Enable',
  5028. 'value' => $this->config['homepageSabnzbdEnabled']
  5029. ),
  5030. array(
  5031. 'type' => 'select',
  5032. 'name' => 'homepageSabnzbdAuth',
  5033. 'label' => 'Minimum Authentication',
  5034. 'value' => $this->config['homepageSabnzbdAuth'],
  5035. 'options' => $groups
  5036. )
  5037. ),
  5038. 'Connection' => array(
  5039. array(
  5040. 'type' => 'input',
  5041. 'name' => 'sabnzbdURL',
  5042. 'label' => 'URL',
  5043. 'value' => $this->config['sabnzbdURL'],
  5044. 'help' => 'Please make sure to use local IP address and port - You also may use local dns name too.',
  5045. 'placeholder' => 'http(s)://hostname:port'
  5046. ),
  5047. array(
  5048. 'type' => 'password-alt',
  5049. 'name' => 'sabnzbdToken',
  5050. 'label' => 'Token',
  5051. 'value' => $this->config['sabnzbdToken']
  5052. )
  5053. ),
  5054. 'Misc Options' => array(
  5055. array(
  5056. 'type' => 'select',
  5057. 'name' => 'homepageDownloadRefresh',
  5058. 'label' => 'Refresh Seconds',
  5059. 'value' => $this->config['homepageDownloadRefresh'],
  5060. 'options' => $this->optionTime()
  5061. ),
  5062. array(
  5063. 'type' => 'switch',
  5064. 'name' => 'sabnzbdCombine',
  5065. 'label' => 'Add to Combined Downloader',
  5066. 'value' => $this->config['sabnzbdCombine']
  5067. ),
  5068. ),
  5069. 'Test Connection' => array(
  5070. array(
  5071. 'type' => 'blank',
  5072. 'label' => 'Please Save before Testing'
  5073. ),
  5074. array(
  5075. 'type' => 'button',
  5076. 'label' => '',
  5077. 'icon' => 'fa fa-flask',
  5078. 'class' => 'pull-right',
  5079. 'text' => 'Test Connection',
  5080. 'attr' => 'onclick="testAPIConnection(\'sabnzbd\')"'
  5081. ),
  5082. )
  5083. )
  5084. ),
  5085. array(
  5086. 'name' => 'NZBGet',
  5087. 'enabled' => strpos('personal', $this->config['license']) !== false,
  5088. 'image' => 'plugins/images/tabs/nzbget.png',
  5089. 'category' => 'Downloader',
  5090. 'settings' => array(
  5091. 'Enable' => array(
  5092. array(
  5093. 'type' => 'switch',
  5094. 'name' => 'homepageNzbgetEnabled',
  5095. 'label' => 'Enable',
  5096. 'value' => $this->config['homepageNzbgetEnabled']
  5097. ),
  5098. array(
  5099. 'type' => 'select',
  5100. 'name' => 'homepageNzbgetAuth',
  5101. 'label' => 'Minimum Authentication',
  5102. 'value' => $this->config['homepageNzbgetAuth'],
  5103. 'options' => $groups
  5104. )
  5105. ),
  5106. 'Connection' => array(
  5107. array(
  5108. 'type' => 'input',
  5109. 'name' => 'nzbgetURL',
  5110. 'label' => 'URL',
  5111. 'value' => $this->config['nzbgetURL'],
  5112. 'help' => 'Please make sure to use local IP address and port - You also may use local dns name too.',
  5113. 'placeholder' => 'http(s)://hostname:port'
  5114. ),
  5115. array(
  5116. 'type' => 'input',
  5117. 'name' => 'nzbgetUsername',
  5118. 'label' => 'Username',
  5119. 'value' => $this->config['nzbgetUsername']
  5120. ),
  5121. array(
  5122. 'type' => 'password',
  5123. 'name' => 'nzbgetPassword',
  5124. 'label' => 'Password',
  5125. 'value' => $this->config['nzbgetPassword']
  5126. )
  5127. ),
  5128. 'Misc Options' => array(
  5129. array(
  5130. 'type' => 'select',
  5131. 'name' => 'homepageDownloadRefresh',
  5132. 'label' => 'Refresh Seconds',
  5133. 'value' => $this->config['homepageDownloadRefresh'],
  5134. 'options' => $this->optionTime()
  5135. ),
  5136. array(
  5137. 'type' => 'switch',
  5138. 'name' => 'nzbgetCombine',
  5139. 'label' => 'Add to Combined Downloader',
  5140. 'value' => $this->config['nzbgetCombine']
  5141. ),
  5142. ),
  5143. 'Test Connection' => array(
  5144. array(
  5145. 'type' => 'blank',
  5146. 'label' => 'Please Save before Testing'
  5147. ),
  5148. array(
  5149. 'type' => 'button',
  5150. 'label' => '',
  5151. 'icon' => 'fa fa-flask',
  5152. 'class' => 'pull-right',
  5153. 'text' => 'Test Connection',
  5154. 'attr' => 'onclick="testAPIConnection(\'nzbget\')"'
  5155. ),
  5156. )
  5157. )
  5158. ),
  5159. array(
  5160. 'name' => 'Transmission',
  5161. 'enabled' => strpos('personal', $this->config['license']) !== false,
  5162. 'image' => 'plugins/images/tabs/transmission.png',
  5163. 'category' => 'Downloader',
  5164. 'settings' => array(
  5165. 'Enable' => array(
  5166. array(
  5167. 'type' => 'switch',
  5168. 'name' => 'homepageTransmissionEnabled',
  5169. 'label' => 'Enable',
  5170. 'value' => $this->config['homepageTransmissionEnabled']
  5171. ),
  5172. array(
  5173. 'type' => 'select',
  5174. 'name' => 'homepageTransmissionAuth',
  5175. 'label' => 'Minimum Authentication',
  5176. 'value' => $this->config['homepageTransmissionAuth'],
  5177. 'options' => $groups
  5178. )
  5179. ),
  5180. 'Connection' => array(
  5181. array(
  5182. 'type' => 'input',
  5183. 'name' => 'transmissionURL',
  5184. 'label' => 'URL',
  5185. 'value' => $this->config['transmissionURL'],
  5186. 'help' => 'Please do not included /web in URL. Please make sure to use local IP address and port - You also may use local dns name too.',
  5187. 'placeholder' => 'http(s)://hostname:port'
  5188. ),
  5189. array(
  5190. 'type' => 'input',
  5191. 'name' => 'transmissionUsername',
  5192. 'label' => 'Username',
  5193. 'value' => $this->config['transmissionUsername']
  5194. ),
  5195. array(
  5196. 'type' => 'password',
  5197. 'name' => 'transmissionPassword',
  5198. 'label' => 'Password',
  5199. 'value' => $this->config['transmissionPassword']
  5200. )
  5201. ),
  5202. 'Misc Options' => array(
  5203. array(
  5204. 'type' => 'switch',
  5205. 'name' => 'transmissionHideSeeding',
  5206. 'label' => 'Hide Seeding',
  5207. 'value' => $this->config['transmissionHideSeeding']
  5208. ), array(
  5209. 'type' => 'switch',
  5210. 'name' => 'transmissionHideCompleted',
  5211. 'label' => 'Hide Completed',
  5212. 'value' => $this->config['transmissionHideCompleted']
  5213. ),
  5214. array(
  5215. 'type' => 'select',
  5216. 'name' => 'homepageDownloadRefresh',
  5217. 'label' => 'Refresh Seconds',
  5218. 'value' => $this->config['homepageDownloadRefresh'],
  5219. 'options' => $this->optionTime()
  5220. ),
  5221. array(
  5222. 'type' => 'switch',
  5223. 'name' => 'transmissionCombine',
  5224. 'label' => 'Add to Combined Downloader',
  5225. 'value' => $this->config['transmissionCombine']
  5226. ),
  5227. ),
  5228. 'Test Connection' => array(
  5229. array(
  5230. 'type' => 'blank',
  5231. 'label' => 'Please Save before Testing'
  5232. ),
  5233. array(
  5234. 'type' => 'button',
  5235. 'label' => '',
  5236. 'icon' => 'fa fa-flask',
  5237. 'class' => 'pull-right',
  5238. 'text' => 'Test Connection',
  5239. 'attr' => 'onclick="testAPIConnection(\'transmission\')"'
  5240. ),
  5241. )
  5242. )
  5243. ),
  5244. array(
  5245. 'name' => 'qBittorrent',
  5246. 'enabled' => strpos('personal', $this->config['license']) !== false,
  5247. 'image' => 'plugins/images/tabs/qBittorrent.png',
  5248. 'category' => 'Downloader',
  5249. 'settings' => array(
  5250. 'Enable' => array(
  5251. array(
  5252. 'type' => 'switch',
  5253. 'name' => 'homepageqBittorrentEnabled',
  5254. 'label' => 'Enable',
  5255. 'value' => $this->config['homepageqBittorrentEnabled']
  5256. ),
  5257. array(
  5258. 'type' => 'select',
  5259. 'name' => 'homepageqBittorrentAuth',
  5260. 'label' => 'Minimum Authentication',
  5261. 'value' => $this->config['homepageqBittorrentAuth'],
  5262. 'options' => $groups
  5263. )
  5264. ),
  5265. 'Connection' => array(
  5266. array(
  5267. 'type' => 'input',
  5268. 'name' => 'qBittorrentURL',
  5269. 'label' => 'URL',
  5270. 'value' => $this->config['qBittorrentURL'],
  5271. 'help' => 'Please make sure to use local IP address and port - You also may use local dns name too.',
  5272. 'placeholder' => 'http(s)://hostname:port'
  5273. ),
  5274. array(
  5275. 'type' => 'select',
  5276. 'name' => 'qBittorrentApiVersion',
  5277. 'label' => 'API Version',
  5278. 'value' => $this->config['qBittorrentApiVersion'],
  5279. 'options' => $qBittorrentApiOptions
  5280. ),
  5281. array(
  5282. 'type' => 'input',
  5283. 'name' => 'qBittorrentUsername',
  5284. 'label' => 'Username',
  5285. 'value' => $this->config['qBittorrentUsername']
  5286. ),
  5287. array(
  5288. 'type' => 'password',
  5289. 'name' => 'qBittorrentPassword',
  5290. 'label' => 'Password',
  5291. 'value' => $this->config['qBittorrentPassword']
  5292. )
  5293. ),
  5294. 'Misc Options' => array(
  5295. array(
  5296. 'type' => 'switch',
  5297. 'name' => 'qBittorrentHideSeeding',
  5298. 'label' => 'Hide Seeding',
  5299. 'value' => $this->config['qBittorrentHideSeeding']
  5300. ),
  5301. array(
  5302. 'type' => 'switch',
  5303. 'name' => 'qBittorrentHideCompleted',
  5304. 'label' => 'Hide Completed',
  5305. 'value' => $this->config['qBittorrentHideCompleted']
  5306. ),
  5307. array(
  5308. 'type' => 'select',
  5309. 'name' => 'qBittorrentSortOrder',
  5310. 'label' => 'Order',
  5311. 'value' => $this->config['qBittorrentSortOrder'],
  5312. 'options' => $qBittorrentSortOptions
  5313. ), array(
  5314. 'type' => 'switch',
  5315. 'name' => 'qBittorrentReverseSorting',
  5316. 'label' => 'Reverse Sorting',
  5317. 'value' => $this->config['qBittorrentReverseSorting']
  5318. ),
  5319. array(
  5320. 'type' => 'select',
  5321. 'name' => 'homepageDownloadRefresh',
  5322. 'label' => 'Refresh Seconds',
  5323. 'value' => $this->config['homepageDownloadRefresh'],
  5324. 'options' => $this->optionTime()
  5325. ),
  5326. array(
  5327. 'type' => 'switch',
  5328. 'name' => 'qBittorrentCombine',
  5329. 'label' => 'Add to Combined Downloader',
  5330. 'value' => $this->config['qBittorrentCombine']
  5331. ),
  5332. ),
  5333. 'Test Connection' => array(
  5334. array(
  5335. 'type' => 'blank',
  5336. 'label' => 'Please Save before Testing'
  5337. ),
  5338. array(
  5339. 'type' => 'button',
  5340. 'label' => '',
  5341. 'icon' => 'fa fa-flask',
  5342. 'class' => 'pull-right',
  5343. 'text' => 'Test Connection',
  5344. 'attr' => 'onclick="testAPIConnection(\'qbittorrent\')"'
  5345. ),
  5346. )
  5347. )
  5348. ),
  5349. array(
  5350. 'name' => 'rTorrent',
  5351. 'enabled' => strpos('personal', $this->config['license']) !== false,
  5352. 'image' => 'plugins/images/tabs/rTorrent.png',
  5353. 'category' => 'Downloader',
  5354. 'settings' => array(
  5355. 'FYI' => array(
  5356. array(
  5357. 'type' => 'html',
  5358. 'label' => '',
  5359. 'override' => 12,
  5360. 'html' => '
  5361. <div class="row">
  5362. <div class="col-lg-12">
  5363. <div class="panel panel-info">
  5364. <div class="panel-heading">
  5365. <span lang="en">ATTENTION</span>
  5366. </div>
  5367. <div class="panel-wrapper collapse in" aria-expanded="true">
  5368. <div class="panel-body">
  5369. <h4 lang="en">This module requires XMLRPC</h4>
  5370. <span lang="en">Status: [ <b>' . $xmlStatus . '</b> ]</span>
  5371. <br/></br>
  5372. <span lang="en">
  5373. <h4><b>Note about API URL</b></h4>
  5374. Organizr appends the url with <code>/RPC2</code> unless the URL ends in <code>.php</code><br/>
  5375. <h5>Possible URLs:</h5>
  5376. <li>http://localhost:8080</li>
  5377. <li>https://domain.site/xmlrpc.php</li>
  5378. <li>https://seedbox.site/rutorrent/plugins/httprpc/action.php</li>
  5379. </span>
  5380. </div>
  5381. </div>
  5382. </div>
  5383. </div>
  5384. </div>
  5385. '
  5386. )
  5387. ),
  5388. 'Enable' => array(
  5389. array(
  5390. 'type' => 'switch',
  5391. 'name' => 'homepagerTorrentEnabled',
  5392. 'label' => 'Enable',
  5393. 'value' => $this->config['homepagerTorrentEnabled']
  5394. ),
  5395. array(
  5396. 'type' => 'select',
  5397. 'name' => 'homepagerTorrentAuth',
  5398. 'label' => 'Minimum Authentication',
  5399. 'value' => $this->config['homepagerTorrentAuth'],
  5400. 'options' => $groups
  5401. )
  5402. ),
  5403. 'Connection' => array(
  5404. array(
  5405. 'type' => 'input',
  5406. 'name' => 'rTorrentURL',
  5407. 'label' => 'URL',
  5408. 'value' => $this->config['rTorrentURL'],
  5409. 'help' => 'Please make sure to use local IP address and port - You also may use local dns name too.',
  5410. 'placeholder' => 'http(s)://hostname:port'
  5411. ),
  5412. array(
  5413. 'type' => 'input',
  5414. 'name' => 'rTorrentURLOverride',
  5415. 'label' => 'rTorrent API URL Override',
  5416. 'value' => $this->config['rTorrentURLOverride'],
  5417. 'help' => 'Only use if you cannot connect. Please make sure to use local IP address and port - You also may use local dns name too.',
  5418. 'placeholder' => 'http(s)://hostname:port/xmlrpc'
  5419. ),
  5420. array(
  5421. 'type' => 'input',
  5422. 'name' => 'rTorrentUsername',
  5423. 'label' => 'Username',
  5424. 'value' => $this->config['rTorrentUsername']
  5425. ),
  5426. array(
  5427. 'type' => 'password',
  5428. 'name' => 'rTorrentPassword',
  5429. 'label' => 'Password',
  5430. 'value' => $this->config['rTorrentPassword']
  5431. ),
  5432. array(
  5433. 'type' => 'switch',
  5434. 'name' => 'rTorrentDisableCertCheck',
  5435. 'label' => 'Disable Certificate Check',
  5436. 'value' => $this->config['rTorrentDisableCertCheck']
  5437. ),
  5438. ),
  5439. 'Misc Options' => array(
  5440. array(
  5441. 'type' => 'switch',
  5442. 'name' => 'rTorrentHideSeeding',
  5443. 'label' => 'Hide Seeding',
  5444. 'value' => $this->config['rTorrentHideSeeding']
  5445. ), array(
  5446. 'type' => 'switch',
  5447. 'name' => 'rTorrentHideCompleted',
  5448. 'label' => 'Hide Completed',
  5449. 'value' => $this->config['rTorrentHideCompleted']
  5450. ),
  5451. array(
  5452. 'type' => 'select',
  5453. 'name' => 'rTorrentSortOrder',
  5454. 'label' => 'Order',
  5455. 'value' => $this->config['rTorrentSortOrder'],
  5456. 'options' => $rTorrentSortOptions
  5457. ),
  5458. array(
  5459. 'type' => 'select',
  5460. 'name' => 'homepageDownloadRefresh',
  5461. 'label' => 'Refresh Seconds',
  5462. 'value' => $this->config['homepageDownloadRefresh'],
  5463. 'options' => $this->optionTime()
  5464. ),
  5465. array(
  5466. 'type' => 'number',
  5467. 'name' => 'rTorrentLimit',
  5468. 'label' => 'Item Limit',
  5469. 'value' => $this->config['rTorrentLimit'],
  5470. ),
  5471. array(
  5472. 'type' => 'switch',
  5473. 'name' => 'rTorrentCombine',
  5474. 'label' => 'Add to Combined Downloader',
  5475. 'value' => $this->config['rTorrentCombine']
  5476. ),
  5477. ),
  5478. 'Test Connection' => array(
  5479. array(
  5480. 'type' => 'blank',
  5481. 'label' => 'Please Save before Testing'
  5482. ),
  5483. array(
  5484. 'type' => 'button',
  5485. 'label' => '',
  5486. 'icon' => 'fa fa-flask',
  5487. 'class' => 'pull-right',
  5488. 'text' => 'Test Connection',
  5489. 'attr' => 'onclick="testAPIConnection(\'rtorrent\')"'
  5490. ),
  5491. )
  5492. )
  5493. ),
  5494. array(
  5495. 'name' => 'Deluge',
  5496. 'enabled' => strpos('personal', $this->config['license']) !== false,
  5497. 'image' => 'plugins/images/tabs/deluge.png',
  5498. 'category' => 'Downloader',
  5499. 'settings' => array(
  5500. 'custom' => '
  5501. <div class="row">
  5502. <div class="col-lg-12">
  5503. <div class="panel panel-info">
  5504. <div class="panel-heading">
  5505. <span lang="en">Notice</span>
  5506. </div>
  5507. <div class="panel-wrapper collapse in" aria-expanded="true">
  5508. <div class="panel-body">
  5509. <ul class="list-icons">
  5510. <li><i class="fa fa-chevron-right text-danger"></i> <a href="https://github.com/idlesign/deluge-webapi/tree/master/dist" target="_blank">Download Plugin</a></li>
  5511. <li><i class="fa fa-chevron-right text-danger"></i> Open Deluge Web UI, go to "Preferences -> Plugins -> Install plugin" and choose egg file.</li>
  5512. <li><i class="fa fa-chevron-right text-danger"></i> Activate WebAPI plugin </li>
  5513. </ul>
  5514. </div>
  5515. </div>
  5516. </div>
  5517. </div>
  5518. </div>
  5519. ',
  5520. 'Enable' => array(
  5521. array(
  5522. 'type' => 'switch',
  5523. 'name' => 'homepageDelugeEnabled',
  5524. 'label' => 'Enable',
  5525. 'value' => $this->config['homepageDelugeEnabled']
  5526. ),
  5527. array(
  5528. 'type' => 'select',
  5529. 'name' => 'homepageDelugeAuth',
  5530. 'label' => 'Minimum Authentication',
  5531. 'value' => $this->config['homepageDelugeAuth'],
  5532. 'options' => $groups
  5533. )
  5534. ),
  5535. 'Connection' => array(
  5536. array(
  5537. 'type' => 'input',
  5538. 'name' => 'delugeURL',
  5539. 'label' => 'URL',
  5540. 'value' => $this->config['delugeURL'],
  5541. 'help' => 'Please make sure to use local IP address and port - You also may use local dns name too.',
  5542. 'placeholder' => 'http(s)://hostname:port'
  5543. ),
  5544. array(
  5545. 'type' => 'password',
  5546. 'name' => 'delugePassword',
  5547. 'label' => 'Password',
  5548. 'value' => $this->config['delugePassword']
  5549. )
  5550. ),
  5551. 'Misc Options' => array(
  5552. array(
  5553. 'type' => 'switch',
  5554. 'name' => 'delugeHideSeeding',
  5555. 'label' => 'Hide Seeding',
  5556. 'value' => $this->config['delugeHideSeeding']
  5557. ), array(
  5558. 'type' => 'switch',
  5559. 'name' => 'delugeHideCompleted',
  5560. 'label' => 'Hide Completed',
  5561. 'value' => $this->config['delugeHideCompleted']
  5562. ),
  5563. array(
  5564. 'type' => 'select',
  5565. 'name' => 'homepageDownloadRefresh',
  5566. 'label' => 'Refresh Seconds',
  5567. 'value' => $this->config['homepageDownloadRefresh'],
  5568. 'options' => $this->optionTime()
  5569. ),
  5570. array(
  5571. 'type' => 'switch',
  5572. 'name' => 'delugeCombine',
  5573. 'label' => 'Add to Combined Downloader',
  5574. 'value' => $this->config['delugeCombine']
  5575. ),
  5576. ),
  5577. 'Test Connection' => array(
  5578. array(
  5579. 'type' => 'blank',
  5580. 'label' => 'Please Save before Testing'
  5581. ),
  5582. array(
  5583. 'type' => 'button',
  5584. 'label' => '',
  5585. 'icon' => 'fa fa-flask',
  5586. 'class' => 'pull-right',
  5587. 'text' => 'Test Connection',
  5588. 'attr' => 'onclick="testAPIConnection(\'deluge\')"'
  5589. ),
  5590. )
  5591. )
  5592. ),
  5593. array(
  5594. 'name' => 'Sonarr',
  5595. 'enabled' => strpos('personal', $this->config['license']) !== false,
  5596. 'image' => 'plugins/images/tabs/sonarr.png',
  5597. 'category' => 'PVR',
  5598. 'settings' => array(
  5599. 'Enable' => array(
  5600. array(
  5601. 'type' => 'switch',
  5602. 'name' => 'homepageSonarrEnabled',
  5603. 'label' => 'Enable',
  5604. 'value' => $this->config['homepageSonarrEnabled']
  5605. ),
  5606. array(
  5607. 'type' => 'select',
  5608. 'name' => 'homepageSonarrAuth',
  5609. 'label' => 'Minimum Authentication',
  5610. 'value' => $this->config['homepageSonarrAuth'],
  5611. 'options' => $groups
  5612. )
  5613. ),
  5614. 'Connection' => array(
  5615. array(
  5616. 'type' => 'input',
  5617. 'name' => 'sonarrURL',
  5618. 'label' => 'URL',
  5619. 'value' => $this->config['sonarrURL'],
  5620. 'help' => 'Please make sure to use local IP address and port - You also may use local dns name too.',
  5621. 'placeholder' => 'http(s)://hostname:port'
  5622. ),
  5623. array(
  5624. 'type' => 'password-alt',
  5625. 'name' => 'sonarrToken',
  5626. 'label' => 'Token',
  5627. 'value' => $this->config['sonarrToken']
  5628. )
  5629. ),
  5630. 'API SOCKS' => array(
  5631. array(
  5632. 'type' => 'html',
  5633. 'override' => 12,
  5634. 'label' => '',
  5635. 'html' => '
  5636. <div class="panel panel-default">
  5637. <div class="panel-wrapper collapse in">
  5638. <div class="panel-body">
  5639. <h3 lang="en">Sonarr SOCKS API Connection</h3>
  5640. <p>Using this feature allows you to access the Sonarr API without having to reverse proxy it. Just access it from: </p>
  5641. <code>' . $this->getServerPath() . 'api/v2/socks/sonarr/</code>
  5642. </div>
  5643. </div>
  5644. </div>'
  5645. ),
  5646. array(
  5647. 'type' => 'switch',
  5648. 'name' => 'sonarrSocksEnabled',
  5649. 'label' => 'Enable',
  5650. 'value' => $this->config['sonarrSocksEnabled']
  5651. ),
  5652. array(
  5653. 'type' => 'select',
  5654. 'name' => 'sonarrSocksAuth',
  5655. 'label' => 'Minimum Authentication',
  5656. 'value' => $this->config['sonarrSocksAuth'],
  5657. 'options' => $groups
  5658. ),
  5659. ),
  5660. 'Queue' => array(
  5661. array(
  5662. 'type' => 'switch',
  5663. 'name' => 'homepageSonarrQueueEnabled',
  5664. 'label' => 'Enable',
  5665. 'value' => $this->config['homepageSonarrQueueEnabled']
  5666. ),
  5667. array(
  5668. 'type' => 'select',
  5669. 'name' => 'homepageSonarrQueueAuth',
  5670. 'label' => 'Minimum Authentication',
  5671. 'value' => $this->config['homepageSonarrQueueAuth'],
  5672. 'options' => $groups
  5673. ),
  5674. array(
  5675. 'type' => 'switch',
  5676. 'name' => 'homepageSonarrQueueCombine',
  5677. 'label' => 'Add to Combined Downloader',
  5678. 'value' => $this->config['homepageSonarrQueueCombine']
  5679. ),
  5680. array(
  5681. 'type' => 'select',
  5682. 'name' => 'homepageSonarrQueueRefresh',
  5683. 'label' => 'Refresh Seconds',
  5684. 'value' => $this->config['homepageSonarrQueueRefresh'],
  5685. 'options' => $this->optionTime()
  5686. ),
  5687. ),
  5688. 'Calendar' => array(
  5689. array(
  5690. 'type' => 'number',
  5691. 'name' => 'calendarStart',
  5692. 'label' => '# of Days Before',
  5693. 'value' => $this->config['calendarStart'],
  5694. 'placeholder' => ''
  5695. ),
  5696. array(
  5697. 'type' => 'number',
  5698. 'name' => 'calendarEnd',
  5699. 'label' => '# of Days After',
  5700. 'value' => $this->config['calendarEnd'],
  5701. 'placeholder' => ''
  5702. ),
  5703. array(
  5704. 'type' => 'select',
  5705. 'name' => 'calendarFirstDay',
  5706. 'label' => 'Start Day',
  5707. 'value' => $this->config['calendarFirstDay'],
  5708. 'options' => $day
  5709. ),
  5710. array(
  5711. 'type' => 'select',
  5712. 'name' => 'calendarDefault',
  5713. 'label' => 'Default View',
  5714. 'value' => $this->config['calendarDefault'],
  5715. 'options' => $calendarDefault
  5716. ),
  5717. array(
  5718. 'type' => 'select',
  5719. 'name' => 'calendarTimeFormat',
  5720. 'label' => 'Time Format',
  5721. 'value' => $this->config['calendarTimeFormat'],
  5722. 'options' => $timeFormat
  5723. ),
  5724. array(
  5725. 'type' => 'select',
  5726. 'name' => 'calendarLimit',
  5727. 'label' => 'Items Per Day',
  5728. 'value' => $this->config['calendarLimit'],
  5729. 'options' => $limit
  5730. ),
  5731. array(
  5732. 'type' => 'select',
  5733. 'name' => 'calendarRefresh',
  5734. 'label' => 'Refresh Seconds',
  5735. 'value' => $this->config['calendarRefresh'],
  5736. 'options' => $this->optionTime()
  5737. ),
  5738. array(
  5739. 'type' => 'switch',
  5740. 'name' => 'sonarrUnmonitored',
  5741. 'label' => 'Show Unmonitored',
  5742. 'value' => $this->config['sonarrUnmonitored']
  5743. )
  5744. ),
  5745. 'Test Connection' => array(
  5746. array(
  5747. 'type' => 'blank',
  5748. 'label' => 'Please Save before Testing'
  5749. ),
  5750. array(
  5751. 'type' => 'button',
  5752. 'label' => '',
  5753. 'icon' => 'fa fa-flask',
  5754. 'class' => 'pull-right',
  5755. 'text' => 'Test Connection',
  5756. 'attr' => 'onclick="testAPIConnection(\'sonarr\')"'
  5757. ),
  5758. )
  5759. )
  5760. ),
  5761. array(
  5762. 'name' => 'Lidarr',
  5763. 'enabled' => strpos('personal', $this->config['license']) !== false,
  5764. 'image' => 'plugins/images/tabs/lidarr.png',
  5765. 'category' => 'PMR',
  5766. 'settings' => array(
  5767. 'Enable' => array(
  5768. array(
  5769. 'type' => 'switch',
  5770. 'name' => 'homepageLidarrEnabled',
  5771. 'label' => 'Enable',
  5772. 'value' => $this->config['homepageLidarrEnabled']
  5773. ),
  5774. array(
  5775. 'type' => 'select',
  5776. 'name' => 'homepageLidarrAuth',
  5777. 'label' => 'Minimum Authentication',
  5778. 'value' => $this->config['homepageLidarrAuth'],
  5779. 'options' => $groups
  5780. )
  5781. ),
  5782. 'Connection' => array(
  5783. array(
  5784. 'type' => 'input',
  5785. 'name' => 'lidarrURL',
  5786. 'label' => 'URL',
  5787. 'value' => $this->config['lidarrURL'],
  5788. 'help' => 'Please make sure to use local IP address and port - You also may use local dns name too.',
  5789. 'placeholder' => 'http(s)://hostname:port'
  5790. ),
  5791. array(
  5792. 'type' => 'password-alt',
  5793. 'name' => 'lidarrToken',
  5794. 'label' => 'Token',
  5795. 'value' => $this->config['lidarrToken']
  5796. )
  5797. ),
  5798. 'API SOCKS' => array(
  5799. array(
  5800. 'type' => 'html',
  5801. 'override' => 12,
  5802. 'label' => '',
  5803. 'html' => '
  5804. <div class="panel panel-default">
  5805. <div class="panel-wrapper collapse in">
  5806. <div class="panel-body">
  5807. <h3 lang="en">Lidarr SOCKS API Connection</h3>
  5808. <p>Using this feature allows you to access the Lidarr API without having to reverse proxy it. Just access it from: </p>
  5809. <code>' . $this->getServerPath() . 'api/v2/socks/lidarr/</code>
  5810. </div>
  5811. </div>
  5812. </div>'
  5813. ),
  5814. array(
  5815. 'type' => 'switch',
  5816. 'name' => 'lidarrSocksEnabled',
  5817. 'label' => 'Enable',
  5818. 'value' => $this->config['lidarrSocksEnabled']
  5819. ),
  5820. array(
  5821. 'type' => 'select',
  5822. 'name' => 'lidarrSocksAuth',
  5823. 'label' => 'Minimum Authentication',
  5824. 'value' => $this->config['lidarrSocksAuth'],
  5825. 'options' => $groups
  5826. ),
  5827. ),
  5828. 'Misc Options' => array(
  5829. array(
  5830. 'type' => 'number',
  5831. 'name' => 'calendarStart',
  5832. 'label' => '# of Days Before',
  5833. 'value' => $this->config['calendarStart'],
  5834. 'placeholder' => ''
  5835. ),
  5836. array(
  5837. 'type' => 'number',
  5838. 'name' => 'calendarEnd',
  5839. 'label' => '# of Days After',
  5840. 'value' => $this->config['calendarEnd'],
  5841. 'placeholder' => ''
  5842. ),
  5843. array(
  5844. 'type' => 'select',
  5845. 'name' => 'calendarFirstDay',
  5846. 'label' => 'Start Day',
  5847. 'value' => $this->config['calendarFirstDay'],
  5848. 'options' => $day
  5849. ),
  5850. array(
  5851. 'type' => 'select',
  5852. 'name' => 'calendarDefault',
  5853. 'label' => 'Default View',
  5854. 'value' => $this->config['calendarDefault'],
  5855. 'options' => $calendarDefault
  5856. ),
  5857. array(
  5858. 'type' => 'select',
  5859. 'name' => 'calendarTimeFormat',
  5860. 'label' => 'Time Format',
  5861. 'value' => $this->config['calendarTimeFormat'],
  5862. 'options' => $timeFormat
  5863. ),
  5864. array(
  5865. 'type' => 'select',
  5866. 'name' => 'calendarLimit',
  5867. 'label' => 'Items Per Day',
  5868. 'value' => $this->config['calendarLimit'],
  5869. 'options' => $limit
  5870. ),
  5871. array(
  5872. 'type' => 'select',
  5873. 'name' => 'calendarRefresh',
  5874. 'label' => 'Refresh Seconds',
  5875. 'value' => $this->config['calendarRefresh'],
  5876. 'options' => $this->optionTime()
  5877. ),
  5878. ),
  5879. 'Test Connection' => array(
  5880. array(
  5881. 'type' => 'blank',
  5882. 'label' => 'Please Save before Testing'
  5883. ),
  5884. array(
  5885. 'type' => 'button',
  5886. 'label' => '',
  5887. 'icon' => 'fa fa-flask',
  5888. 'class' => 'pull-right',
  5889. 'text' => 'Test Connection',
  5890. 'attr' => 'onclick="testAPIConnection(\'lidarr\')"'
  5891. ),
  5892. )
  5893. )
  5894. ),
  5895. array(
  5896. 'name' => 'Radarr',
  5897. 'enabled' => strpos('personal', $this->config['license']) !== false,
  5898. 'image' => 'plugins/images/tabs/radarr.png',
  5899. 'category' => 'PVR',
  5900. 'settings' => array(
  5901. 'Enable' => array(
  5902. array(
  5903. 'type' => 'switch',
  5904. 'name' => 'homepageRadarrEnabled',
  5905. 'label' => 'Enable',
  5906. 'value' => $this->config['homepageRadarrEnabled']
  5907. ),
  5908. array(
  5909. 'type' => 'select',
  5910. 'name' => 'homepageRadarrAuth',
  5911. 'label' => 'Minimum Authentication',
  5912. 'value' => $this->config['homepageRadarrAuth'],
  5913. 'options' => $groups
  5914. )
  5915. ),
  5916. 'Connection' => array(
  5917. array(
  5918. 'type' => 'input',
  5919. 'name' => 'radarrURL',
  5920. 'label' => 'URL',
  5921. 'value' => $this->config['radarrURL'],
  5922. 'help' => 'Please make sure to use local IP address and port - You also may use local dns name too.',
  5923. 'placeholder' => 'http(s)://hostname:port'
  5924. ),
  5925. array(
  5926. 'type' => 'password-alt',
  5927. 'name' => 'radarrToken',
  5928. 'label' => 'Token',
  5929. 'value' => $this->config['radarrToken']
  5930. )
  5931. ),
  5932. 'API SOCKS' => array(
  5933. array(
  5934. 'type' => 'html',
  5935. 'override' => 12,
  5936. 'label' => '',
  5937. 'html' => '
  5938. <div class="panel panel-default">
  5939. <div class="panel-wrapper collapse in">
  5940. <div class="panel-body">
  5941. <h3 lang="en">Radarr SOCKS API Connection</h3>
  5942. <p>Using this feature allows you to access the Radarr API without having to reverse proxy it. Just access it from: </p>
  5943. <code>' . $this->getServerPath() . 'api/v2/socks/radarr/</code>
  5944. </div>
  5945. </div>
  5946. </div>'
  5947. ),
  5948. array(
  5949. 'type' => 'switch',
  5950. 'name' => 'radarrSocksEnabled',
  5951. 'label' => 'Enable',
  5952. 'value' => $this->config['radarrSocksEnabled']
  5953. ),
  5954. array(
  5955. 'type' => 'select',
  5956. 'name' => 'radarrSocksAuth',
  5957. 'label' => 'Minimum Authentication',
  5958. 'value' => $this->config['radarrSocksAuth'],
  5959. 'options' => $groups
  5960. ),
  5961. ),
  5962. 'Queue' => array(
  5963. array(
  5964. 'type' => 'switch',
  5965. 'name' => 'homepageRadarrQueueEnabled',
  5966. 'label' => 'Enable',
  5967. 'value' => $this->config['homepageRadarrQueueEnabled']
  5968. ),
  5969. array(
  5970. 'type' => 'select',
  5971. 'name' => 'homepageRadarrQueueAuth',
  5972. 'label' => 'Minimum Authentication',
  5973. 'value' => $this->config['homepageRadarrQueueAuth'],
  5974. 'options' => $groups
  5975. ),
  5976. array(
  5977. 'type' => 'switch',
  5978. 'name' => 'homepageRadarrQueueCombine',
  5979. 'label' => 'Add to Combined Downloader',
  5980. 'value' => $this->config['homepageRadarrQueueCombine']
  5981. ),
  5982. array(
  5983. 'type' => 'select',
  5984. 'name' => 'homepageRadarrQueueRefresh',
  5985. 'label' => 'Refresh Seconds',
  5986. 'value' => $this->config['homepageRadarrQueueRefresh'],
  5987. 'options' => $this->optionTime()
  5988. ),
  5989. ),
  5990. 'Calendar' => array(
  5991. array(
  5992. 'type' => 'number',
  5993. 'name' => 'calendarStart',
  5994. 'label' => '# of Days Before',
  5995. 'value' => $this->config['calendarStart'],
  5996. 'placeholder' => ''
  5997. ),
  5998. array(
  5999. 'type' => 'number',
  6000. 'name' => 'calendarEnd',
  6001. 'label' => '# of Days After',
  6002. 'value' => $this->config['calendarEnd'],
  6003. 'placeholder' => ''
  6004. ),
  6005. array(
  6006. 'type' => 'select',
  6007. 'name' => 'calendarFirstDay',
  6008. 'label' => 'Start Day',
  6009. 'value' => $this->config['calendarFirstDay'],
  6010. 'options' => $day
  6011. ),
  6012. array(
  6013. 'type' => 'select',
  6014. 'name' => 'calendarDefault',
  6015. 'label' => 'Default View',
  6016. 'value' => $this->config['calendarDefault'],
  6017. 'options' => $calendarDefault
  6018. ),
  6019. array(
  6020. 'type' => 'select',
  6021. 'name' => 'calendarTimeFormat',
  6022. 'label' => 'Time Format',
  6023. 'value' => $this->config['calendarTimeFormat'],
  6024. 'options' => $timeFormat
  6025. ),
  6026. array(
  6027. 'type' => 'select',
  6028. 'name' => 'calendarLimit',
  6029. 'label' => 'Items Per Day',
  6030. 'value' => $this->config['calendarLimit'],
  6031. 'options' => $limit
  6032. ),
  6033. array(
  6034. 'type' => 'select',
  6035. 'name' => 'calendarRefresh',
  6036. 'label' => 'Refresh Seconds',
  6037. 'value' => $this->config['calendarRefresh'],
  6038. 'options' => $this->optionTime()
  6039. )
  6040. ),
  6041. 'Test Connection' => array(
  6042. array(
  6043. 'type' => 'blank',
  6044. 'label' => 'Please Save before Testing'
  6045. ),
  6046. array(
  6047. 'type' => 'button',
  6048. 'label' => '',
  6049. 'icon' => 'fa fa-flask',
  6050. 'class' => 'pull-right',
  6051. 'text' => 'Test Connection',
  6052. 'attr' => 'onclick="testAPIConnection(\'radarr\')"'
  6053. ),
  6054. )
  6055. )
  6056. ),
  6057. array(
  6058. 'name' => 'CouchPotato',
  6059. 'enabled' => strpos('personal', $this->config['license']) !== false,
  6060. 'image' => 'plugins/images/tabs/couchpotato.png',
  6061. 'category' => 'PVR',
  6062. 'settings' => array(
  6063. 'Enable' => array(
  6064. array(
  6065. 'type' => 'switch',
  6066. 'name' => 'homepageCouchpotatoEnabled',
  6067. 'label' => 'Enable',
  6068. 'value' => $this->config['homepageCouchpotatoEnabled']
  6069. ),
  6070. array(
  6071. 'type' => 'select',
  6072. 'name' => 'homepageCouchpotatoAuth',
  6073. 'label' => 'Minimum Authentication',
  6074. 'value' => $this->config['homepageCouchpotatoAuth'],
  6075. 'options' => $groups
  6076. )
  6077. ),
  6078. 'Connection' => array(
  6079. array(
  6080. 'type' => 'input',
  6081. 'name' => 'couchpotatoURL',
  6082. 'label' => 'URL',
  6083. 'value' => $this->config['couchpotatoURL'],
  6084. 'help' => 'Please make sure to use local IP address and port - You also may use local dns name too.',
  6085. 'placeholder' => 'http(s)://hostname:port'
  6086. ),
  6087. array(
  6088. 'type' => 'password-alt',
  6089. 'name' => 'couchpotatoToken',
  6090. 'label' => 'Token',
  6091. 'value' => $this->config['couchpotatoToken']
  6092. )
  6093. ),
  6094. 'Misc Options' => array(
  6095. array(
  6096. 'type' => 'select',
  6097. 'name' => 'calendarFirstDay',
  6098. 'label' => 'Start Day',
  6099. 'value' => $this->config['calendarFirstDay'],
  6100. 'options' => $day
  6101. ),
  6102. array(
  6103. 'type' => 'select',
  6104. 'name' => 'calendarDefault',
  6105. 'label' => 'Default View',
  6106. 'value' => $this->config['calendarDefault'],
  6107. 'options' => $calendarDefault
  6108. ),
  6109. array(
  6110. 'type' => 'select',
  6111. 'name' => 'calendarTimeFormat',
  6112. 'label' => 'Time Format',
  6113. 'value' => $this->config['calendarTimeFormat'],
  6114. 'options' => $timeFormat
  6115. ),
  6116. array(
  6117. 'type' => 'select',
  6118. 'name' => 'calendarLimit',
  6119. 'label' => 'Items Per Day',
  6120. 'value' => $this->config['calendarLimit'],
  6121. 'options' => $limit
  6122. ),
  6123. array(
  6124. 'type' => 'select',
  6125. 'name' => 'calendarRefresh',
  6126. 'label' => 'Refresh Seconds',
  6127. 'value' => $this->config['calendarRefresh'],
  6128. 'options' => $this->optionTime()
  6129. )
  6130. )
  6131. )
  6132. ),
  6133. array(
  6134. 'name' => 'SickRage',
  6135. 'enabled' => strpos('personal', $this->config['license']) !== false,
  6136. 'image' => 'plugins/images/tabs/sickrage.png',
  6137. 'category' => 'PVR',
  6138. 'settings' => array(
  6139. 'Enable' => array(
  6140. array(
  6141. 'type' => 'switch',
  6142. 'name' => 'homepageSickrageEnabled',
  6143. 'label' => 'Enable',
  6144. 'value' => $this->config['homepageSickrageEnabled']
  6145. ),
  6146. array(
  6147. 'type' => 'select',
  6148. 'name' => 'homepageSickrageAuth',
  6149. 'label' => 'Minimum Authentication',
  6150. 'value' => $this->config['homepageSickrageAuth'],
  6151. 'options' => $groups
  6152. )
  6153. ),
  6154. 'Connection' => array(
  6155. array(
  6156. 'type' => 'input',
  6157. 'name' => 'sickrageURL',
  6158. 'label' => 'URL',
  6159. 'value' => $this->config['sickrageURL'],
  6160. 'help' => 'Please make sure to use local IP address and port - You also may use local dns name too.',
  6161. 'placeholder' => 'http(s)://hostname:port'
  6162. ),
  6163. array(
  6164. 'type' => 'password-alt',
  6165. 'name' => 'sickrageToken',
  6166. 'label' => 'Token',
  6167. 'value' => $this->config['sickrageToken']
  6168. )
  6169. ),
  6170. 'Misc Options' => array(
  6171. array(
  6172. 'type' => 'select',
  6173. 'name' => 'calendarFirstDay',
  6174. 'label' => 'Start Day',
  6175. 'value' => $this->config['calendarFirstDay'],
  6176. 'options' => $day
  6177. ),
  6178. array(
  6179. 'type' => 'select',
  6180. 'name' => 'calendarDefault',
  6181. 'label' => 'Default View',
  6182. 'value' => $this->config['calendarDefault'],
  6183. 'options' => $calendarDefault
  6184. ),
  6185. array(
  6186. 'type' => 'select',
  6187. 'name' => 'calendarTimeFormat',
  6188. 'label' => 'Time Format',
  6189. 'value' => $this->config['calendarTimeFormat'],
  6190. 'options' => $timeFormat
  6191. ),
  6192. array(
  6193. 'type' => 'select',
  6194. 'name' => 'calendarLimit',
  6195. 'label' => 'Items Per Day',
  6196. 'value' => $this->config['calendarLimit'],
  6197. 'options' => $limit
  6198. ),
  6199. array(
  6200. 'type' => 'select',
  6201. 'name' => 'calendarRefresh',
  6202. 'label' => 'Refresh Seconds',
  6203. 'value' => $this->config['calendarRefresh'],
  6204. 'options' => $this->optionTime()
  6205. )
  6206. ),
  6207. 'Test Connection' => array(
  6208. array(
  6209. 'type' => 'blank',
  6210. 'label' => 'Please Save before Testing'
  6211. ),
  6212. array(
  6213. 'type' => 'button',
  6214. 'label' => '',
  6215. 'icon' => 'fa fa-flask',
  6216. 'class' => 'pull-right',
  6217. 'text' => 'Test Connection',
  6218. 'attr' => 'onclick="testAPIConnection(\'sickrage\')"'
  6219. ),
  6220. )
  6221. )
  6222. ),
  6223. array(
  6224. 'name' => 'Ombi',
  6225. 'enabled' => strpos('personal', $this->config['license']) !== false,
  6226. 'image' => 'plugins/images/tabs/ombi.png',
  6227. 'category' => 'Requests',
  6228. 'settings' => array(
  6229. 'Enable' => array(
  6230. array(
  6231. 'type' => 'switch',
  6232. 'name' => 'homepageOmbiEnabled',
  6233. 'label' => 'Enable',
  6234. 'value' => $this->config['homepageOmbiEnabled']
  6235. ),
  6236. array(
  6237. 'type' => 'select',
  6238. 'name' => 'homepageOmbiAuth',
  6239. 'label' => 'Minimum Authentication',
  6240. 'value' => $this->config['homepageOmbiAuth'],
  6241. 'options' => $groups
  6242. )
  6243. ),
  6244. 'Connection' => array(
  6245. array(
  6246. 'type' => 'input',
  6247. 'name' => 'ombiURL',
  6248. 'label' => 'URL',
  6249. 'value' => $this->config['ombiURL'],
  6250. 'help' => 'Please make sure to use local IP address and port - You also may use local dns name too.',
  6251. 'placeholder' => 'http(s)://hostname:port'
  6252. ),
  6253. array(
  6254. 'type' => 'password-alt',
  6255. 'name' => 'ombiToken',
  6256. 'label' => 'Token',
  6257. 'value' => $this->config['ombiToken']
  6258. )
  6259. ),
  6260. 'Misc Options' => array(
  6261. array(
  6262. 'type' => 'select',
  6263. 'name' => 'homepageOmbiRequestAuth',
  6264. 'label' => 'Minimum Group to Request',
  6265. 'value' => $this->config['homepageOmbiRequestAuth'],
  6266. 'options' => $groups
  6267. ),
  6268. array(
  6269. 'type' => 'select',
  6270. 'name' => 'ombiTvDefault',
  6271. 'label' => 'TV Show Default Request',
  6272. 'value' => $this->config['ombiTvDefault'],
  6273. 'options' => $ombiTvOptions
  6274. ),
  6275. array(
  6276. 'type' => 'switch',
  6277. 'name' => 'ombiLimitUser',
  6278. 'label' => 'Limit to User',
  6279. 'value' => $this->config['ombiLimitUser']
  6280. ),
  6281. array(
  6282. 'type' => 'number',
  6283. 'name' => 'ombiLimit',
  6284. 'label' => 'Item Limit',
  6285. 'value' => $this->config['ombiLimit'],
  6286. ),
  6287. array(
  6288. 'type' => 'select',
  6289. 'name' => 'ombiRefresh',
  6290. 'label' => 'Refresh Seconds',
  6291. 'value' => $this->config['ombiRefresh'],
  6292. 'options' => $this->optionTime()
  6293. ),
  6294. array(
  6295. 'type' => 'switch',
  6296. 'name' => 'ombiAlias',
  6297. 'label' => 'Use Ombi Alias Names',
  6298. 'value' => $this->config['ombiAlias'],
  6299. 'help' => 'Use Ombi Alias Names instead of Usernames - If Alias is blank, Alias will fallback to Username'
  6300. )
  6301. ),
  6302. 'Default Filter' => array(
  6303. array(
  6304. 'type' => 'switch',
  6305. 'name' => 'ombiDefaultFilterAvailable',
  6306. 'label' => 'Show Available',
  6307. 'value' => $this->config['ombiDefaultFilterAvailable'],
  6308. 'help' => 'Show All Available Ombi Requests'
  6309. ),
  6310. array(
  6311. 'type' => 'switch',
  6312. 'name' => 'ombiDefaultFilterUnavailable',
  6313. 'label' => 'Show Unavailable',
  6314. 'value' => $this->config['ombiDefaultFilterUnavailable'],
  6315. 'help' => 'Show All Unavailable Ombi Requests'
  6316. ),
  6317. array(
  6318. 'type' => 'switch',
  6319. 'name' => 'ombiDefaultFilterApproved',
  6320. 'label' => 'Show Approved',
  6321. 'value' => $this->config['ombiDefaultFilterApproved'],
  6322. 'help' => 'Show All Approved Ombi Requests'
  6323. ),
  6324. array(
  6325. 'type' => 'switch',
  6326. 'name' => 'ombiDefaultFilterUnapproved',
  6327. 'label' => 'Show Unapproved',
  6328. 'value' => $this->config['ombiDefaultFilterUnapproved'],
  6329. 'help' => 'Show All Unapproved Ombi Requests'
  6330. ),
  6331. array(
  6332. 'type' => 'switch',
  6333. 'name' => 'ombiDefaultFilterDenied',
  6334. 'label' => 'Show Denied',
  6335. 'value' => $this->config['ombiDefaultFilterDenied'],
  6336. 'help' => 'Show All Denied Ombi Requests'
  6337. )
  6338. ),
  6339. 'Test Connection' => array(
  6340. array(
  6341. 'type' => 'blank',
  6342. 'label' => 'Please Save before Testing'
  6343. ),
  6344. array(
  6345. 'type' => 'button',
  6346. 'label' => '',
  6347. 'icon' => 'fa fa-flask',
  6348. 'class' => 'pull-right',
  6349. 'text' => 'Test Connection',
  6350. 'attr' => 'onclick="testAPIConnection(\'ombi\')"'
  6351. ),
  6352. )
  6353. )
  6354. ),
  6355. array(
  6356. 'name' => 'Unifi',
  6357. 'enabled' => true,
  6358. 'image' => 'plugins/images/tabs/ubnt.png',
  6359. 'category' => 'Monitor',
  6360. 'settings' => array(
  6361. 'Enable' => array(
  6362. array(
  6363. 'type' => 'switch',
  6364. 'name' => 'homepageUnifiEnabled',
  6365. 'label' => 'Enable',
  6366. 'value' => $this->config['homepageUnifiEnabled']
  6367. ),
  6368. array(
  6369. 'type' => 'select',
  6370. 'name' => 'homepageUnifiAuth',
  6371. 'label' => 'Minimum Authentication',
  6372. 'value' => $this->config['homepageUnifiAuth'],
  6373. 'options' => $groups
  6374. )
  6375. ),
  6376. 'Connection' => array(
  6377. array(
  6378. 'type' => 'input',
  6379. 'name' => 'unifiURL',
  6380. 'label' => 'URL',
  6381. 'value' => $this->config['unifiURL'],
  6382. 'help' => 'URL for Unifi',
  6383. 'placeholder' => 'Unifi API URL'
  6384. ),
  6385. array(
  6386. 'type' => 'blank',
  6387. 'label' => ''
  6388. ),
  6389. array(
  6390. 'type' => 'input',
  6391. 'name' => 'unifiUsername',
  6392. 'label' => 'Username',
  6393. 'value' => $this->config['unifiUsername'],
  6394. 'help' => 'Username is case-sensitive',
  6395. ),
  6396. array(
  6397. 'type' => 'password',
  6398. 'name' => 'unifiPassword',
  6399. 'label' => 'Password',
  6400. 'value' => $this->config['unifiPassword']
  6401. ),
  6402. array(
  6403. 'type' => 'input',
  6404. 'name' => 'unifiSiteName',
  6405. 'label' => 'Site Name (Not for UnifiOS)',
  6406. 'value' => $this->config['unifiSiteName'],
  6407. 'help' => 'Site Name - not Site ID nor Site Description',
  6408. ),
  6409. array(
  6410. 'type' => 'button',
  6411. 'label' => 'Grab Unifi Site (Not for UnifiOS)',
  6412. 'icon' => 'fa fa-building',
  6413. 'text' => 'Get Unifi Site',
  6414. 'attr' => 'onclick="getUnifiSite()"'
  6415. ),
  6416. ),
  6417. 'Misc Options' => array(
  6418. array(
  6419. 'type' => 'select',
  6420. 'name' => 'homepageUnifiRefresh',
  6421. 'label' => 'Refresh Seconds',
  6422. 'value' => $this->config['homepageUnifiRefresh'],
  6423. 'options' => $this->optionTime()
  6424. ),
  6425. ),
  6426. 'Test Connection' => array(
  6427. array(
  6428. 'type' => 'blank',
  6429. 'label' => 'Please Save before Testing'
  6430. ),
  6431. array(
  6432. 'type' => 'button',
  6433. 'label' => '',
  6434. 'icon' => 'fa fa-flask',
  6435. 'class' => 'pull-right',
  6436. 'text' => 'Test Connection',
  6437. 'attr' => 'onclick="testAPIConnection(\'unifi\')"'
  6438. ),
  6439. )
  6440. )
  6441. ),
  6442. array(
  6443. 'name' => 'HealthChecks',
  6444. 'enabled' => true,
  6445. 'image' => 'plugins/images/tabs/healthchecks.png',
  6446. 'category' => 'Monitor',
  6447. 'settings' => array(
  6448. 'Enable' => array(
  6449. array(
  6450. 'type' => 'switch',
  6451. 'name' => 'homepageHealthChecksEnabled',
  6452. 'label' => 'Enable',
  6453. 'value' => $this->config['homepageHealthChecksEnabled']
  6454. ),
  6455. array(
  6456. 'type' => 'select',
  6457. 'name' => 'homepageHealthChecksAuth',
  6458. 'label' => 'Minimum Authentication',
  6459. 'value' => $this->config['homepageHealthChecksAuth'],
  6460. 'options' => $groups
  6461. )
  6462. ),
  6463. 'Connection' => array(
  6464. array(
  6465. 'type' => 'input',
  6466. 'name' => 'healthChecksURL',
  6467. 'label' => 'URL',
  6468. 'value' => $this->config['healthChecksURL'],
  6469. 'help' => 'URL for HealthChecks API',
  6470. 'placeholder' => 'HealthChecks API URL'
  6471. ),
  6472. array(
  6473. 'type' => 'password-alt',
  6474. 'name' => 'healthChecksToken',
  6475. 'label' => 'Token',
  6476. 'value' => $this->config['healthChecksToken']
  6477. )
  6478. ),
  6479. 'Misc Options' => array(
  6480. array(
  6481. 'type' => 'input',
  6482. 'name' => 'healthChecksTags',
  6483. 'label' => 'Tags',
  6484. 'value' => $this->config['healthChecksTags'],
  6485. 'help' => 'Pull only checks with this tag - Blank for all',
  6486. 'placeholder' => 'Multiple tags using CSV - tag1,tag2'
  6487. ),
  6488. array(
  6489. 'type' => 'select',
  6490. 'name' => 'homepageHealthChecksRefresh',
  6491. 'label' => 'Refresh Seconds',
  6492. 'value' => $this->config['homepageHealthChecksRefresh'],
  6493. 'options' => $this->optionTime()
  6494. ),
  6495. ),
  6496. )
  6497. ),
  6498. array(
  6499. 'name' => 'CustomHTML-1',
  6500. 'enabled' => strpos('personal,business', $this->config['license']) !== false,
  6501. 'image' => 'plugins/images/tabs/custom1.png',
  6502. 'category' => 'Custom',
  6503. 'settings' => array(
  6504. 'Enable' => array(
  6505. array(
  6506. 'type' => 'switch',
  6507. 'name' => 'homepageCustomHTMLoneEnabled',
  6508. 'label' => 'Enable',
  6509. 'value' => $this->config['homepageCustomHTMLoneEnabled']
  6510. ),
  6511. array(
  6512. 'type' => 'select',
  6513. 'name' => 'homepageCustomHTMLoneAuth',
  6514. 'label' => 'Minimum Authentication',
  6515. 'value' => $this->config['homepageCustomHTMLoneAuth'],
  6516. 'options' => $groups
  6517. )
  6518. ),
  6519. 'Code' => array(
  6520. array(
  6521. 'type' => 'textbox',
  6522. 'name' => 'customHTMLone',
  6523. 'class' => 'hidden customHTMLoneTextarea',
  6524. 'label' => '',
  6525. 'value' => $this->config['customHTMLone'],
  6526. ),
  6527. array(
  6528. 'type' => 'html',
  6529. 'override' => 12,
  6530. 'label' => 'Custom HTML/JavaScript',
  6531. 'html' => '<button type="button" class="hidden savecustomHTMLoneTextarea btn btn-info btn-circle pull-right m-r-5 m-l-10"><i class="fa fa-save"></i> </button><div id="customHTMLoneEditor" style="height:300px">' . htmlentities($this->config['customHTMLone']) . '</div>'
  6532. ),
  6533. )
  6534. )
  6535. ),
  6536. array(
  6537. 'name' => 'CustomHTML-2',
  6538. 'enabled' => strpos('personal,business', $this->config['license']) !== false,
  6539. 'image' => 'plugins/images/tabs/custom2.png',
  6540. 'category' => 'Custom',
  6541. 'settings' => array(
  6542. 'Enable' => array(
  6543. array(
  6544. 'type' => 'switch',
  6545. 'name' => 'homepageCustomHTMLtwoEnabled',
  6546. 'label' => 'Enable',
  6547. 'value' => $this->config['homepageCustomHTMLtwoEnabled']
  6548. ),
  6549. array(
  6550. 'type' => 'select',
  6551. 'name' => 'homepageCustomHTMLtwoAuth',
  6552. 'label' => 'Minimum Authentication',
  6553. 'value' => $this->config['homepageCustomHTMLtwoAuth'],
  6554. 'options' => $groups
  6555. )
  6556. ),
  6557. 'Code' => array(
  6558. array(
  6559. 'type' => 'textbox',
  6560. 'name' => 'customHTMLtwo',
  6561. 'class' => 'hidden customHTMLtwoTextarea',
  6562. 'label' => '',
  6563. 'value' => $this->config['customHTMLtwo'],
  6564. ),
  6565. array(
  6566. 'type' => 'html',
  6567. 'override' => 12,
  6568. 'label' => 'Custom HTML/JavaScript',
  6569. 'html' => '<button type="button" class="hidden savecustomHTMLtwoTextarea btn btn-info btn-circle pull-right m-r-5 m-l-10"><i class="fa fa-save"></i> </button><div id="customHTMLtwoEditor" style="height:300px">' . htmlentities($this->config['customHTMLtwo']) . '</div>'
  6570. ),
  6571. )
  6572. )
  6573. ),
  6574. array(
  6575. 'name' => 'Misc',
  6576. 'enabled' => true,
  6577. 'image' => 'plugins/images/organizr/logo-no-border.png',
  6578. 'category' => 'Custom',
  6579. 'settings' => array(
  6580. 'YouTube' => array(
  6581. array(
  6582. 'type' => 'input',
  6583. 'name' => 'youtubeAPI',
  6584. 'label' => 'Youtube API Key',
  6585. 'value' => $this->config['youtubeAPI'],
  6586. 'help' => 'Please make sure to input this API key as the organizr one gets limited'
  6587. ),
  6588. array(
  6589. 'type' => 'html',
  6590. 'override' => 6,
  6591. 'label' => 'Instructions',
  6592. 'html' => '<a href="https://www.slickremix.com/docs/get-api-key-for-youtube/" target="_blank">Click here for instructions</a>'
  6593. ),
  6594. )
  6595. )
  6596. ),
  6597. array(
  6598. 'name' => 'Pi-hole',
  6599. 'enabled' => true,
  6600. 'image' => 'plugins/images/tabs/pihole.png',
  6601. 'category' => 'Monitor',
  6602. 'settings' => array(
  6603. 'Enable' => array(
  6604. array(
  6605. 'type' => 'switch',
  6606. 'name' => 'homepagePiholeEnabled',
  6607. 'label' => 'Enable',
  6608. 'value' => $this->config['homepagePiholeEnabled']
  6609. ),
  6610. array(
  6611. 'type' => 'select',
  6612. 'name' => 'homepagePiholeAuth',
  6613. 'label' => 'Minimum Authentication',
  6614. 'value' => $this->config['homepagePiholeAuth'],
  6615. 'options' => $groups
  6616. )
  6617. ),
  6618. 'Connection' => array(
  6619. array(
  6620. 'type' => 'input',
  6621. 'name' => 'piholeURL',
  6622. 'label' => 'URL',
  6623. 'value' => $this->config['piholeURL'],
  6624. 'help' => 'Please make sure to use local IP address and port and to include \'/admin/\' at the end of the URL. You can add multiple Pi-holes by comma separating the URLs.',
  6625. 'placeholder' => 'http(s)://hostname:port/admin/'
  6626. ),
  6627. ),
  6628. 'Misc' => array(
  6629. array(
  6630. 'type' => 'switch',
  6631. 'name' => 'piholeHeaderToggle',
  6632. 'label' => 'Toggle Title',
  6633. 'value' => $this->config['piholeHeaderToggle'],
  6634. 'help' => 'Shows/hides the title of this homepage module'
  6635. ),
  6636. array(
  6637. 'type' => 'switch',
  6638. 'name' => 'homepagePiholeCombine',
  6639. 'label' => 'Combine stat cards',
  6640. 'value' => $this->config['homepagePiholeCombine'],
  6641. 'help' => 'This controls whether to combine the stats for multiple pihole instances into 1 card.',
  6642. ),
  6643. ),
  6644. 'Test Connection' => array(
  6645. array(
  6646. 'type' => 'blank',
  6647. 'label' => 'Please Save before Testing'
  6648. ),
  6649. array(
  6650. 'type' => 'button',
  6651. 'label' => '',
  6652. 'icon' => 'fa fa-flask',
  6653. 'class' => 'pull-right',
  6654. 'text' => 'Test Connection',
  6655. 'attr' => 'onclick="testAPIConnection(\'pihole\')"'
  6656. ),
  6657. )
  6658. )
  6659. ),
  6660. array(
  6661. 'name' => 'Tautulli',
  6662. 'enabled' => strpos('personal', $this->config['license']) !== false,
  6663. 'image' => 'plugins/images/tabs/tautulli.png',
  6664. 'category' => 'Monitor',
  6665. 'settings' => array(
  6666. 'Enable' => array(
  6667. array(
  6668. 'type' => 'switch',
  6669. 'name' => 'homepageTautulliEnabled',
  6670. 'label' => 'Enable',
  6671. 'value' => $this->config['homepageTautulliEnabled']
  6672. ),
  6673. array(
  6674. 'type' => 'select',
  6675. 'name' => 'homepageTautulliAuth',
  6676. 'label' => 'Minimum Authentication',
  6677. 'value' => $this->config['homepageTautulliAuth'],
  6678. 'options' => $groups
  6679. )
  6680. ),
  6681. 'Options' => array(
  6682. array(
  6683. 'type' => 'input',
  6684. 'name' => 'tautulliHeader',
  6685. 'label' => 'Title',
  6686. 'value' => $this->config['tautulliHeader'],
  6687. 'help' => 'Sets the title of this homepage module'
  6688. ),
  6689. array(
  6690. 'type' => 'switch',
  6691. 'name' => 'tautulliHeaderToggle',
  6692. 'label' => 'Toggle Title',
  6693. 'value' => $this->config['tautulliHeaderToggle'],
  6694. 'help' => 'Shows/hides the title of this homepage module'
  6695. )
  6696. ),
  6697. 'Connection' => array(
  6698. array(
  6699. 'type' => 'input',
  6700. 'name' => 'tautulliURL',
  6701. 'label' => 'URL',
  6702. 'value' => $this->config['tautulliURL'],
  6703. 'help' => 'URL for Tautulli API, include the IP, the port and the base URL (e.g. /tautulli/) in the URL',
  6704. 'placeholder' => 'http://<ip>:<port>'
  6705. ),
  6706. array(
  6707. 'type' => 'password-alt',
  6708. 'name' => 'tautulliApikey',
  6709. 'label' => 'API Key',
  6710. 'value' => $this->config['tautulliApikey']
  6711. ),
  6712. array(
  6713. 'type' => 'select',
  6714. 'name' => 'homepageTautulliRefresh',
  6715. 'label' => 'Refresh Seconds',
  6716. 'value' => $this->config['homepageTautulliRefresh'],
  6717. 'options' => $this->optionTime()
  6718. ),
  6719. ),
  6720. 'Library Stats' => array(
  6721. array(
  6722. 'type' => 'switch',
  6723. 'name' => 'tautulliLibraries',
  6724. 'label' => 'Libraries',
  6725. 'value' => $this->config['tautulliLibraries'],
  6726. 'help' => 'Shows/hides the card with library information.',
  6727. ),
  6728. array(
  6729. 'type' => 'select',
  6730. 'name' => 'homepageTautulliLibraryAuth',
  6731. 'label' => 'Minimum Authentication',
  6732. 'value' => $this->config['homepageTautulliLibraryAuth'],
  6733. 'options' => $groups
  6734. ),
  6735. ),
  6736. 'Viewing Stats' => array(
  6737. array(
  6738. 'type' => 'switch',
  6739. 'name' => 'tautulliPopularMovies',
  6740. 'label' => 'Popular Movies',
  6741. 'value' => $this->config['tautulliPopularMovies'],
  6742. 'help' => 'Shows/hides the card with Popular Movies information.',
  6743. ),
  6744. array(
  6745. 'type' => 'switch',
  6746. 'name' => 'tautulliPopularTV',
  6747. 'label' => 'Popular TV',
  6748. 'value' => $this->config['tautulliPopularTV'],
  6749. 'help' => 'Shows/hides the card with Popular TV information.',
  6750. ),
  6751. array(
  6752. 'type' => 'switch',
  6753. 'name' => 'tautulliTopMovies',
  6754. 'label' => 'Top Movies',
  6755. 'value' => $this->config['tautulliTopMovies'],
  6756. 'help' => 'Shows/hides the card with Top Movies information.',
  6757. ),
  6758. array(
  6759. 'type' => 'switch',
  6760. 'name' => 'tautulliTopTV',
  6761. 'label' => 'Top TV',
  6762. 'value' => $this->config['tautulliTopTV'],
  6763. 'help' => 'Shows/hides the card with Top TV information.',
  6764. ),
  6765. array(
  6766. 'type' => 'select',
  6767. 'name' => 'homepageTautulliViewsAuth',
  6768. 'label' => 'Minimum Authentication',
  6769. 'value' => $this->config['homepageTautulliViewsAuth'],
  6770. 'options' => $groups
  6771. ),
  6772. ),
  6773. 'Misc Stats' => array(
  6774. array(
  6775. 'type' => 'switch',
  6776. 'name' => 'tautulliTopUsers',
  6777. 'label' => 'Top Users',
  6778. 'value' => $this->config['tautulliTopUsers'],
  6779. 'help' => 'Shows/hides the card with Top Users information.',
  6780. ),
  6781. array(
  6782. 'type' => 'switch',
  6783. 'name' => 'tautulliTopPlatforms',
  6784. 'label' => 'Top Platforms',
  6785. 'value' => $this->config['tautulliTopPlatforms'],
  6786. 'help' => 'Shows/hides the card with Top Platforms information.',
  6787. ),
  6788. array(
  6789. 'type' => 'select',
  6790. 'name' => 'homepageTautulliMiscAuth',
  6791. 'label' => 'Minimum Authentication',
  6792. 'value' => $this->config['homepageTautulliMiscAuth'],
  6793. 'options' => $groups
  6794. ),
  6795. ),
  6796. 'Test Connection' => array(
  6797. array(
  6798. 'type' => 'blank',
  6799. 'label' => 'Please Save before Testing'
  6800. ),
  6801. array(
  6802. 'type' => 'button',
  6803. 'label' => '',
  6804. 'icon' => 'fa fa-flask',
  6805. 'class' => 'pull-right',
  6806. 'text' => 'Test Connection',
  6807. 'attr' => 'onclick="testAPIConnection(\'tautulli\')"'
  6808. ),
  6809. )
  6810. )
  6811. ),
  6812. array(
  6813. 'name' => 'Monitorr',
  6814. 'enabled' => true,
  6815. 'image' => 'plugins/images/tabs/monitorr.png',
  6816. 'category' => 'Monitor',
  6817. 'settings' => array(
  6818. 'Enable' => array(
  6819. array(
  6820. 'type' => 'switch',
  6821. 'name' => 'homepageMonitorrEnabled',
  6822. 'label' => 'Enable',
  6823. 'value' => $this->config['homepageMonitorrEnabled']
  6824. ),
  6825. array(
  6826. 'type' => 'select',
  6827. 'name' => 'homepageMonitorrAuth',
  6828. 'label' => 'Minimum Authentication',
  6829. 'value' => $this->config['homepageMonitorrAuth'],
  6830. 'options' => $groups
  6831. )
  6832. ),
  6833. 'Connection' => array(
  6834. array(
  6835. 'type' => 'input',
  6836. 'name' => 'monitorrURL',
  6837. 'label' => 'URL',
  6838. 'value' => $this->config['monitorrURL'],
  6839. 'help' => 'URL for Monitorr. Please use the revers proxy URL i.e. https://domain.com/monitorr/.',
  6840. 'placeholder' => 'http://domain.com/monitorr/'
  6841. ),
  6842. array(
  6843. 'type' => 'select',
  6844. 'name' => 'homepageMonitorrRefresh',
  6845. 'label' => 'Refresh Seconds',
  6846. 'value' => $this->config['homepageMonitorrRefresh'],
  6847. 'options' => $this->optionTime()
  6848. ),
  6849. ),
  6850. 'Options' => array(
  6851. array(
  6852. 'type' => 'input',
  6853. 'name' => 'monitorrHeader',
  6854. 'label' => 'Title',
  6855. 'value' => $this->config['monitorrHeader'],
  6856. 'help' => 'Sets the title of this homepage module',
  6857. ),
  6858. array(
  6859. 'type' => 'switch',
  6860. 'name' => 'monitorrHeaderToggle',
  6861. 'label' => 'Toggle Title',
  6862. 'value' => $this->config['monitorrHeaderToggle'],
  6863. 'help' => 'Shows/hides the title of this homepage module'
  6864. ),
  6865. array(
  6866. 'type' => 'switch',
  6867. 'name' => 'monitorrCompact',
  6868. 'label' => 'Compact view',
  6869. 'value' => $this->config['monitorrCompact'],
  6870. 'help' => 'Toggles the compact view of this homepage module'
  6871. ),
  6872. ),
  6873. )
  6874. ),
  6875. array(
  6876. 'name' => 'Weather-Air',
  6877. 'enabled' => true,
  6878. 'image' => 'plugins/images/tabs/wind.png',
  6879. 'category' => 'Monitor',
  6880. 'settings' => array(
  6881. 'Enable' => array(
  6882. array(
  6883. 'type' => 'switch',
  6884. 'name' => 'homepageWeatherAndAirEnabled',
  6885. 'label' => 'Enable',
  6886. 'value' => $this->config['homepageWeatherAndAirEnabled']
  6887. ),
  6888. array(
  6889. 'type' => 'select',
  6890. 'name' => 'homepageWeatherAndAirAuth',
  6891. 'label' => 'Minimum Authentication',
  6892. 'value' => $this->config['homepageWeatherAndAirAuth'],
  6893. 'options' => $groups
  6894. )
  6895. ),
  6896. 'Connection' => array(
  6897. array(
  6898. 'type' => 'input',
  6899. 'name' => 'homepageWeatherAndAirLatitude',
  6900. 'label' => 'Latitude',
  6901. 'value' => $this->config['homepageWeatherAndAirLatitude'],
  6902. 'help' => 'Please enter full latitude including minus if needed'
  6903. ),
  6904. array(
  6905. 'type' => 'input',
  6906. 'name' => 'homepageWeatherAndAirLongitude',
  6907. 'label' => 'Longitude',
  6908. 'value' => $this->config['homepageWeatherAndAirLongitude'],
  6909. 'help' => 'Please enter full longitude including minus if needed'
  6910. ),
  6911. array(
  6912. 'type' => 'blank',
  6913. 'label' => ''
  6914. ),
  6915. array(
  6916. 'type' => 'button',
  6917. 'label' => '',
  6918. 'icon' => 'fa fa-search',
  6919. 'class' => 'pull-right',
  6920. 'text' => 'Need Help With Coordinates?',
  6921. 'attr' => 'onclick="showLookupCoordinatesModal()"'
  6922. ),
  6923. ),
  6924. 'Options' => array(
  6925. array(
  6926. 'type' => 'input',
  6927. 'name' => 'homepageWeatherAndAirWeatherHeader',
  6928. 'label' => 'Title',
  6929. 'value' => $this->config['homepageWeatherAndAirWeatherHeader'],
  6930. 'help' => 'Sets the title of this homepage module',
  6931. ),
  6932. array(
  6933. 'type' => 'switch',
  6934. 'name' => 'homepageWeatherAndAirWeatherHeaderToggle',
  6935. 'label' => 'Toggle Title',
  6936. 'value' => $this->config['homepageWeatherAndAirWeatherHeaderToggle'],
  6937. 'help' => 'Shows/hides the title of this homepage module'
  6938. ),
  6939. array(
  6940. 'type' => 'switch',
  6941. 'name' => 'homepageWeatherAndAirWeatherEnabled',
  6942. 'label' => 'Enable Weather',
  6943. 'value' => $this->config['homepageWeatherAndAirWeatherEnabled'],
  6944. 'help' => 'Toggles the view module for Weather'
  6945. ),
  6946. array(
  6947. 'type' => 'switch',
  6948. 'name' => 'homepageWeatherAndAirAirQualityEnabled',
  6949. 'label' => 'Enable Air Quality',
  6950. 'value' => $this->config['homepageWeatherAndAirAirQualityEnabled'],
  6951. 'help' => 'Toggles the view module for Air Quality'
  6952. ),
  6953. array(
  6954. 'type' => 'switch',
  6955. 'name' => 'homepageWeatherAndAirPollenEnabled',
  6956. 'label' => 'Enable Pollen',
  6957. 'value' => $this->config['homepageWeatherAndAirPollenEnabled'],
  6958. 'help' => 'Toggles the view module for Pollen'
  6959. ),
  6960. array(
  6961. 'type' => 'select',
  6962. 'name' => 'homepageWeatherAndAirUnits',
  6963. 'label' => 'Unit of Measurement',
  6964. 'value' => $this->config['homepageWeatherAndAirUnits'],
  6965. 'options' => array(
  6966. array(
  6967. 'name' => 'Imperial',
  6968. 'value' => 'imperial'
  6969. ),
  6970. array(
  6971. 'name' => 'Metric',
  6972. 'value' => 'metric'
  6973. )
  6974. )
  6975. ),
  6976. array(
  6977. 'type' => 'select',
  6978. 'name' => 'homepageWeatherAndAirRefresh',
  6979. 'label' => 'Refresh Seconds',
  6980. 'value' => $this->config['homepageWeatherAndAirRefresh'],
  6981. 'options' => $this->optionTime()
  6982. ),
  6983. ),
  6984. )
  6985. ),
  6986. array(
  6987. 'name' => 'Speedtest',
  6988. 'enabled' => true,
  6989. 'image' => 'plugins/images/tabs/speedtest-icon.png',
  6990. 'category' => 'Monitor',
  6991. 'settings' => array(
  6992. 'Enable' => array(
  6993. array(
  6994. 'type' => 'html',
  6995. 'override' => 6,
  6996. 'label' => 'Info',
  6997. 'html' => '<p>This homepage item requires <a href="https://github.com/henrywhitaker3/Speedtest-Tracker" target="_blank" rel="noreferrer noopener">Speedtest-Tracker <i class="fa fa-external-link" aria-hidden="true"></i></a> to be running on your network.</p>'
  6998. ),
  6999. array(
  7000. 'type' => 'switch',
  7001. 'name' => 'homepageSpeedtestEnabled',
  7002. 'label' => 'Enable',
  7003. 'value' => $this->config['homepageSpeedtestEnabled']
  7004. ),
  7005. array(
  7006. 'type' => 'select',
  7007. 'name' => 'homepageSpeedtestAuth',
  7008. 'label' => 'Minimum Authentication',
  7009. 'value' => $this->config['homepageSpeedtestAuth'],
  7010. 'options' => $groups
  7011. )
  7012. ),
  7013. 'Connection' => array(
  7014. array(
  7015. 'type' => 'input',
  7016. 'name' => 'speedtestURL',
  7017. 'label' => 'URL',
  7018. 'value' => $this->config['speedtestURL'],
  7019. 'help' => 'Enter the IP:PORT of your speedtest instance e.g. http(s)://<ip>:<port>'
  7020. ),
  7021. ),
  7022. 'Options' => array(
  7023. array(
  7024. 'type' => 'input',
  7025. 'name' => 'speedtestHeader',
  7026. 'label' => 'Title',
  7027. 'value' => $this->config['speedtestHeader'],
  7028. 'help' => 'Sets the title of this homepage module',
  7029. ),
  7030. array(
  7031. 'type' => 'switch',
  7032. 'name' => 'speedtestHeaderToggle',
  7033. 'label' => 'Toggle Title',
  7034. 'value' => $this->config['speedtestHeaderToggle'],
  7035. 'help' => 'Shows/hides the title of this homepage module'
  7036. ),
  7037. ),
  7038. )
  7039. ),
  7040. $this->netdataSettingsArray(),
  7041. array(
  7042. 'name' => 'Octoprint',
  7043. 'enabled' => true,
  7044. 'image' => 'plugins/images/tabs/octoprint.png',
  7045. 'category' => 'Monitor',
  7046. 'settings' => array(
  7047. 'Enable' => array(
  7048. array(
  7049. 'type' => 'switch',
  7050. 'name' => 'homepageOctoprintEnabled',
  7051. 'label' => 'Enable',
  7052. 'value' => $this->config['homepageOctoprintEnabled']
  7053. ),
  7054. array(
  7055. 'type' => 'select',
  7056. 'name' => 'homepageOctoprintAuth',
  7057. 'label' => 'Minimum Authentication',
  7058. 'value' => $this->config['homepageOctoprintAuth'],
  7059. 'options' => $groups
  7060. )
  7061. ),
  7062. 'Connection' => array(
  7063. array(
  7064. 'type' => 'input',
  7065. 'name' => 'octoprintURL',
  7066. 'label' => 'URL',
  7067. 'value' => $this->config['octoprintURL'],
  7068. 'help' => 'Enter the IP:PORT of your Octoprint instance e.g. http://octopi.local'
  7069. ),
  7070. array(
  7071. 'type' => 'input',
  7072. 'name' => 'octoprintToken',
  7073. 'label' => 'API Key',
  7074. 'value' => $this->config['octoprintToken'],
  7075. 'help' => 'Enter your Octoprint API key, found in Octoprint settings page.'
  7076. ),
  7077. ),
  7078. 'Options' => array(
  7079. array(
  7080. 'type' => 'input',
  7081. 'name' => 'octoprintHeader',
  7082. 'label' => 'Title',
  7083. 'value' => $this->config['octoprintHeader'],
  7084. 'help' => 'Sets the title of this homepage module',
  7085. ),
  7086. array(
  7087. 'type' => 'switch',
  7088. 'name' => 'octoprintToggle',
  7089. 'label' => 'Toggle Title',
  7090. 'value' => $this->config['octoprintHeaderToggle'],
  7091. 'help' => 'Shows/hides the title of this homepage module'
  7092. ),
  7093. ),
  7094. )
  7095. ),
  7096. );
  7097. }
  7098. public function isTabNameTaken($name, $id = null)
  7099. {
  7100. if ($id) {
  7101. $response = [
  7102. array(
  7103. 'function' => 'fetchAll',
  7104. 'query' => array(
  7105. 'SELECT * FROM tabs WHERE `name` LIKE ? AND `id` != ?',
  7106. $name,
  7107. $id
  7108. )
  7109. ),
  7110. ];
  7111. } else {
  7112. $response = [
  7113. array(
  7114. 'function' => 'fetchAll',
  7115. 'query' => array(
  7116. 'SELECT * FROM tabs WHERE `name` LIKE ?',
  7117. $name
  7118. )
  7119. ),
  7120. ];
  7121. }
  7122. return $this->processQueries($response);
  7123. }
  7124. public function isCategoryNameTaken($name, $id = null)
  7125. {
  7126. if ($id) {
  7127. $response = [
  7128. array(
  7129. 'function' => 'fetchAll',
  7130. 'query' => array(
  7131. 'SELECT * FROM categories WHERE `category` LIKE ? AND `id` != ?',
  7132. $name,
  7133. $id
  7134. )
  7135. ),
  7136. ];
  7137. } else {
  7138. $response = [
  7139. array(
  7140. 'function' => 'fetchAll',
  7141. 'query' => array(
  7142. 'SELECT * FROM categories WHERE `category` LIKE ?',
  7143. $name
  7144. )
  7145. ),
  7146. ];
  7147. }
  7148. return $this->processQueries($response);
  7149. }
  7150. public function isGroupNameTaken($name, $id = null)
  7151. {
  7152. if ($id) {
  7153. $response = [
  7154. array(
  7155. 'function' => 'fetchAll',
  7156. 'query' => array(
  7157. 'SELECT * FROM groups WHERE `group` LIKE ? AND `id` != ?',
  7158. $name,
  7159. $id
  7160. )
  7161. ),
  7162. ];
  7163. } else {
  7164. $response = [
  7165. array(
  7166. 'function' => 'fetchAll',
  7167. 'query' => array(
  7168. 'SELECT * FROM groups WHERE `group` LIKE ?',
  7169. $name
  7170. )
  7171. ),
  7172. ];
  7173. }
  7174. return $this->processQueries($response);
  7175. }
  7176. public function getTableColumns($table)
  7177. {
  7178. $response = [
  7179. array(
  7180. 'function' => 'fetchAll',
  7181. 'query' => array(
  7182. 'PRAGMA table_info(?)',
  7183. $table
  7184. )
  7185. ),
  7186. ];
  7187. return $this->processQueries($response);
  7188. }
  7189. public function getTableColumnsFormatted($table)
  7190. {
  7191. $columns = $this->getTableColumns($table);
  7192. if ($columns) {
  7193. $columnsFormatted = [];
  7194. foreach ($columns as $k => $v) {
  7195. $columnsFormatted[$v['name']] = $v;
  7196. }
  7197. return $columnsFormatted;
  7198. } else {
  7199. return false;
  7200. }
  7201. }
  7202. public function getTabById($id)
  7203. {
  7204. $response = [
  7205. array(
  7206. 'function' => 'fetch',
  7207. 'query' => array(
  7208. 'SELECT * FROM tabs WHERE `id` = ?',
  7209. $id
  7210. )
  7211. ),
  7212. ];
  7213. return $this->processQueries($response);
  7214. }
  7215. public function getTabGroupByTabName($tab)
  7216. {
  7217. $response = [
  7218. array(
  7219. 'function' => 'fetch',
  7220. 'query' => array(
  7221. 'SELECT group_id FROM tabs WHERE name LIKE %~like~',
  7222. $tab
  7223. )
  7224. ),
  7225. ];
  7226. $query = $this->processQueries($response);
  7227. return $query ? $query['group_id'] : 0;
  7228. }
  7229. public function getCategoryById($id)
  7230. {
  7231. $response = [
  7232. array(
  7233. 'function' => 'fetch',
  7234. 'query' => array(
  7235. 'SELECT * FROM categories WHERE `id` = ?',
  7236. $id
  7237. )
  7238. ),
  7239. ];
  7240. return $this->processQueries($response);
  7241. }
  7242. public function getGroupUserCountById($id)
  7243. {
  7244. $response = [
  7245. array(
  7246. 'function' => 'fetchSingle',
  7247. 'query' => array(
  7248. 'SELECT count(username) AS count FROM groups INNER JOIN users ON users.group_id = groups.group_id AND groups.id = ?',
  7249. $id
  7250. )
  7251. ),
  7252. ];
  7253. return $this->processQueries($response);
  7254. }
  7255. public function getGroupById($id)
  7256. {
  7257. $response = [
  7258. array(
  7259. 'function' => 'fetch',
  7260. 'query' => array(
  7261. 'SELECT * FROM groups WHERE `id` = ?',
  7262. $id
  7263. )
  7264. ),
  7265. ];
  7266. return $this->processQueries($response);
  7267. }
  7268. public function getGroupByGroupId($id)
  7269. {
  7270. $response = [
  7271. array(
  7272. 'function' => 'fetch',
  7273. 'query' => array(
  7274. 'SELECT * FROM groups WHERE `group_id` = ?',
  7275. $id
  7276. )
  7277. ),
  7278. ];
  7279. return $this->processQueries($response);
  7280. }
  7281. public function getDefaultGroup()
  7282. {
  7283. $response = [
  7284. array(
  7285. 'function' => 'fetch',
  7286. 'query' => array(
  7287. 'SELECT * FROM groups WHERE `default` = 1'
  7288. )
  7289. ),
  7290. ];
  7291. return $this->processQueries($response);
  7292. }
  7293. public function getDefaultGroupId()
  7294. {
  7295. $response = [
  7296. array(
  7297. 'function' => 'fetchSingle',
  7298. 'query' => array(
  7299. 'SELECT `group_id` FROM groups WHERE `default` = 1'
  7300. )
  7301. ),
  7302. ];
  7303. return $this->processQueries($response);
  7304. }
  7305. public function getDefaultCategory()
  7306. {
  7307. $response = [
  7308. array(
  7309. 'function' => 'fetch',
  7310. 'query' => array(
  7311. 'SELECT * FROM categories WHERE `default` = 1'
  7312. )
  7313. ),
  7314. ];
  7315. return $this->processQueries($response);
  7316. }
  7317. public function getDefaultCategoryId()
  7318. {
  7319. $response = [
  7320. array(
  7321. 'function' => 'fetchSingle',
  7322. 'query' => array(
  7323. 'SELECT `category_id` FROM categories WHERE `default` = 1'
  7324. )
  7325. ),
  7326. ];
  7327. return $this->processQueries($response);
  7328. }
  7329. public function getNextTabOrder()
  7330. {
  7331. $response = [
  7332. array(
  7333. 'function' => 'fetchSingle',
  7334. 'query' => array(
  7335. 'SELECT `order` from tabs ORDER BY `order` DESC'
  7336. )
  7337. ),
  7338. ];
  7339. return $this->processQueries($response);
  7340. }
  7341. public function getNextCategoryOrder()
  7342. {
  7343. $response = [
  7344. array(
  7345. 'function' => 'fetchSingle',
  7346. 'query' => array(
  7347. 'SELECT `order` from categories ORDER BY `order` DESC'
  7348. )
  7349. ),
  7350. ];
  7351. return $this->processQueries($response);
  7352. }
  7353. public function getNextGroupOrder()
  7354. {
  7355. $response = [
  7356. array(
  7357. 'function' => 'fetchSingle',
  7358. 'query' => array(
  7359. 'SELECT `group_id` from groups WHERE `group_id` != "999" ORDER BY `group_id` DESC'
  7360. )
  7361. ),
  7362. ];
  7363. return $this->processQueries($response);
  7364. }
  7365. public function getNextCategoryId()
  7366. {
  7367. $response = [
  7368. array(
  7369. 'function' => 'fetchSingle',
  7370. 'query' => array(
  7371. 'SELECT `category_id` from categories ORDER BY `category_id` DESC'
  7372. )
  7373. ),
  7374. ];
  7375. return $this->processQueries($response);
  7376. }
  7377. public function clearTabDefault()
  7378. {
  7379. $response = [
  7380. array(
  7381. 'function' => 'query',
  7382. 'query' => array(
  7383. 'UPDATE tabs SET `default` = 0'
  7384. )
  7385. ),
  7386. ];
  7387. return $this->processQueries($response);
  7388. }
  7389. public function clearCategoryDefault()
  7390. {
  7391. $response = [
  7392. array(
  7393. 'function' => 'query',
  7394. 'query' => array(
  7395. 'UPDATE categories SET `default` = 0'
  7396. )
  7397. ),
  7398. ];
  7399. return $this->processQueries($response);
  7400. }
  7401. public function clearGroupDefault()
  7402. {
  7403. $response = [
  7404. array(
  7405. 'function' => 'query',
  7406. 'query' => array(
  7407. 'UPDATE groups SET `default` = 0'
  7408. )
  7409. ),
  7410. ];
  7411. return $this->processQueries($response);
  7412. }
  7413. public function checkKeys($tabInfo, $newData)
  7414. {
  7415. foreach ($newData as $k => $v) {
  7416. if (!array_key_exists($k, $tabInfo)) {
  7417. unset($newData[$k]);
  7418. }
  7419. }
  7420. return $newData;
  7421. }
  7422. public function deleteTab($id)
  7423. {
  7424. $response = [
  7425. array(
  7426. 'function' => 'query',
  7427. 'query' => array(
  7428. 'DELETE FROM tabs WHERE id = ?',
  7429. $id
  7430. )
  7431. ),
  7432. ];
  7433. $tabInfo = $this->getTabById($id);
  7434. if ($tabInfo) {
  7435. $this->writeLog('success', 'Tab Delete Function - Deleted Tab [' . $tabInfo['name'] . ']', $this->user['username']);
  7436. $this->setAPIResponse('success', 'Tab deleted', 204);
  7437. return $this->processQueries($response);
  7438. } else {
  7439. $this->setAPIResponse('error', 'id not found', 404);
  7440. return false;
  7441. }
  7442. }
  7443. public function addTab($array)
  7444. {
  7445. if (!$array) {
  7446. $this->setAPIResponse('error', 'no data was sent', 422);
  7447. return null;
  7448. }
  7449. $array = $this->checkKeys($this->getTableColumnsFormatted('tabs'), $array);
  7450. $array['group_id'] = ($array['group_id']) ?? $this->getDefaultGroupId();
  7451. $array['category_id'] = ($array['category_id']) ?? $this->getDefaultCategoryId();
  7452. $array['enabled'] = ($array['enabled']) ?? 0;
  7453. $array['default'] = ($array['default']) ?? 0;
  7454. $array['type'] = ($array['type']) ?? 1;
  7455. $array['order'] = ($array['order']) ?? $this->getNextTabOrder() + 1;
  7456. if (array_key_exists('name', $array)) {
  7457. if ($this->isTabNameTaken($array['name'])) {
  7458. $this->setAPIResponse('error', 'Tab name: ' . $array['name'] . ' is already taken', 409);
  7459. return false;
  7460. }
  7461. } else {
  7462. $this->setAPIResponse('error', 'Tab name was not supplied', 422);
  7463. return false;
  7464. }
  7465. if (!array_key_exists('url', $array) && !array_key_exists('url_local', $array)) {
  7466. $this->setAPIResponse('error', 'Tab url or url_local was not supplied', 422);
  7467. return false;
  7468. }
  7469. if (!array_key_exists('image', $array)) {
  7470. $this->setAPIResponse('error', 'Tab image was not supplied', 422);
  7471. return false;
  7472. }
  7473. $response = [
  7474. array(
  7475. 'function' => 'query',
  7476. 'query' => array(
  7477. 'INSERT INTO [tabs]',
  7478. $array
  7479. )
  7480. ),
  7481. ];
  7482. $this->setAPIResponse(null, 'Tab added');
  7483. $this->writeLog('success', 'Tab Editor Function - Added Tab for [' . $array['name'] . ']', $this->user['username']);
  7484. return $this->processQueries($response);
  7485. }
  7486. public function updateTab($id, $array)
  7487. {
  7488. if (!$id || $id == '') {
  7489. $this->setAPIResponse('error', 'id was not set', 422);
  7490. return null;
  7491. }
  7492. if (!$array) {
  7493. $this->setAPIResponse('error', 'no data was sent', 422);
  7494. return null;
  7495. }
  7496. $tabInfo = $this->getTabById($id);
  7497. if ($tabInfo) {
  7498. $array = $this->checkKeys($tabInfo, $array);
  7499. } else {
  7500. $this->setAPIResponse('error', 'No tab info found', 404);
  7501. return false;
  7502. }
  7503. if (array_key_exists('name', $array)) {
  7504. if ($this->isTabNameTaken($array['name'], $id)) {
  7505. $this->setAPIResponse('error', 'Tab name: ' . $array['name'] . ' is already taken', 409);
  7506. return false;
  7507. }
  7508. }
  7509. if (array_key_exists('default', $array)) {
  7510. if ($array['default']) {
  7511. $this->clearTabDefault();
  7512. }
  7513. }
  7514. $response = [
  7515. array(
  7516. 'function' => 'query',
  7517. 'query' => array(
  7518. 'UPDATE tabs SET',
  7519. $array,
  7520. 'WHERE id = ?',
  7521. $id
  7522. )
  7523. ),
  7524. ];
  7525. $this->setAPIResponse(null, 'Tab info updated');
  7526. $this->writeLog('success', 'Tab Editor Function - Edited Tab Info for [' . $tabInfo['name'] . ']', $this->user['username']);
  7527. return $this->processQueries($response);
  7528. }
  7529. public function updateTabOrder($array)
  7530. {
  7531. if (count($array) >= 1) {
  7532. foreach ($array as $tab) {
  7533. if (count($tab) !== 2) {
  7534. $this->setAPIResponse('error', 'data is malformed', 422);
  7535. break;
  7536. }
  7537. $id = $tab['id'] ?? null;
  7538. $order = $tab['order'] ?? null;
  7539. if ($id && $order) {
  7540. $response = [
  7541. array(
  7542. 'function' => 'query',
  7543. 'query' => array(
  7544. 'UPDATE tabs set `order` = ? WHERE `id` = ?',
  7545. $order,
  7546. $id
  7547. )
  7548. ),
  7549. ];
  7550. $this->processQueries($response);
  7551. $this->setAPIResponse(null, 'Tab Order updated');
  7552. } else {
  7553. $this->setAPIResponse('error', 'data is malformed', 422);
  7554. }
  7555. }
  7556. } else {
  7557. $this->setAPIResponse('error', 'data is empty or not in array', 422);
  7558. return false;
  7559. }
  7560. }
  7561. public function addCategory($array)
  7562. {
  7563. if (!$array) {
  7564. $this->setAPIResponse('error', 'no data was sent', 422);
  7565. return null;
  7566. }
  7567. $array = $this->checkKeys($this->getTableColumnsFormatted('categories'), $array);
  7568. $array['default'] = ($array['default']) ?? 0;
  7569. $array['order'] = ($array['order']) ?? $this->getNextCategoryOrder() + 1;
  7570. $array['category_id'] = ($array['category_id']) ?? $this->getNextCategoryId() + 1;
  7571. if (array_key_exists('category', $array)) {
  7572. if ($this->isCategoryNameTaken($array['category'])) {
  7573. $this->setAPIResponse('error', 'Category name: ' . $array['category'] . ' is already taken', 409);
  7574. return false;
  7575. }
  7576. } else {
  7577. $this->setAPIResponse('error', 'Category name was not supplied', 422);
  7578. return false;
  7579. }
  7580. if (!array_key_exists('image', $array)) {
  7581. $this->setAPIResponse('error', 'Category image was not supplied', 422);
  7582. }
  7583. $response = [
  7584. array(
  7585. 'function' => 'query',
  7586. 'query' => array(
  7587. 'INSERT INTO [categories]',
  7588. $array
  7589. )
  7590. ),
  7591. ];
  7592. $this->setAPIResponse(null, 'Category added');
  7593. $this->writeLog('success', 'Category Editor Function - Added Category for [' . $array['category'] . ']', $this->user['username']);
  7594. return $this->processQueries($response);
  7595. }
  7596. public function updateCategory($id, $array)
  7597. {
  7598. if (!$id || $id == '') {
  7599. $this->setAPIResponse('error', 'id was not set', 422);
  7600. return null;
  7601. }
  7602. if (!$array) {
  7603. $this->setAPIResponse('error', 'no data was sent', 422);
  7604. return null;
  7605. }
  7606. $categoryInfo = $this->getCategoryById($id);
  7607. if ($categoryInfo) {
  7608. $array = $this->checkKeys($categoryInfo, $array);
  7609. } else {
  7610. $this->setAPIResponse('error', 'No category info found', 404);
  7611. return false;
  7612. }
  7613. if (array_key_exists('category', $array)) {
  7614. if ($this->isCategoryNameTaken($array['category'], $id)) {
  7615. $this->setAPIResponse('error', 'Category name: ' . $array['category'] . ' is already taken', 409);
  7616. return false;
  7617. }
  7618. }
  7619. if (array_key_exists('default', $array)) {
  7620. if ($array['default']) {
  7621. $this->clearCategoryDefault();
  7622. }
  7623. }
  7624. $response = [
  7625. array(
  7626. 'function' => 'query',
  7627. 'query' => array(
  7628. 'UPDATE categories SET',
  7629. $array,
  7630. 'WHERE id = ?',
  7631. $id
  7632. )
  7633. ),
  7634. ];
  7635. $this->setAPIResponse(null, 'Category info updated');
  7636. $this->writeLog('success', 'Category Editor Function - Edited Category Info for [' . $categoryInfo['category'] . ']', $this->user['username']);
  7637. return $this->processQueries($response);
  7638. }
  7639. public function updateCategoryOrder($array)
  7640. {
  7641. if (count($array) >= 1) {
  7642. foreach ($array as $category) {
  7643. if (count($category) !== 2) {
  7644. $this->setAPIResponse('error', 'data is malformed', 422);
  7645. break;
  7646. }
  7647. $id = $category['id'] ?? null;
  7648. $order = $category['order'] ?? null;
  7649. if ($id && $order) {
  7650. $response = [
  7651. array(
  7652. 'function' => 'query',
  7653. 'query' => array(
  7654. 'UPDATE categories set `order` = ? WHERE `id` = ?',
  7655. $order,
  7656. $id
  7657. )
  7658. ),
  7659. ];
  7660. $this->processQueries($response);
  7661. $this->setAPIResponse(null, 'Category Order updated');
  7662. } else {
  7663. $this->setAPIResponse('error', 'data is malformed', 422);
  7664. }
  7665. }
  7666. } else {
  7667. $this->setAPIResponse('error', 'data is empty or not in array', 422);
  7668. return false;
  7669. }
  7670. }
  7671. public function deleteCategory($id)
  7672. {
  7673. $response = [
  7674. array(
  7675. 'function' => 'query',
  7676. 'query' => array(
  7677. 'DELETE FROM categories WHERE id = ?',
  7678. $id
  7679. )
  7680. ),
  7681. ];
  7682. $categoryInfo = $this->getCategoryById($id);
  7683. if ($categoryInfo) {
  7684. $this->writeLog('success', 'Category Delete Function - Deleted Category [' . $categoryInfo['category'] . ']', $this->user['username']);
  7685. $this->setAPIResponse('success', 'Category deleted', 204);
  7686. return $this->processQueries($response);
  7687. } else {
  7688. $this->setAPIResponse('error', 'id not found', 404);
  7689. return false;
  7690. }
  7691. }
  7692. public function marketplaceFileListFormat($files, $folder, $type)
  7693. {
  7694. foreach ($files as $k => $v) {
  7695. $splitFiles = explode('|', $v);
  7696. $prePath = (strlen($k) !== 1) ? $k . '/' : $k;
  7697. foreach ($splitFiles as $file) {
  7698. $filesList[] = array(
  7699. 'fileName' => $file,
  7700. 'path' => $prePath,
  7701. 'githubPath' => 'https://raw.githubusercontent.com/causefx/Organizr/v2-' . $type . '/' . $folder . $prePath . $file
  7702. );
  7703. }
  7704. }
  7705. return $filesList;
  7706. }
  7707. public function removeTheme($theme)
  7708. {
  7709. $theme = $this->reverseCleanClassName($theme);
  7710. $array = $this->getThemesGithub();
  7711. $arrayLower = array_change_key_case($array);
  7712. if (!$array) {
  7713. $this->setAPIResponse('error', 'Could not access theme marketplace', 409);
  7714. return false;
  7715. }
  7716. if (!$arrayLower[$theme]) {
  7717. $this->setAPIResponse('error', 'Theme does not exist in marketplace', 404);
  7718. return false;
  7719. } else {
  7720. $key = array_search($theme, array_keys($arrayLower));
  7721. $theme = array_keys($array)[$key];
  7722. }
  7723. $array = $array[$theme];
  7724. $downloadList = $this->marketplaceFileListFormat($array['files'], $array['github_folder'], 'themes');
  7725. if (!$downloadList) {
  7726. $this->setAPIResponse('error', 'Could not get download list for theme', 409);
  7727. return false;
  7728. }
  7729. $name = $theme;
  7730. $version = $array['version'];
  7731. $installedThemesNew = '';
  7732. foreach ($downloadList as $k => $v) {
  7733. $file = array(
  7734. 'from' => $v['githubPath'],
  7735. 'to' => str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, $this->root . $v['path'] . $v['fileName']),
  7736. 'path' => str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, $this->root . $v['path'])
  7737. );
  7738. if (!$this->rrmdir($file['to'])) {
  7739. $this->writeLog('error', 'Theme Function - Remove File Failed for: ' . $v['githubPath'], $this->user['username']);
  7740. return false;
  7741. }
  7742. }
  7743. if ($this->config['installedThemes'] !== '') {
  7744. $installedThemes = explode('|', $this->config['installedThemes']);
  7745. foreach ($installedThemes as $k => $v) {
  7746. $themes = explode(':', $v);
  7747. $installedThemesList[$themes[0]] = $themes[1];
  7748. }
  7749. if (isset($installedThemesList[$name])) {
  7750. foreach ($installedThemesList as $k => $v) {
  7751. if ($k !== $name) {
  7752. if ($installedThemesNew == '') {
  7753. $installedThemesNew .= $k . ':' . $v;
  7754. } else {
  7755. $installedThemesNew .= '|' . $k . ':' . $v;
  7756. }
  7757. }
  7758. }
  7759. }
  7760. }
  7761. $this->updateConfig(array('installedThemes' => $installedThemesNew));
  7762. $this->setAPIResponse('success', 'Theme removed', 200, $installedThemesNew);
  7763. return true;
  7764. }
  7765. public function installTheme($theme)
  7766. {
  7767. $theme = $this->reverseCleanClassName($theme);
  7768. $array = $this->getThemesGithub();
  7769. $arrayLower = array_change_key_case($array);
  7770. if (!$array) {
  7771. $this->setAPIResponse('error', 'Could not access theme marketplace', 409);
  7772. return false;
  7773. }
  7774. if (!$arrayLower[$theme]) {
  7775. $this->setAPIResponse('error', 'Theme does not exist in marketplace', 404);
  7776. return false;
  7777. } else {
  7778. $key = array_search($theme, array_keys($arrayLower));
  7779. $theme = array_keys($array)[$key];
  7780. }
  7781. $array = $array[$theme];
  7782. $downloadList = $this->marketplaceFileListFormat($array['files'], $array['github_folder'], 'themes');
  7783. if (!$downloadList) {
  7784. $this->setAPIResponse('error', 'Could not get download list for theme', 409);
  7785. return false;
  7786. }
  7787. $name = $theme;
  7788. $version = $array['version'];
  7789. foreach ($downloadList as $k => $v) {
  7790. $file = array(
  7791. 'from' => $v['githubPath'],
  7792. 'to' => str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, $this->root . $v['path'] . $v['fileName']),
  7793. 'path' => str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, $this->root . $v['path'])
  7794. );
  7795. if (!$this->downloadFileToPath($file['from'], $file['to'], $file['path'])) {
  7796. $this->writeLog('error', 'Theme Function - Downloaded File Failed for: ' . $v['githubPath'], $this->user['username']);
  7797. $this->setAPIResponse('error', 'Theme download failed', 500);
  7798. return false;
  7799. }
  7800. }
  7801. if ($this->config['installedThemes'] !== '') {
  7802. $installedThemes = explode('|', $this->config['installedThemes']);
  7803. foreach ($installedThemes as $k => $v) {
  7804. $themes = explode(':', $v);
  7805. $installedThemesList[$themes[0]] = $themes[1];
  7806. }
  7807. if (isset($installedThemesList[$name])) {
  7808. $installedThemesList[$name] = $version;
  7809. $installedThemesNew = '';
  7810. foreach ($installedThemesList as $k => $v) {
  7811. if ($installedThemesNew == '') {
  7812. $installedThemesNew .= $k . ':' . $v;
  7813. } else {
  7814. $installedThemesNew .= '|' . $k . ':' . $v;
  7815. }
  7816. }
  7817. } else {
  7818. $installedThemesNew = $this->config['installedThemes'] . '|' . $name . ':' . $version;
  7819. }
  7820. } else {
  7821. $installedThemesNew = $name . ':' . $version;
  7822. }
  7823. $this->updateConfig(array('installedThemes' => $installedThemesNew));
  7824. $this->setAPIResponse('success', 'Theme installed', 200, $installedThemesNew);
  7825. return true;
  7826. }
  7827. public function removePlugin($plugin)
  7828. {
  7829. $plugin = $this->reverseCleanClassName($plugin);
  7830. $array = $this->getPluginsGithub();
  7831. $arrayLower = array_change_key_case($array);
  7832. if (!$array) {
  7833. $this->setAPIResponse('error', 'Could not access plugin marketplace', 409);
  7834. return false;
  7835. }
  7836. if (!$arrayLower[$plugin]) {
  7837. $this->setAPIResponse('error', 'Plugin does not exist in marketplace', 404);
  7838. return false;
  7839. } else {
  7840. $key = array_search($plugin, array_keys($arrayLower));
  7841. $plugin = array_keys($array)[$key];
  7842. }
  7843. $array = $array[$plugin];
  7844. $downloadList = $this->marketplaceFileListFormat($array['files'], $array['github_folder'], 'plugins');
  7845. if (!$downloadList) {
  7846. $this->setAPIResponse('error', 'Could not get download list for plugin', 409);
  7847. return false;
  7848. }
  7849. $name = $plugin;
  7850. $version = $array['version'];
  7851. $installedPluginsNew = '';
  7852. foreach ($downloadList as $k => $v) {
  7853. $file = array(
  7854. 'from' => $v['githubPath'],
  7855. 'to' => str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, $this->root . $v['path'] . $v['fileName']),
  7856. 'path' => str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, $this->root . $v['path'])
  7857. );
  7858. if (!$this->rrmdir($file['to'])) {
  7859. $this->writeLog('error', 'Plugin Function - Remove File Failed for: ' . $v['githubPath'], $this->user['username']);
  7860. return false;
  7861. }
  7862. }
  7863. if ($this->config['installedPlugins'] !== '') {
  7864. $installedPlugins = explode('|', $this->config['installedPlugins']);
  7865. foreach ($installedPlugins as $k => $v) {
  7866. $plugins = explode(':', $v);
  7867. $installedPluginsList[$plugins[0]] = $plugins[1];
  7868. }
  7869. if (isset($installedPluginsList[$name])) {
  7870. foreach ($installedPluginsList as $k => $v) {
  7871. if ($k !== $name) {
  7872. if ($installedPluginsNew == '') {
  7873. $installedPluginsNew .= $k . ':' . $v;
  7874. } else {
  7875. $installedPluginsNew .= '|' . $k . ':' . $v;
  7876. }
  7877. }
  7878. }
  7879. }
  7880. }
  7881. $this->updateConfig(array('installedPlugins' => $installedPluginsNew));
  7882. $this->setAPIResponse('success', 'Plugin removed', 200, $installedPluginsNew);
  7883. return true;
  7884. }
  7885. public function installPlugin($plugin)
  7886. {
  7887. $plugin = $this->reverseCleanClassName($plugin);
  7888. $array = $this->getPluginsGithub();
  7889. $arrayLower = array_change_key_case($array);
  7890. if (!$array) {
  7891. $this->setAPIResponse('error', 'Could not access plugin marketplace', 409);
  7892. return false;
  7893. }
  7894. if (!$arrayLower[$plugin]) {
  7895. $this->setAPIResponse('error', 'Plugin does not exist in marketplace', 404);
  7896. return false;
  7897. } else {
  7898. $key = array_search($plugin, array_keys($arrayLower));
  7899. $plugin = array_keys($array)[$key];
  7900. }
  7901. $array = $array[$plugin];
  7902. $downloadList = $this->marketplaceFileListFormat($array['files'], $array['github_folder'], 'plugins');
  7903. if (!$downloadList) {
  7904. $this->setAPIResponse('error', 'Could not get download list for plugin', 409);
  7905. return false;
  7906. }
  7907. $name = $plugin;
  7908. $version = $array['version'];
  7909. foreach ($downloadList as $k => $v) {
  7910. $file = array(
  7911. 'from' => $v['githubPath'],
  7912. 'to' => str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, $this->root . $v['path'] . $v['fileName']),
  7913. 'path' => str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, $this->root . $v['path'])
  7914. );
  7915. if (!$this->downloadFileToPath($file['from'], $file['to'], $file['path'])) {
  7916. $this->writeLog('error', 'Plugin Function - Downloaded File Failed for: ' . $v['githubPath'], $this->user['username']);
  7917. $this->setAPIResponse('error', 'Plugin download failed', 500);
  7918. return false;
  7919. }
  7920. }
  7921. if ($this->config['installedPlugins'] !== '') {
  7922. $installedPlugins = explode('|', $this->config['installedPlugins']);
  7923. foreach ($installedPlugins as $k => $v) {
  7924. $plugins = explode(':', $v);
  7925. $installedPluginsList[$plugins[0]] = $plugins[1];
  7926. }
  7927. if (isset($installedPluginsList[$name])) {
  7928. $installedPluginsList[$name] = $version;
  7929. $installedPluginsNew = '';
  7930. foreach ($installedPluginsList as $k => $v) {
  7931. if ($installedPluginsNew == '') {
  7932. $installedPluginsNew .= $k . ':' . $v;
  7933. } else {
  7934. $installedPluginsNew .= '|' . $k . ':' . $v;
  7935. }
  7936. }
  7937. } else {
  7938. $installedPluginsNew = $this->config['installedPlugins'] . '|' . $name . ':' . $version;
  7939. }
  7940. } else {
  7941. $installedPluginsNew = $name . ':' . $version;
  7942. }
  7943. $this->updateConfig(array('installedPlugins' => $installedPluginsNew));
  7944. $this->setAPIResponse('success', 'Plugin installed', 200, $installedPluginsNew);
  7945. return true;
  7946. }
  7947. public function getThemesGithub()
  7948. {
  7949. $url = 'https://raw.githubusercontent.com/causefx/Organizr/v2-themes/themes.json';
  7950. $options = (localURL($url)) ? array('verify' => false) : array();
  7951. $response = Requests::get($url, array(), $options);
  7952. if ($response->success) {
  7953. return json_decode($response->body, true);
  7954. }
  7955. return false;
  7956. }
  7957. public function getPluginsGithub()
  7958. {
  7959. $url = 'https://raw.githubusercontent.com/causefx/Organizr/v2-plugins/plugins.json';
  7960. $options = (localURL($url)) ? array('verify' => false) : array();
  7961. $response = Requests::get($url, array(), $options);
  7962. if ($response->success) {
  7963. return json_decode($response->body, true);
  7964. }
  7965. return false;
  7966. }
  7967. public function guestHash($start, $end)
  7968. {
  7969. $ip = $_SERVER['REMOTE_ADDR'];
  7970. $ip = md5($ip);
  7971. return substr($ip, $start, $end);
  7972. }
  7973. public function rrmdir($dir)
  7974. {
  7975. ini_set('max_execution_time', 0);
  7976. set_time_limit(0);
  7977. if (is_dir($dir)) {
  7978. $files = scandir($dir);
  7979. foreach ($files as $file) {
  7980. if ($file != "." && $file != "..") {
  7981. $this->rrmdir("$dir/$file");
  7982. }
  7983. }
  7984. rmdir($dir);
  7985. } elseif (file_exists($dir)) {
  7986. unlink($dir);
  7987. }
  7988. return true;
  7989. }
  7990. public function rcopy($src, $dst)
  7991. {
  7992. ini_set('max_execution_time', 0);
  7993. set_time_limit(0);
  7994. $src = $this->cleanPath($src);
  7995. $dst = $this->cleanPath($dst);
  7996. if (is_dir($src)) {
  7997. if (!file_exists($dst)) : mkdir($dst);
  7998. endif;
  7999. $files = scandir($src);
  8000. foreach ($files as $file) {
  8001. if ($file != "." && $file != "..") {
  8002. $this->rcopy("$src/$file", "$dst/$file");
  8003. }
  8004. }
  8005. } elseif (file_exists($src)) {
  8006. copy($src, $dst);
  8007. }
  8008. return true;
  8009. }
  8010. public function unzipFile($zipFile)
  8011. {
  8012. ini_set('max_execution_time', 0);
  8013. set_time_limit(0);
  8014. $zip = new ZipArchive;
  8015. $extractPath = dirname(__DIR__, 2) . DIRECTORY_SEPARATOR . "upgrade/";
  8016. if ($zip->open($extractPath . $zipFile) != "true") {
  8017. $this->writeLog("error", "organizr could not unzip upgrade.zip");
  8018. } else {
  8019. $this->writeLog("success", "organizr unzipped upgrade.zip");
  8020. }
  8021. /* Extract Zip File */
  8022. $zip->extractTo($extractPath);
  8023. $zip->close();
  8024. return true;
  8025. }
  8026. public function downloadFile($url, $path)
  8027. {
  8028. ini_set('max_execution_time', 0);
  8029. set_time_limit(0);
  8030. $folderPath = dirname(__DIR__, 2) . DIRECTORY_SEPARATOR . "upgrade" . DIRECTORY_SEPARATOR;
  8031. if (!file_exists($folderPath)) {
  8032. if (@!mkdir($folderPath)) {
  8033. $this->writeLog('error', 'Update Function - Folder Creation failed', $this->user['username']);
  8034. return false;
  8035. }
  8036. }
  8037. $newfname = $folderPath . $path;
  8038. $context = stream_context_create(
  8039. array(
  8040. 'ssl' => array(
  8041. 'verify_peer' => true,
  8042. 'cafile' => $this->getCert()
  8043. )
  8044. )
  8045. );
  8046. $file = fopen($url, 'rb', false, $context);
  8047. if ($file) {
  8048. $newf = fopen($newfname, 'wb');
  8049. if ($newf) {
  8050. while (!feof($file)) {
  8051. fwrite($newf, fread($file, 1024 * 8), 1024 * 8);
  8052. }
  8053. }
  8054. } else {
  8055. $this->writeLog("error", "organizr could not download $url");
  8056. return false;
  8057. }
  8058. if ($file) {
  8059. fclose($file);
  8060. $this->writeLog("success", "organizr finished downloading the github zip file");
  8061. } else {
  8062. $this->writeLog("error", "organizr could not download the github zip file");
  8063. return false;
  8064. }
  8065. if ($newf) {
  8066. fclose($newf);
  8067. $this->writeLog("success", "organizr created upgrade zip file from github zip file");
  8068. } else {
  8069. $this->writeLog("error", "organizr could not create upgrade zip file from github zip file");
  8070. return false;
  8071. }
  8072. return true;
  8073. }
  8074. public function downloadFileToPath($from, $to, $path)
  8075. {
  8076. ini_set('max_execution_time', 0);
  8077. set_time_limit(0);
  8078. if (@!mkdir($path, 0777, true)) {
  8079. $this->writeLog("error", "organizr could not create folder or folder already exists", 'SYSTEM');
  8080. }
  8081. $file = fopen($from, 'rb');
  8082. if ($file) {
  8083. $newf = fopen($to, 'wb');
  8084. if ($newf) {
  8085. while (!feof($file)) {
  8086. fwrite($newf, fread($file, 1024 * 8), 1024 * 8);
  8087. }
  8088. }
  8089. } else {
  8090. $this->writeLog("error", "organizr could not download file", 'SYSTEM');
  8091. }
  8092. if ($file) {
  8093. fclose($file);
  8094. $this->writeLog("success", "organizr finished downloading the file", 'SYSTEM');
  8095. } else {
  8096. $this->writeLog("error", "organizr could not download the file", 'SYSTEM');
  8097. }
  8098. if ($newf) {
  8099. fclose($newf);
  8100. $this->writeLog("success", "organizr saved/moved the file", 'SYSTEM');
  8101. } else {
  8102. $this->writeLog("error", "organizr could not saved/moved the file", 'SYSTEM');
  8103. }
  8104. return true;
  8105. }
  8106. public function getAllUsers($includeGroups = false)
  8107. {
  8108. $response = [
  8109. array(
  8110. 'function' => 'fetchAll',
  8111. 'query' => array(
  8112. 'SELECT * FROM users'
  8113. ),
  8114. 'key' => 'users'
  8115. ),
  8116. ];
  8117. $groups = array(
  8118. 'function' => 'fetchAll',
  8119. 'query' => array(
  8120. 'SELECT * FROM groups ORDER BY group_id ASC'
  8121. ),
  8122. 'key' => 'groups'
  8123. );
  8124. $addGroups = (isset($_GET['includeGroups']) || $includeGroups) ?? false;
  8125. if ($addGroups) {
  8126. array_push($response, $groups);
  8127. }
  8128. return $this->processQueries($response);
  8129. }
  8130. public function getAllGroups()
  8131. {
  8132. $response = [
  8133. array(
  8134. 'function' => 'fetchAll',
  8135. 'query' => array(
  8136. 'SELECT * FROM groups ORDER BY group_id ASC'
  8137. ),
  8138. 'key' => 'groups'
  8139. ),
  8140. ];
  8141. $users = array(
  8142. 'function' => 'fetchAll',
  8143. 'query' => array(
  8144. 'SELECT * FROM users'
  8145. ),
  8146. 'key' => 'users'
  8147. );
  8148. $addUsers = (isset($_GET['includeUsers'])) ?? false;
  8149. if ($addUsers) {
  8150. array_push($response, $users);
  8151. }
  8152. return $this->processQueries($response);
  8153. }
  8154. public function importUsers($array)
  8155. {
  8156. $imported = 0;
  8157. foreach ($array as $user) {
  8158. $password = $this->random_ascii_string(30);
  8159. if ($user['username'] !== '' && $user['email'] !== '' && $password !== '') {
  8160. $newUser = $this->createUser($user['username'], $password, $user['email']);
  8161. if (!$newUser) {
  8162. $this->writeLog('error', 'Import Function - Error', $user['username']);
  8163. } else {
  8164. $imported++;
  8165. }
  8166. }
  8167. }
  8168. $this->setAPIResponse('success', 'Imported ' . $imported . ' users', 200);
  8169. return true;
  8170. }
  8171. public function importUsersType($type)
  8172. {
  8173. if ($type !== '') {
  8174. switch ($type) {
  8175. case 'plex':
  8176. return $this->importUsers($this->allPlexUsers(true));
  8177. case 'jellyfin':
  8178. return $this->importUsers($this->allJellyfinUsers(true));
  8179. case 'emby':
  8180. return $this->importUsers($this->allEmbyUsers(true));
  8181. default:
  8182. return false;
  8183. }
  8184. }
  8185. return false;
  8186. }
  8187. public function allPlexUsers($newOnly = false, $friendsOnly = false)
  8188. {
  8189. try {
  8190. if (!empty($this->config['plexToken'])) {
  8191. $url = 'https://plex.tv/api/users';
  8192. $headers = array(
  8193. 'X-Plex-Token' => $this->config['plexToken'],
  8194. );
  8195. $response = Requests::get($url, $headers);
  8196. if ($response->success) {
  8197. libxml_use_internal_errors(true);
  8198. $userXML = simplexml_load_string($response->body);
  8199. if (is_array($userXML) || is_object($userXML)) {
  8200. $results = array();
  8201. foreach ($userXML as $child) {
  8202. if (((string)$child['restricted'] == '0')) {
  8203. if ($newOnly) {
  8204. $taken = $this->usernameTaken((string)$child['username'], (string)$child['email']);
  8205. if (!$taken) {
  8206. $results[] = array(
  8207. 'username' => (string)$child['username'],
  8208. 'email' => (string)$child['email'],
  8209. 'id' => (string)$child['id'],
  8210. );
  8211. }
  8212. } elseif ($friendsOnly) {
  8213. $machineMatches = false;
  8214. foreach ($child->Server as $server) {
  8215. if ((string)$server['machineIdentifier'] == $this->config['plexID']) {
  8216. $machineMatches = true;
  8217. }
  8218. }
  8219. if ($machineMatches) {
  8220. $results[] = array(
  8221. 'username' => (string)$child['username'],
  8222. 'email' => (string)$child['email'],
  8223. 'id' => (string)$child['id'],
  8224. );
  8225. }
  8226. } else {
  8227. $results[] = array(
  8228. 'username' => (string)$child['username'],
  8229. 'email' => (string)$child['email'],
  8230. 'id' => (string)$child['id'],
  8231. );
  8232. }
  8233. }
  8234. }
  8235. return $results;
  8236. }
  8237. }
  8238. }
  8239. return false;
  8240. } catch (Requests_Exception $e) {
  8241. $this->writeLog('success', 'Plex Import User Function - Error: ' . $e->getMessage(), 'SYSTEM');
  8242. }
  8243. return false;
  8244. }
  8245. public function allJellyfinUsers($newOnly = false)
  8246. {
  8247. try {
  8248. if (!empty($this->config['jellyfinURL']) && !empty($this->config['jellyfinToken'])) {
  8249. $url = $this->qualifyURL($this->config['jellyfinURL']) . '/Users?api_key=' . $this->config['jellyfinToken'];
  8250. $headers = array();
  8251. $response = Requests::get($url, $headers);
  8252. if ($response->success) {
  8253. $users = json_decode($response->body, true);
  8254. if (is_array($users) || is_object($users)) {
  8255. $results = array();
  8256. foreach ($users as $child) {
  8257. // Jellyfin doesn't list emails for some reason
  8258. $email = $this->random_ascii_string(10) . '@placeholder.eml';
  8259. if ($newOnly) {
  8260. $taken = $this->usernameTaken((string)$child['Name'], $email);
  8261. if (!$taken) {
  8262. $results[] = array(
  8263. 'username' => (string)$child['Name'],
  8264. 'email' => $email
  8265. );
  8266. }
  8267. } else {
  8268. $results[] = array(
  8269. 'username' => (string)$child['Name'],
  8270. 'email' => $email,
  8271. );
  8272. }
  8273. }
  8274. return $results;
  8275. }
  8276. }
  8277. }
  8278. return false;
  8279. } catch (Requests_Exception $e) {
  8280. $this->writeLog('success', 'Jellyfin Import User Function - Error: ' . $e->getMessage(), 'SYSTEM');
  8281. }
  8282. return false;
  8283. }
  8284. public function allEmbyUsers($newOnly = false)
  8285. {
  8286. try {
  8287. if (!empty($this->config['embyURL']) && !empty($this->config['embyToken'])) {
  8288. $url = $this->qualifyURL($this->config['embyURL']) . '/Users?api_key=' . $this->config['embyToken'];
  8289. $headers = array();
  8290. $response = Requests::get($url, $headers);
  8291. if ($response->success) {
  8292. $users = json_decode($response->body, true);
  8293. if (is_array($users) || is_object($users)) {
  8294. $results = array();
  8295. foreach ($users as $child) {
  8296. // Emby doesn't list emails for some reason
  8297. $email = $this->random_ascii_string(10) . '@placeholder.eml';
  8298. if ($newOnly) {
  8299. $taken = $this->usernameTaken((string)$child['Name'], $email);
  8300. if (!$taken) {
  8301. $results[] = array(
  8302. 'username' => (string)$child['Name'],
  8303. 'email' => $email
  8304. );
  8305. }
  8306. } else {
  8307. $results[] = array(
  8308. 'username' => (string)$child['Name'],
  8309. 'email' => $email,
  8310. );
  8311. }
  8312. }
  8313. return $results;
  8314. }
  8315. }
  8316. }
  8317. return false;
  8318. } catch (Requests_Exception $e) {
  8319. $this->writeLog('success', 'Emby Import User Function - Error: ' . $e->getMessage(), 'SYSTEM');
  8320. }
  8321. return false;
  8322. }
  8323. public function updateUser($id, $array)
  8324. {
  8325. if (!$id) {
  8326. $this->setAPIResponse('error', 'Id was not supplied', 422);
  8327. return false;
  8328. }
  8329. if ($id !== $this->user['userID']) {
  8330. if (!$this->qualifyRequest('1', true)) {
  8331. return false;
  8332. }
  8333. }
  8334. $user = $this->getUserById($id);
  8335. if ($user) {
  8336. $array = $this->checkKeys($user, $array);
  8337. } else {
  8338. $this->setAPIResponse('error', 'User was not found', 404);
  8339. return false;
  8340. }
  8341. if ($user['group_id'] == 0 && $this->user['groupID'] !== 0) {
  8342. $this->setAPIResponse('error', 'Cannot update admin unless you are admin', 401);
  8343. return false;
  8344. }
  8345. if (array_key_exists('username', $array)) {
  8346. if ($array['username'] == '') {
  8347. $this->setAPIResponse('error', 'Username was set but empty', 409);
  8348. return false;
  8349. }
  8350. if ($this->usernameTaken($array['username'], $array['username'], $id)) {
  8351. $this->setAPIResponse('error', 'Username: ' . $array['username'] . ' is already taken', 409);
  8352. return false;
  8353. }
  8354. }
  8355. if (array_key_exists('email', $array)) {
  8356. if ($array['email'] == '') {
  8357. $this->setAPIResponse('error', 'Email was set but empty', 409);
  8358. return false;
  8359. }
  8360. if ($this->usernameTaken($array['email'], $array['email'], $id)) {
  8361. $this->setAPIResponse('error', 'Email: ' . $array['email'] . ' is already taken', 409);
  8362. return false;
  8363. }
  8364. }
  8365. if (array_key_exists('group_id', $array)) {
  8366. if ($array['group_id'] == '') {
  8367. $this->setAPIResponse('error', 'group_id was set but empty', 409);
  8368. return false;
  8369. }
  8370. if (!$this->qualifyRequest('1', false)) {
  8371. $this->setAPIResponse('error', 'Cannot change your own group_id', 401);
  8372. return false;
  8373. }
  8374. if (($id == $this->user['userID']) && $this->user['groupID'] == 0) {
  8375. $array['group_id'] = 0;
  8376. }
  8377. if (($id == $this->user['userID']) && ($array['group_id'] == 0 && $this->user['groupID'] !== 0)) {
  8378. $this->setAPIResponse('error', 'Only admins can make others admins', 401);
  8379. return false;
  8380. }
  8381. $array['group'] = $this->getGroupByGroupId($array['group_id'])['group'];
  8382. if (!$array['group']) {
  8383. $this->setAPIResponse('error', 'group_id does not exist', 404);
  8384. return false;
  8385. }
  8386. }
  8387. if (array_key_exists('locked', $array)) {
  8388. $this->setAPIResponse('error', 'Cannot use endpoint to unlock or lock user - please use /users/{id}/lock', 409);
  8389. return false;
  8390. }
  8391. if (array_key_exists('password', $array)) {
  8392. if ($array['password'] == '') {
  8393. $this->setAPIResponse('error', 'Password was set but empty', 409);
  8394. return false;
  8395. }
  8396. $array['password'] = password_hash($array['password'], PASSWORD_BCRYPT);
  8397. }
  8398. if (array_key_exists('register_date', $array)) {
  8399. $this->setAPIResponse('error', 'Cannot update register date', 409);
  8400. return false;
  8401. }
  8402. $response = [
  8403. array(
  8404. 'function' => 'query',
  8405. 'query' => array(
  8406. 'UPDATE users SET',
  8407. $array,
  8408. 'WHERE id = ?',
  8409. $id
  8410. )
  8411. ),
  8412. ];
  8413. $this->setAPIResponse(null, 'User info updated');
  8414. $this->writeLog('success', 'User Editor Function - Updated User Info for [' . $user['username'] . ']', $this->user['username']);
  8415. return $this->processQueries($response);
  8416. }
  8417. public function deleteUser($id)
  8418. {
  8419. $response = [
  8420. array(
  8421. 'function' => 'query',
  8422. 'query' => array(
  8423. 'DELETE FROM users WHERE id = ?',
  8424. $id
  8425. )
  8426. ),
  8427. ];
  8428. $userInfo = $this->getUserById($id);
  8429. if ($userInfo) {
  8430. $this->writeLog('success', 'User Delete Function - Deleted User [' . $userInfo['username'] . ']', $this->user['username']);
  8431. $this->setAPIResponse('success', 'User deleted', 204);
  8432. return $this->processQueries($response);
  8433. } else {
  8434. $this->setAPIResponse('error', 'id not found', 404);
  8435. return false;
  8436. }
  8437. }
  8438. public function addUser($array)
  8439. {
  8440. $username = $array['username'] ?? null;
  8441. $password = $array['password'] ?? null;
  8442. $email = $array['email'] ?? null;
  8443. if (!$username) {
  8444. $this->setAPIResponse('error', 'Username was not supplied', 409);
  8445. return false;
  8446. }
  8447. if (!$password) {
  8448. $this->setAPIResponse('error', 'Password was not supplied', 409);
  8449. return false;
  8450. }
  8451. if ($this->createUser($username, $password, $email)) {
  8452. $this->writeLog('success', 'Create User Function - Account created for [' . $username . ']', $this->user['username']);
  8453. return true;
  8454. } else {
  8455. $this->writeLog('error', 'Create User Function - An error occurred', $this->user['username']);
  8456. return false;
  8457. }
  8458. }
  8459. public function createUser($username, $password, $email = null)
  8460. {
  8461. $username = $username ?? null;
  8462. $password = $password ?? null;
  8463. $email = ($email) ? $email : $this->random_ascii_string(10) . '@placeholder.eml';
  8464. if (!$username) {
  8465. $this->setAPIResponse('error', 'Username was set but empty', 409);
  8466. return false;
  8467. }
  8468. if (!$password) {
  8469. $this->setAPIResponse('error', 'Password was set but empty', 409);
  8470. return false;
  8471. }
  8472. if ($this->usernameTaken($username, $email)) {
  8473. $this->setAPIResponse('error', 'Username: ' . $username . ' or Email: ' . $email . ' is already taken', 409);
  8474. return false;
  8475. }
  8476. $defaults = $this->getDefaultGroup();
  8477. $userInfo = [
  8478. 'username' => $username,
  8479. 'password' => password_hash($password, PASSWORD_BCRYPT),
  8480. 'email' => $email,
  8481. 'group' => $defaults['group'],
  8482. 'group_id' => $defaults['group_id'],
  8483. 'image' => $this->gravatar($email),
  8484. 'register_date' => $this->currentTime,
  8485. ];
  8486. $response = [
  8487. array(
  8488. 'function' => 'query',
  8489. 'query' => array(
  8490. 'INSERT INTO [users]',
  8491. $userInfo
  8492. )
  8493. ),
  8494. ];
  8495. $this->setAPIResponse('success', 'User created', 200);
  8496. return $this->processQueries($response);
  8497. }
  8498. public function updateGroup($id, $array)
  8499. {
  8500. if (!$id || $id == '') {
  8501. $this->setAPIResponse('error', 'id was not set', 422);
  8502. return null;
  8503. }
  8504. if (!$array) {
  8505. $this->setAPIResponse('error', 'no data was sent', 422);
  8506. return null;
  8507. }
  8508. $groupInfo = $this->getGroupById($id);
  8509. if ($groupInfo) {
  8510. $array = $this->checkKeys($groupInfo, $array);
  8511. } else {
  8512. $this->setAPIResponse('error', 'No category info found', 404);
  8513. return false;
  8514. }
  8515. if (array_key_exists('group_id', $array)) {
  8516. $this->setAPIResponse('error', 'Cannot change group_id', 409);
  8517. return false;
  8518. }
  8519. if (array_key_exists('group', $array)) {
  8520. if ($array['group'] == '') {
  8521. $this->setAPIResponse('error', 'Group was set but empty', 409);
  8522. return false;
  8523. }
  8524. if ($this->isGroupNameTaken($array['group'], $id)) {
  8525. $this->setAPIResponse('error', 'Group name: ' . $array['group'] . ' is already taken', 409);
  8526. return false;
  8527. }
  8528. }
  8529. if (array_key_exists('image', $array)) {
  8530. if ($array['image'] == '') {
  8531. $this->setAPIResponse('error', 'Image was set but empty', 409);
  8532. return false;
  8533. }
  8534. }
  8535. if (array_key_exists('default', $array)) {
  8536. if ($groupInfo['group_id'] == 0 || $groupInfo['group_id'] == 999) {
  8537. $this->setAPIResponse('error', 'Setting ' . $groupInfo['group'] . ' as default group is not allowed', 409);
  8538. return false;
  8539. }
  8540. if ($array['default']) {
  8541. $this->clearGroupDefault();
  8542. $array['default'] = 1;
  8543. }
  8544. }
  8545. $response = [
  8546. array(
  8547. 'function' => 'query',
  8548. 'query' => array(
  8549. 'UPDATE groups SET',
  8550. $array,
  8551. 'WHERE id = ?',
  8552. $id
  8553. )
  8554. ),
  8555. ];
  8556. $this->setAPIResponse(null, 'Group info updated');
  8557. $this->writeLog('success', 'Group Editor Function - Edited Group Info for [' . $groupInfo['group'] . ']', $this->user['username']);
  8558. return $this->processQueries($response);
  8559. }
  8560. public function deleteGroup($id)
  8561. {
  8562. $response = [
  8563. array(
  8564. 'function' => 'query',
  8565. 'query' => array(
  8566. 'DELETE FROM groups WHERE id = ?',
  8567. $id
  8568. )
  8569. ),
  8570. ];
  8571. $groupInfo = $this->getGroupById($id);
  8572. if ($groupInfo['group_id'] == 0 || $groupInfo['group_id'] == 999) {
  8573. $this->setAPIResponse('error', 'Cannot delete group: ' . $groupInfo['group'] . ' as it is not allowed', 409);
  8574. return false;
  8575. }
  8576. if ($this->getGroupUserCountById($id) >= 1) {
  8577. $this->setAPIResponse('error', 'Cannot delete group as group still has users assigned to it', 409);
  8578. return false;
  8579. }
  8580. if ($groupInfo) {
  8581. $this->writeLog('success', 'Group Delete Function - Deleted Group [' . $groupInfo['group'] . ']', $this->user['username']);
  8582. $this->setAPIResponse('success', 'Group deleted', 204);
  8583. return $this->processQueries($response);
  8584. } else {
  8585. $this->setAPIResponse('error', 'id not found', 404);
  8586. return false;
  8587. }
  8588. }
  8589. public function addGroup($array)
  8590. {
  8591. if (!$array) {
  8592. $this->setAPIResponse('error', 'no data was sent', 422);
  8593. return null;
  8594. }
  8595. $array = $this->checkKeys($this->getTableColumnsFormatted('groups'), $array);
  8596. $array['default'] = ($array['default']) ?? 0;
  8597. $array['group_id'] = $this->getNextGroupOrder() + 1;
  8598. if (array_key_exists('group', $array)) {
  8599. if ($this->isGroupNameTaken($array['group'])) {
  8600. $this->setAPIResponse('error', 'Group name: ' . $array['group'] . ' is already taken', 409);
  8601. return false;
  8602. }
  8603. } else {
  8604. $this->setAPIResponse('error', 'Group name was not supplied', 422);
  8605. return false;
  8606. }
  8607. if (array_key_exists('image', $array)) {
  8608. if ($array['image'] == '') {
  8609. $this->setAPIResponse('error', 'Group image cannot be empty', 422);
  8610. return false;
  8611. }
  8612. } else {
  8613. $this->setAPIResponse('error', 'Group image was not supplied', 422);
  8614. return false;
  8615. }
  8616. $response = [
  8617. array(
  8618. 'function' => 'query',
  8619. 'query' => array(
  8620. 'INSERT INTO [groups]',
  8621. $array
  8622. )
  8623. ),
  8624. ];
  8625. $this->setAPIResponse(null, 'Tab added');
  8626. $this->writeLog('success', 'Tab Editor Function - Added Tab for [' . $array['name'] . ']', $this->user['username']);
  8627. return $this->processQueries($response);
  8628. }
  8629. public function userList($type = null)
  8630. {
  8631. switch ($type) {
  8632. case 'plex':
  8633. if (!empty($this->config['plexToken']) && !empty($this->config['plexID'])) {
  8634. $url = 'https://plex.tv/api/servers/' . $this->config['plexID'] . '/shared_servers';
  8635. try {
  8636. $headers = array(
  8637. "Accept" => "application/json",
  8638. "X-Plex-Token" => $this->config['plexToken']
  8639. );
  8640. $response = Requests::get($url, $headers, array());
  8641. libxml_use_internal_errors(true);
  8642. if ($response->success) {
  8643. $libraryList = array();
  8644. $plex = simplexml_load_string($response->body);
  8645. foreach ($plex->SharedServer as $child) {
  8646. if (!empty($child['username'])) {
  8647. $username = (string)strtolower($child['username']);
  8648. $email = (string)strtolower($child['email']);
  8649. $libraryList['users'][$username] = (string)$child['id'];
  8650. $libraryList['emails'][$email] = (string)$child['id'];
  8651. $libraryList['both'][$username] = $email;
  8652. }
  8653. }
  8654. $libraryList = array_change_key_case($libraryList, CASE_LOWER);
  8655. return $libraryList;
  8656. }
  8657. } catch (Requests_Exception $e) {
  8658. $this->writeLog('error', 'Plex Connect Function - Error: ' . $e->getMessage(), 'SYSTEM');
  8659. }
  8660. }
  8661. break;
  8662. default:
  8663. # code...
  8664. break;
  8665. }
  8666. return false;
  8667. }
  8668. public function encrypt($password, $key = null)
  8669. {
  8670. $key = ($key) ? $key : ((isset($this->config['organizrHash'])) ? $this->config['organizrHash'] : null);
  8671. return openssl_encrypt($password, 'AES-256-CBC', $key, 0, $this->fillString($key, 16));
  8672. }
  8673. public function decrypt($password, $key = null)
  8674. {
  8675. if (empty($password)) {
  8676. return '';
  8677. }
  8678. $key = ($key) ? $key : ((isset($this->config['organizrHash'])) ? $this->config['organizrHash'] : null);
  8679. return openssl_decrypt($password, 'AES-256-CBC', $key, 0, $this->fillString($key, 16));
  8680. }
  8681. public function getCert()
  8682. {
  8683. $url = 'http://curl.haxx.se/ca/cacert.pem';
  8684. $file = dirname(__DIR__, 1) . DIRECTORY_SEPARATOR . 'functions' . DIRECTORY_SEPARATOR . 'cert' . DIRECTORY_SEPARATOR . 'cacert.pem';
  8685. $file2 = dirname(__DIR__, 1) . DIRECTORY_SEPARATOR . 'functions' . DIRECTORY_SEPARATOR . 'cert' . DIRECTORY_SEPARATOR . 'cacert-initial.pem';
  8686. $useCert = (file_exists($file)) ? $file : $file2;
  8687. if ($this->config['selfSignedCert'] !== '') {
  8688. if (file_exists($this->config['selfSignedCert'])) {
  8689. return $this->config['selfSignedCert'];
  8690. }
  8691. }
  8692. $context = stream_context_create(
  8693. array(
  8694. 'ssl' => array(
  8695. 'verify_peer' => true,
  8696. 'cafile' => $useCert
  8697. )
  8698. )
  8699. );
  8700. if (!file_exists($file)) {
  8701. file_put_contents($file, fopen($url, 'r', false, $context));
  8702. } elseif (file_exists($file) && time() - 2592000 > filemtime($file)) {
  8703. file_put_contents($file, fopen($url, 'r', false, $context));
  8704. }
  8705. return $file;
  8706. }
  8707. public function plexJoinAPI($array)
  8708. {
  8709. $username = ($array['username']) ?? null;
  8710. $email = ($array['email']) ?? null;
  8711. $password = ($array['password']) ?? null;
  8712. if (!$username) {
  8713. $this->setAPIResponse('error', 'Username not supplied', 409);
  8714. return false;
  8715. }
  8716. if (!$email) {
  8717. $this->setAPIResponse('error', 'Email not supplied', 409);
  8718. return false;
  8719. }
  8720. if (!$password) {
  8721. $this->setAPIResponse('error', 'Password not supplied', 409);
  8722. return false;
  8723. }
  8724. return $this->plexJoin($username, $email, $password);
  8725. }
  8726. public function plexJoin($username, $email, $password)
  8727. {
  8728. try {
  8729. $url = 'https://plex.tv/api/v2/users';
  8730. $headers = array(
  8731. 'Accept' => 'application/json',
  8732. 'Content-Type' => 'application/x-www-form-urlencoded',
  8733. 'X-Plex-Product' => 'Organizr',
  8734. 'X-Plex-Version' => '2.0',
  8735. 'X-Plex-Client-Identifier' => $this->config['uuid'],
  8736. );
  8737. $data = array(
  8738. 'email' => $email,
  8739. 'username' => $username,
  8740. 'password' => $password,
  8741. );
  8742. $response = Requests::post($url, $headers, $data, array());
  8743. $json = json_decode($response->body, true);
  8744. $errors = !empty($json['errors']);
  8745. $success = !empty($json['user']);
  8746. //Use This for later
  8747. $errorMessage = "";
  8748. if ($errors) {
  8749. foreach ($json['errors'] as $error) {
  8750. if (isset($error['message']) && isset($error['field'])) {
  8751. $errorMessage .= "[Plex.tv Error: " . $error['message'] . " for field: (" . $error['field'] . ")]";
  8752. }
  8753. }
  8754. }
  8755. $msg = (!empty($success) && empty($errors)) ? 'User has joined Plex' : $errorMessage;
  8756. $status = (!empty($success) && empty($errors)) ? 'success' : 'error';
  8757. $code = (!empty($success) && empty($errors)) ? 200 : 422;
  8758. $this->setAPIResponse($status, $msg, $code);
  8759. return (!empty($success) && empty($errors));
  8760. } catch (Requests_Exception $e) {
  8761. $this->writeLog('error', 'Plex.TV Connect Function - Error: ' . $e->getMessage(), 'SYSTEM');
  8762. $this->setAPIResponse('error', 'An Error Occurred', 409);
  8763. return false;
  8764. }
  8765. return false;
  8766. }
  8767. public function lockCurrentUser()
  8768. {
  8769. if ($this->user['userID'] == '999') {
  8770. $this->setAPIResponse('error', 'Locking not allowed on Guest users', 409);
  8771. return false;
  8772. }
  8773. return $this->lockUser($this->user['userID']);
  8774. }
  8775. public function lockUser($id)
  8776. {
  8777. $user = $this->getUserById($id);
  8778. if (!$user) {
  8779. $this->setAPIResponse('error', 'User not found', 404);
  8780. return false;
  8781. }
  8782. $response = [
  8783. array(
  8784. 'function' => 'query',
  8785. 'query' => array(
  8786. 'UPDATE users SET',
  8787. ['locked' => '1'],
  8788. 'WHERE id = ?',
  8789. $id
  8790. )
  8791. ),
  8792. ];
  8793. $this->writeLog('success', 'User Lockout Function - User: ' . $user['username'] . ' account locked', $this->user['username']);
  8794. $this->setAPIResponse('success', 'User account locked', 200);
  8795. return $this->processQueries($response);
  8796. }
  8797. public function unlockCurrentUser($array)
  8798. {
  8799. if ($array['password'] == '') {
  8800. $this->setAPIResponse('error', 'Password Not Set', 422);
  8801. return false;
  8802. }
  8803. $user = $this->getUserById($this->user['userID']);
  8804. if (!password_verify($array['password'], $user['password'])) {
  8805. $this->setAPIResponse('error', 'Password Incorrect', 401);
  8806. return false;
  8807. }
  8808. return $this->unlockUser($this->user['userID']);
  8809. }
  8810. public function unlockUser($id)
  8811. {
  8812. $user = $this->getUserById($id);
  8813. if (!$user) {
  8814. $this->setAPIResponse('error', 'User not found', 404);
  8815. return false;
  8816. }
  8817. $response = [
  8818. array(
  8819. 'function' => 'query',
  8820. 'query' => array(
  8821. 'UPDATE users SET',
  8822. ['locked' => '0'],
  8823. 'WHERE id = ?',
  8824. $id
  8825. )
  8826. ),
  8827. ];
  8828. $this->writeLog('success', 'User Lockout Function - User: ' . $user['username'] . ' account unlocked', $this->user['username']);
  8829. $this->setAPIResponse('success', 'User account unlocked', 200);
  8830. return $this->processQueries($response);
  8831. }
  8832. public function youtubeSearch($query)
  8833. {
  8834. if (!$query) {
  8835. $this->setAPIResponse('error', 'No query supplied', 422);
  8836. return false;
  8837. }
  8838. $keys = array(
  8839. 'AIzaSyBsdt8nLJRMTwOq5PY5A5GLZ2q7scgn01w',
  8840. 'AIzaSyD-8SHutB60GCcSM8q_Fle38rJUV7ujd8k',
  8841. 'AIzaSyBzOpVBT6VII-b-8gWD0MOEosGg4hyhCsQ',
  8842. 'AIzaSyBKnRe1P8fpfBHgooJpmT0WOsrdUtZ4cpk'
  8843. );
  8844. $randomKeyIndex = array_rand($keys);
  8845. $key = $keys[$randomKeyIndex];
  8846. $apikey = ($this->config['youtubeAPI'] !== '') ? $this->config['youtubeAPI'] : $key;
  8847. $results = false;
  8848. $url = "https://www.googleapis.com/youtube/v3/search?part=snippet&q=$query+official+trailer&part=snippet&maxResults=1&type=video&videoDuration=short&key=$apikey";
  8849. $response = Requests::get($url);
  8850. if ($response->success) {
  8851. $results = json_decode($response->body, true);
  8852. $this->setAPIResponse('success', null, 200, $results);
  8853. return $results;
  8854. } else {
  8855. $this->setAPIResponse('error', 'Bad response from YouTube', 500);
  8856. return false;
  8857. }
  8858. }
  8859. public function scrapePage($array)
  8860. {
  8861. try {
  8862. $url = $array['url'] ?? false;
  8863. $type = $array['type'] ?? false;
  8864. if (!$url) {
  8865. $this->setAPIResponse('error', 'URL was not supplied', 422);
  8866. return false;
  8867. }
  8868. $url = $this->qualifyURL($url);
  8869. $data = array(
  8870. 'full_url' => $url,
  8871. 'drill_url' => $this->qualifyURL($url, true)
  8872. );
  8873. $options = array('verify' => false);
  8874. $response = Requests::get($url, array(), $options);
  8875. $data['response_code'] = $response->status_code;
  8876. if ($response->success) {
  8877. $data['result'] = 'Success';
  8878. switch ($type) {
  8879. case 'html':
  8880. $data['data'] = html_entity_decode($response->body);
  8881. break;
  8882. case 'json':
  8883. $data['data'] = json_decode($response->body);
  8884. break;
  8885. default:
  8886. $data['data'] = $response->body;
  8887. }
  8888. $this->setAPIResponse('success', null, 200, $data);
  8889. return $data;
  8890. } else {
  8891. $this->setAPIResponse('error', 'Error getting successful response', 500);
  8892. return false;
  8893. }
  8894. } catch (Requests_Exception $e) {
  8895. $this->setAPIResponse('error', $e->getMessage(), 500);
  8896. return false;
  8897. }
  8898. }
  8899. protected function processQueries(array $request, $migration = false)
  8900. {
  8901. $results = array();
  8902. $firstKey = '';
  8903. try {
  8904. foreach ($request as $k => $v) {
  8905. $query = ($migration) ? $this->otherDb->query($v['query']) : $this->db->query($v['query']);
  8906. $keyName = (isset($v['key'])) ? $v['key'] : $k;
  8907. $firstKey = (isset($v['key']) && $k == 0) ? $v['key'] : $k;
  8908. switch ($v['function']) {
  8909. case 'fetchAll':
  8910. $results[$keyName] = $query->fetchAll();
  8911. break;
  8912. case 'fetch':
  8913. $results[$keyName] = $query->fetch();
  8914. break;
  8915. case 'getAffectedRows':
  8916. $results[$keyName] = $query->getAffectedRows();
  8917. break;
  8918. case 'getRowCount':
  8919. $results[$keyName] = $query->getRowCount();
  8920. break;
  8921. case 'fetchSingle':
  8922. $results[$keyName] = $query->fetchSingle();
  8923. break;
  8924. case 'query':
  8925. $results[$keyName] = $query;
  8926. break;
  8927. default:
  8928. return false;
  8929. }
  8930. }
  8931. } catch (Exception $e) {
  8932. return $e;
  8933. }
  8934. return count($request) > 1 ? $results : $results[$firstKey];
  8935. }
  8936. }