learnpath.class.php 332 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869487048714872487348744875487648774878487948804881488248834884488548864887488848894890489148924893489448954896489748984899490049014902490349044905490649074908490949104911491249134914491549164917491849194920492149224923492449254926492749284929493049314932493349344935493649374938493949404941494249434944494549464947494849494950495149524953495449554956495749584959496049614962496349644965496649674968496949704971497249734974497549764977497849794980498149824983498449854986498749884989499049914992499349944995499649974998499950005001500250035004500550065007500850095010501150125013501450155016501750185019502050215022502350245025502650275028502950305031503250335034503550365037503850395040504150425043504450455046504750485049505050515052505350545055505650575058505950605061506250635064506550665067506850695070507150725073507450755076507750785079508050815082508350845085508650875088508950905091509250935094509550965097509850995100510151025103510451055106510751085109511051115112511351145115511651175118511951205121512251235124512551265127512851295130513151325133513451355136513751385139514051415142514351445145514651475148514951505151515251535154515551565157515851595160516151625163516451655166516751685169517051715172517351745175517651775178517951805181518251835184518551865187518851895190519151925193519451955196519751985199520052015202520352045205520652075208520952105211521252135214521552165217521852195220522152225223522452255226522752285229523052315232523352345235523652375238523952405241524252435244524552465247524852495250525152525253525452555256525752585259526052615262526352645265526652675268526952705271527252735274527552765277527852795280528152825283528452855286528752885289529052915292529352945295529652975298529953005301530253035304530553065307530853095310531153125313531453155316531753185319532053215322532353245325532653275328532953305331533253335334533553365337533853395340534153425343534453455346534753485349535053515352535353545355535653575358535953605361536253635364536553665367536853695370537153725373537453755376537753785379538053815382538353845385538653875388538953905391539253935394539553965397539853995400540154025403540454055406540754085409541054115412541354145415541654175418541954205421542254235424542554265427542854295430543154325433543454355436543754385439544054415442544354445445544654475448544954505451545254535454545554565457545854595460546154625463546454655466546754685469547054715472547354745475547654775478547954805481548254835484548554865487548854895490549154925493549454955496549754985499550055015502550355045505550655075508550955105511551255135514551555165517551855195520552155225523552455255526552755285529553055315532553355345535553655375538553955405541554255435544554555465547554855495550555155525553555455555556555755585559556055615562556355645565556655675568556955705571557255735574557555765577557855795580558155825583558455855586558755885589559055915592559355945595559655975598559956005601560256035604560556065607560856095610561156125613561456155616561756185619562056215622562356245625562656275628562956305631563256335634563556365637563856395640564156425643564456455646564756485649565056515652565356545655565656575658565956605661566256635664566556665667566856695670567156725673567456755676567756785679568056815682568356845685568656875688568956905691569256935694569556965697569856995700570157025703570457055706570757085709571057115712571357145715571657175718571957205721572257235724572557265727572857295730573157325733573457355736573757385739574057415742574357445745574657475748574957505751575257535754575557565757575857595760576157625763576457655766576757685769577057715772577357745775577657775778577957805781578257835784578557865787578857895790579157925793579457955796579757985799580058015802580358045805580658075808580958105811581258135814581558165817581858195820582158225823582458255826582758285829583058315832583358345835583658375838583958405841584258435844584558465847584858495850585158525853585458555856585758585859586058615862586358645865586658675868586958705871587258735874587558765877587858795880588158825883588458855886588758885889589058915892589358945895589658975898589959005901590259035904590559065907590859095910591159125913591459155916591759185919592059215922592359245925592659275928592959305931593259335934593559365937593859395940594159425943594459455946594759485949595059515952595359545955595659575958595959605961596259635964596559665967596859695970597159725973597459755976597759785979598059815982598359845985598659875988598959905991599259935994599559965997599859996000600160026003600460056006600760086009601060116012601360146015601660176018601960206021602260236024602560266027602860296030603160326033603460356036603760386039604060416042604360446045604660476048604960506051605260536054605560566057605860596060606160626063606460656066606760686069607060716072607360746075607660776078607960806081608260836084608560866087608860896090609160926093609460956096609760986099610061016102610361046105610661076108610961106111611261136114611561166117611861196120612161226123612461256126612761286129613061316132613361346135613661376138613961406141614261436144614561466147614861496150615161526153615461556156615761586159616061616162616361646165616661676168616961706171617261736174617561766177617861796180618161826183618461856186618761886189619061916192619361946195619661976198619962006201620262036204620562066207620862096210621162126213621462156216621762186219622062216222622362246225622662276228622962306231623262336234623562366237623862396240624162426243624462456246624762486249625062516252625362546255625662576258625962606261626262636264626562666267626862696270627162726273627462756276627762786279628062816282628362846285628662876288628962906291629262936294629562966297629862996300630163026303630463056306630763086309631063116312631363146315631663176318631963206321632263236324632563266327632863296330633163326333633463356336633763386339634063416342634363446345634663476348634963506351635263536354635563566357635863596360636163626363636463656366636763686369637063716372637363746375637663776378637963806381638263836384638563866387638863896390639163926393639463956396639763986399640064016402640364046405640664076408640964106411641264136414641564166417641864196420642164226423642464256426642764286429643064316432643364346435643664376438643964406441644264436444644564466447644864496450645164526453645464556456645764586459646064616462646364646465646664676468646964706471647264736474647564766477647864796480648164826483648464856486648764886489649064916492649364946495649664976498649965006501650265036504650565066507650865096510651165126513651465156516651765186519652065216522652365246525652665276528652965306531653265336534653565366537653865396540654165426543654465456546654765486549655065516552655365546555655665576558655965606561656265636564656565666567656865696570657165726573657465756576657765786579658065816582658365846585658665876588658965906591659265936594659565966597659865996600660166026603660466056606660766086609661066116612661366146615661666176618661966206621662266236624662566266627662866296630663166326633663466356636663766386639664066416642664366446645664666476648664966506651665266536654665566566657665866596660666166626663666466656666666766686669667066716672667366746675667666776678667966806681668266836684668566866687668866896690669166926693669466956696669766986699670067016702670367046705670667076708670967106711671267136714671567166717671867196720672167226723672467256726672767286729673067316732673367346735673667376738673967406741674267436744674567466747674867496750675167526753675467556756675767586759676067616762676367646765676667676768676967706771677267736774677567766777677867796780678167826783678467856786678767886789679067916792679367946795679667976798679968006801680268036804680568066807680868096810681168126813681468156816681768186819682068216822682368246825682668276828682968306831683268336834683568366837683868396840684168426843684468456846684768486849685068516852685368546855685668576858685968606861686268636864686568666867686868696870687168726873687468756876687768786879688068816882688368846885688668876888688968906891689268936894689568966897689868996900690169026903690469056906690769086909691069116912691369146915691669176918691969206921692269236924692569266927692869296930693169326933693469356936693769386939694069416942694369446945694669476948694969506951695269536954695569566957695869596960696169626963696469656966696769686969697069716972697369746975697669776978697969806981698269836984698569866987698869896990699169926993699469956996699769986999700070017002700370047005700670077008700970107011701270137014701570167017701870197020702170227023702470257026702770287029703070317032703370347035703670377038703970407041704270437044704570467047704870497050705170527053705470557056705770587059706070617062706370647065706670677068706970707071707270737074707570767077707870797080708170827083708470857086708770887089709070917092709370947095709670977098709971007101710271037104710571067107710871097110711171127113711471157116711771187119712071217122712371247125712671277128712971307131713271337134713571367137713871397140714171427143714471457146714771487149715071517152715371547155715671577158715971607161716271637164716571667167716871697170717171727173717471757176717771787179718071817182718371847185718671877188718971907191719271937194719571967197719871997200720172027203720472057206720772087209721072117212721372147215721672177218721972207221722272237224722572267227722872297230723172327233723472357236723772387239724072417242724372447245724672477248724972507251725272537254725572567257725872597260726172627263726472657266726772687269727072717272727372747275727672777278727972807281728272837284728572867287728872897290729172927293729472957296729772987299730073017302730373047305730673077308730973107311731273137314731573167317731873197320732173227323732473257326732773287329733073317332733373347335733673377338733973407341734273437344734573467347734873497350735173527353735473557356735773587359736073617362736373647365736673677368736973707371737273737374737573767377737873797380738173827383738473857386738773887389739073917392739373947395739673977398739974007401740274037404740574067407740874097410741174127413741474157416741774187419742074217422742374247425742674277428742974307431743274337434743574367437743874397440744174427443744474457446744774487449745074517452745374547455745674577458745974607461746274637464746574667467746874697470747174727473747474757476747774787479748074817482748374847485748674877488748974907491749274937494749574967497749874997500750175027503750475057506750775087509751075117512751375147515751675177518751975207521752275237524752575267527752875297530753175327533753475357536753775387539754075417542754375447545754675477548754975507551755275537554755575567557755875597560756175627563756475657566756775687569757075717572757375747575757675777578757975807581758275837584758575867587758875897590759175927593759475957596759775987599760076017602760376047605760676077608760976107611761276137614761576167617761876197620762176227623762476257626762776287629763076317632763376347635763676377638763976407641764276437644764576467647764876497650765176527653765476557656765776587659766076617662766376647665766676677668766976707671767276737674767576767677767876797680768176827683768476857686768776887689769076917692769376947695769676977698769977007701770277037704770577067707770877097710771177127713771477157716771777187719772077217722772377247725772677277728772977307731773277337734773577367737773877397740774177427743774477457746774777487749775077517752775377547755775677577758775977607761776277637764776577667767776877697770777177727773777477757776777777787779778077817782778377847785778677877788778977907791779277937794779577967797779877997800780178027803780478057806780778087809781078117812781378147815781678177818781978207821782278237824782578267827782878297830783178327833783478357836783778387839784078417842784378447845784678477848784978507851785278537854785578567857785878597860786178627863786478657866786778687869787078717872787378747875787678777878787978807881788278837884788578867887788878897890789178927893789478957896789778987899790079017902790379047905790679077908790979107911791279137914791579167917791879197920792179227923792479257926792779287929793079317932793379347935793679377938793979407941794279437944794579467947794879497950795179527953795479557956795779587959796079617962796379647965796679677968796979707971797279737974797579767977797879797980798179827983798479857986798779887989799079917992799379947995799679977998799980008001800280038004800580068007800880098010801180128013801480158016801780188019802080218022802380248025802680278028802980308031803280338034803580368037803880398040804180428043804480458046804780488049805080518052805380548055805680578058805980608061806280638064806580668067806880698070807180728073807480758076807780788079808080818082808380848085808680878088808980908091809280938094809580968097809880998100810181028103810481058106810781088109811081118112811381148115811681178118811981208121812281238124812581268127812881298130813181328133813481358136813781388139814081418142814381448145814681478148814981508151815281538154815581568157815881598160816181628163816481658166816781688169817081718172817381748175817681778178817981808181818281838184818581868187818881898190819181928193819481958196819781988199820082018202820382048205820682078208820982108211821282138214821582168217821882198220822182228223822482258226822782288229823082318232823382348235823682378238823982408241824282438244824582468247824882498250825182528253825482558256825782588259826082618262826382648265826682678268826982708271827282738274827582768277827882798280828182828283828482858286828782888289829082918292829382948295829682978298829983008301830283038304830583068307830883098310831183128313831483158316831783188319832083218322832383248325832683278328832983308331833283338334833583368337833883398340834183428343834483458346834783488349835083518352835383548355835683578358835983608361836283638364836583668367836883698370837183728373837483758376837783788379838083818382838383848385838683878388838983908391839283938394839583968397839883998400840184028403840484058406840784088409841084118412841384148415841684178418841984208421842284238424842584268427842884298430843184328433843484358436843784388439844084418442844384448445844684478448844984508451845284538454845584568457845884598460846184628463846484658466846784688469847084718472847384748475847684778478847984808481848284838484848584868487848884898490849184928493849484958496849784988499850085018502850385048505850685078508850985108511851285138514851585168517851885198520852185228523852485258526852785288529853085318532853385348535853685378538853985408541854285438544854585468547854885498550855185528553855485558556855785588559856085618562856385648565856685678568856985708571857285738574857585768577857885798580858185828583858485858586858785888589859085918592859385948595859685978598859986008601860286038604860586068607860886098610861186128613861486158616861786188619862086218622862386248625862686278628862986308631863286338634863586368637863886398640864186428643864486458646864786488649865086518652865386548655865686578658865986608661866286638664866586668667866886698670867186728673867486758676867786788679868086818682868386848685868686878688868986908691869286938694869586968697869886998700870187028703870487058706870787088709871087118712871387148715871687178718871987208721872287238724872587268727872887298730873187328733873487358736873787388739874087418742874387448745874687478748874987508751875287538754875587568757875887598760876187628763876487658766876787688769877087718772877387748775877687778778877987808781878287838784878587868787878887898790879187928793879487958796879787988799880088018802880388048805880688078808880988108811881288138814881588168817881888198820882188228823882488258826882788288829883088318832883388348835883688378838883988408841884288438844884588468847884888498850885188528853885488558856885788588859886088618862886388648865886688678868886988708871887288738874887588768877887888798880888188828883888488858886888788888889889088918892889388948895889688978898889989008901890289038904890589068907890889098910891189128913891489158916891789188919892089218922892389248925892689278928892989308931893289338934893589368937893889398940
  1. <?php
  2. /* For licensing terms, see /dokeos_license.txt */
  3. /**
  4. * This (abstract?) class defines the parent attributes and methods for the dokeos learnpaths and scorm
  5. * learnpaths. It is used by the scorm class as well as the dokeos_lp class.
  6. * @package dokeos.learnpath
  7. * @author Yannick Warnier <ywarnier@beeznest.org>
  8. * @license GNU/GPL - See Dokeos license directory for details
  9. */
  10. /**
  11. * Defines the learnpath parent class
  12. * @package dokeos.learnpath
  13. */
  14. class learnpath {
  15. var $attempt = 0; //the number for the current ID view
  16. var $cc; //course (code) this learnpath is located in
  17. var $current; //id of the current item the user is viewing
  18. var $current_score; //the score of the current item
  19. var $current_time_start; //the time the user loaded this resource (this does not mean he can see it yet)
  20. var $current_time_stop; //the time the user closed this resource
  21. var $default_status = 'not attempted';
  22. var $encoding = 'UTF-8';
  23. var $error = '';
  24. var $extra_information = ''; //this string can be used by proprietary SCORM contents to store data about the current learnpath
  25. var $force_commit = false; //for SCORM only - if set to true, will send a scorm LMSCommit() request on each LMSSetValue()
  26. var $index; //the index of the active learnpath_item in $ordered_items array
  27. var $items = array();
  28. var $last; //item_id of last item viewed in the learning path
  29. var $last_item_seen = 0; //in case we have already come in this learnpath, reuse the last item seen if authorized
  30. var $license; //which license this course has been given - not used yet on 20060522
  31. var $lp_id; //DB ID for this learnpath
  32. var $lp_view_id; //DB ID for lp_view
  33. var $log_file; //file where to log learnpath API msg
  34. var $maker; //which maker has conceived the content (ENI, Articulate, ...)
  35. var $message = '';
  36. var $mode = 'embedded'; //holds the video display mode (fullscreen or embedded)
  37. var $name; //learnpath name (they generally have one)
  38. var $ordered_items = array(); //list of the learnpath items in the order they are to be read
  39. var $path = ''; //path inside the scorm directory (if scorm)
  40. var $theme; // the current theme of the learning path
  41. var $preview_image; // the current image of the learning path
  42. // Tells if all the items of the learnpath can be tried again. Defaults to "no" (=1)
  43. var $prevent_reinit = 1;
  44. // Describes the mode of progress bar display
  45. var $progress_bar_mode = '%';
  46. // Percentage progress as saved in the db
  47. var $progress_db = '0';
  48. var $proximity; //wether the content is distant or local or unknown
  49. var $refs_list = array (); //list of items by ref => db_id. Used only for prerequisites match.
  50. //!!!This array (refs_list) is built differently depending on the nature of the LP.
  51. //If SCORM, uses ref, if Dokeos, uses id to keep a unique value
  52. var $type; //type of learnpath. Could be 'dokeos', 'scorm', 'scorm2004', 'aicc', ...
  53. //TODO check if this type variable is useful here (instead of just in the controller script)
  54. var $user_id; //ID of the user that is viewing/using the course
  55. var $update_queue = array();
  56. var $scorm_debug = 0;
  57. var $arrMenu = array(); //array for the menu items
  58. var $debug = 0; //logging level
  59. /**
  60. * Class constructor. Needs a database handler, a course code and a learnpath id from the database.
  61. * Also builds the list of items into $this->items.
  62. * @param string Course code
  63. * @param integer Learnpath ID
  64. * @param integer User ID
  65. * @return boolean True on success, false on error
  66. */
  67. function learnpath($course, $lp_id, $user_id) {
  68. //check params
  69. //check course code
  70. if($this->debug>0){error_log('New LP - In learnpath::learnpath('.$course.','.$lp_id.','.$user_id.')',0);}
  71. if(empty($course)){
  72. $this->error = 'Course code is empty';
  73. return false;
  74. }
  75. else
  76. {
  77. $main_table = Database::get_main_table(TABLE_MAIN_COURSE);
  78. //$course = Database::escape_string($course);
  79. $course = $this->escape_string($course);
  80. $sql = "SELECT * FROM $main_table WHERE code = '$course'";
  81. if($this->debug>2){error_log('New LP - learnpath::learnpath() '.__LINE__.' - Querying course: '.$sql,0);}
  82. //$res = Database::query($sql);
  83. $res = api_sql_query($sql, __FILE__, __LINE__);
  84. if(Database::num_rows($res)>0)
  85. {
  86. $this->cc = $course;
  87. }
  88. else
  89. {
  90. $this->error = 'Course code does not exist in database ('.$sql.')';
  91. return false;
  92. }
  93. }
  94. //check learnpath ID
  95. if(empty($lp_id))
  96. {
  97. $this->error = 'Learnpath ID is empty';
  98. return false;
  99. }
  100. else
  101. {
  102. //TODO make it flexible to use any course_code (still using env course code here)
  103. $lp_table = Database::get_course_table(TABLE_LP_MAIN);
  104. //$id = Database::escape_integer($id);
  105. $lp_id = $this->escape_string($lp_id);
  106. $sql = "SELECT * FROM $lp_table WHERE id = '$lp_id'";
  107. if($this->debug>2){error_log('New LP - learnpath::learnpath() '.__LINE__.' - Querying lp: '.$sql,0);}
  108. //$res = Database::query($sql);
  109. $res = api_sql_query($sql, __FILE__, __LINE__);
  110. if(Database::num_rows($res)>0)
  111. {
  112. $this->lp_id = $lp_id;
  113. $row = Database::fetch_array($res);
  114. $this->type = $row['lp_type'];
  115. $this->name = stripslashes($row['name']);
  116. $this->encoding = $row['default_encoding'];
  117. $this->proximity = $row['content_local'];
  118. $this->theme = $row['theme'];
  119. $this->maker = $row['content_maker'];
  120. $this->prevent_reinit = $row['prevent_reinit'];
  121. $this->license = $row['content_license'];
  122. $this->scorm_debug = $row['debug'];
  123. $this->js_lib = $row['js_lib'];
  124. $this->path = $row['path'];
  125. $this->preview_image= $row['preview_image'];
  126. $this->author= $row['author'];
  127. if($this->type == 2){
  128. if($row['force_commit'] == 1){
  129. $this->force_commit = true;
  130. }
  131. }
  132. $this->mode = $row['default_view_mod'];
  133. }
  134. else
  135. {
  136. $this->error = 'Learnpath ID does not exist in database ('.$sql.')';
  137. return false;
  138. }
  139. }
  140. //check user ID
  141. if(empty($user_id)){
  142. $this->error = 'User ID is empty';
  143. return false;
  144. }
  145. else
  146. {
  147. //$main_table = Database::get_main_user_table();
  148. $main_table = Database::get_main_table(TABLE_MAIN_USER);
  149. //$user_id = Database::escape_integer($user_id);
  150. $user_id = $this->escape_string($user_id);
  151. $sql = "SELECT * FROM $main_table WHERE user_id = '$user_id'";
  152. if($this->debug>2){error_log('New LP - learnpath::learnpath() '.__LINE__.' - Querying user: '.$sql,0);}
  153. //$res = Database::query($sql);
  154. $res = api_sql_query($sql, __FILE__, __LINE__);
  155. if(Database::num_rows($res)>0)
  156. {
  157. $this->user_id = $user_id;
  158. }
  159. else
  160. {
  161. $this->error = 'User ID does not exist in database ('.$sql.')';
  162. return false;
  163. }
  164. }
  165. //end of variables checking
  166. //now get the latest attempt from this user on this LP, if available, otherwise create a new one
  167. $lp_table = Database::get_course_table(TABLE_LP_VIEW);
  168. //selecting by view_count descending allows to get the highest view_count first
  169. $sql = "SELECT * FROM $lp_table WHERE lp_id = '$lp_id' AND user_id = '$user_id' ORDER BY view_count DESC";
  170. if ($this->debug > 2) {
  171. error_log('New LP - learnpath::learnpath() ' . __LINE__ . ' - querying lp_view: ' . $sql, 0);
  172. }
  173. //$res = Database::query($sql);
  174. $res = api_sql_query($sql, __FILE__, __LINE__);
  175. $view_id = 0; //used later to query lp_item_view
  176. if (Database :: num_rows($res) > 0) {
  177. if ($this->debug > 2) {
  178. error_log('New LP - learnpath::learnpath() ' . __LINE__ . ' - Found previous view', 0);
  179. }
  180. $row = Database :: fetch_array($res);
  181. $this->attempt = $row['view_count'];
  182. $this->lp_view_id = $row['id'];
  183. $this->last_item_seen = $row['last_item'];
  184. $this->progress_db = $row['progress'];
  185. } else {
  186. if ($this->debug > 2) {
  187. error_log('New LP - learnpath::learnpath() ' . __LINE__ . ' - NOT Found previous view', 0);
  188. }
  189. $this->attempt = 1;
  190. $sql_ins = "INSERT INTO $lp_table (lp_id,user_id,view_count) VALUES ($lp_id,$user_id,1)";
  191. $res_ins = api_sql_query($sql_ins, __FILE__, __LINE__);
  192. $this->lp_view_id = Database :: get_last_insert_id();
  193. if ($this->debug > 2) {
  194. error_log('New LP - learnpath::learnpath() ' . __LINE__ . ' - inserting new lp_view: ' . $sql_ins, 0);
  195. }
  196. }
  197. //initialise items
  198. $lp_item_table = Database::get_course_table(TABLE_LP_ITEM);
  199. $sql = "SELECT * FROM $lp_item_table WHERE lp_id = '".$this->lp_id."' ORDER BY parent_item_id, display_order";
  200. $res = api_sql_query($sql, __FILE__, __LINE__);
  201. while($row = Database::fetch_array($res))
  202. {
  203. $oItem = '';
  204. //$this->ordered_items[] = $row['id'];
  205. switch ($this->type) {
  206. case 3 : //aicc
  207. $oItem = new aiccItem('db', $row['id']);
  208. if (is_object($oItem)) {
  209. $my_item_id = $oItem->get_id();
  210. $oItem->set_lp_view($this->lp_view_id);
  211. $oItem->set_prevent_reinit($this->prevent_reinit);
  212. // Don't use reference here as the next loop will make the pointed object change
  213. $this->items[$my_item_id] = $oItem;
  214. $this->refs_list[$oItem->ref] = $my_item_id;
  215. if ($this->debug > 2) {
  216. error_log('New LP - learnpath::learnpath() - aicc object with id ' . $my_item_id . ' set in items[]', 0);
  217. }
  218. }
  219. break;
  220. case 2 :
  221. require_once ('scorm.class.php');
  222. require_once ('scormItem.class.php');
  223. $oItem = new scormItem('db', $row['id']);
  224. if (is_object($oItem)) {
  225. $my_item_id = $oItem->get_id();
  226. $oItem->set_lp_view($this->lp_view_id);
  227. $oItem->set_prevent_reinit($this->prevent_reinit);
  228. // Don't use reference here as the next loop will make the pointed object change
  229. $this->items[$my_item_id] = $oItem;
  230. $this->refs_list[$oItem->ref] = $my_item_id;
  231. if ($this->debug > 2) {
  232. error_log('New LP - object with id ' . $my_item_id . ' set in items[]', 0);
  233. }
  234. }
  235. break;
  236. case 1 :
  237. default :
  238. require_once ('learnpathItem.class.php');
  239. $oItem = new learnpathItem($row['id'], $user_id);
  240. if (is_object($oItem)) {
  241. $my_item_id = $oItem->get_id();
  242. //$oItem->set_lp_view($this->lp_view_id); moved down to when we are sure the item_view exists
  243. $oItem->set_prevent_reinit($this->prevent_reinit);
  244. // Don't use reference here as the next loop will make the pointed object change
  245. $this->items[$my_item_id] = $oItem;
  246. $this->refs_list[$my_item_id] = $my_item_id;
  247. if ($this->debug > 2) {
  248. error_log('New LP - learnpath::learnpath() ' . __LINE__ . ' - object with id ' . $my_item_id . ' set in items[]', 0);
  249. }
  250. }
  251. break;
  252. }
  253. //items is a list of pointers to all items, classified by DB ID, not SCO id
  254. if ($row['parent_item_id'] == 0 OR empty ($this->items[$row['parent_item_id']])) {
  255. $this->items[$row['id']]->set_level(0);
  256. } else {
  257. $level = $this->items[$row['parent_item_id']]->get_level() + 1;
  258. $this->items[$row['id']]->set_level($level);
  259. if (is_object($this->items[$row['parent_item_id']])) {
  260. //items is a list of pointers from item DB ids to item objects
  261. $this->items[$row['parent_item_id']]->add_child($row['id']);
  262. } else {
  263. if ($this->debug > 2) {
  264. error_log('New LP - learnpath::learnpath() ' . __LINE__ . ' - The parent item (' . $row['parent_item_id'] . ') of item ' . $row['id'] . ' could not be found', 0);
  265. }
  266. }
  267. }
  268. //get last viewing vars
  269. $lp_item_view_table = Database :: get_course_table(TABLE_LP_ITEM_VIEW);
  270. //this query should only return one or zero result
  271. $sql = "SELECT * " .
  272. "FROM $lp_item_view_table " .
  273. "WHERE lp_view_id = " . $this->lp_view_id . " " .
  274. "AND lp_item_id = " . $row['id'] . " ORDER BY view_count DESC ";
  275. if ($this->debug > 2) {
  276. error_log('New LP - learnpath::learnpath() - Selecting item_views: ' . $sql, 0);
  277. }
  278. //get the item status
  279. $res2 = api_sql_query($sql, __FILE__, __LINE__);
  280. if (Database :: num_rows($res2) > 0) {
  281. //if this learnpath has already been used by this user, get his last attempt count and
  282. //the last item seen back into this object
  283. //$max = 0;
  284. $row2 = Database :: fetch_array($res2);
  285. if ($this->debug > 2) {
  286. error_log('New LP - learnpath::learnpath() - Got item_view: ' . print_r($row2, true), 0);
  287. }
  288. $this->items[$row['id']]->set_status($row2['status']);
  289. if (empty ($row2['status'])) {
  290. $this->items[$row['id']]->set_status($this->default_status);
  291. }
  292. //$this->attempt = $row['view_count'];
  293. //$this->last_item = $row['id'];
  294. } else //no item found in lp_item_view for this view
  295. {
  296. //first attempt from this user. Set attempt to 1 and last_item to 0 (first item available)
  297. //TODO if the learnpath has not got attempts activated, always use attempt '1'
  298. //$this->attempt = 1;
  299. //$this->last_item = 0;
  300. $this->items[$row['id']]->set_status($this->default_status);
  301. //Add that row to the lp_item_view table so that we have something to show in the stats page
  302. $sql_ins = "INSERT INTO $lp_item_view_table " .
  303. "(lp_item_id, lp_view_id, view_count, status) VALUES " .
  304. "(" . $row['id'] . "," . $this->lp_view_id . ",1,'not attempted')";
  305. if ($this->debug > 2) {
  306. error_log('New LP - learnpath::learnpath() ' . __LINE__ . ' - Inserting blank item_view : ' . $sql_ins, 0);
  307. }
  308. $res_ins = api_sql_query($sql_ins, __FILE__, __LINE__);
  309. }
  310. //setting the view in the item object
  311. $this->items[$row['id']]->set_lp_view($this->lp_view_id);
  312. }
  313. $this->ordered_items = $this->get_flat_ordered_items_list($this->get_id(), 0);
  314. $this->max_ordered_items = 0;
  315. foreach ($this->ordered_items as $index => $dummy) {
  316. if ($index > $this->max_ordered_items AND !empty ($dummy)) {
  317. $this->max_ordered_items = $index;
  318. }
  319. }
  320. //TODO define the current item better
  321. $this->first();
  322. if ($this->debug > 2) {
  323. error_log('New LP - learnpath::learnpath() ' . __LINE__ . ' - End of learnpath constructor for learnpath ' . $this->get_id(), 0);
  324. }
  325. }
  326. /**
  327. * Function rewritten based on old_add_item() from Yannick Warnier. Due the fact that users can decide where the item should come, I had to overlook this function and
  328. * I found it better to rewrite it. Old function is still available. Added also the possibility to add a description.
  329. *
  330. * @param int $parent
  331. * @param int $previous
  332. * @param string $type
  333. * @param int $id
  334. * @param string $title
  335. * @param string $description
  336. * @return int
  337. */
  338. function add_item($parent, $previous, $type = 'dokeos_chapter', $id, $title, $description, $prerequisites = 0, $max_time_allowed = 0) {
  339. if ($this->debug > 0) {
  340. error_log('New LP - In learnpath::add_item(' . $parent . ',' . $previous . ',' . $type . ',' . $id . ',' . $title . ')', 0);
  341. }
  342. $tbl_lp_item = Database :: get_course_table(TABLE_LP_ITEM);
  343. $parent = intval($parent);
  344. $previous = intval($previous);
  345. $type = $this->escape_string($type);
  346. $id = intval($id);
  347. $max_time_allowed = $this->escape_string(htmlentities($max_time_allowed));
  348. if (empty ($max_time_allowed)) {
  349. $max_time_allowed = 0;
  350. }
  351. //$title = htmlspecialchars($title, ENT_QUOTES, $charset); // TODO: To be checked for encoding problems.
  352. //$title = $this->escape_string(api_convert_encoding($title, $this->encoding, $charset));
  353. $title = $this->escape_string(api_convert_encoding($title, $this->encoding, api_get_system_encoding()));
  354. $description = $this->escape_string(api_convert_encoding($description, $this->encoding, api_get_system_encoding()));
  355. $sql_count = " SELECT COUNT(id) AS num
  356. FROM " . $tbl_lp_item . "
  357. WHERE lp_id = " . $this->get_id() . " AND parent_item_id = " . $parent;
  358. $res_count = api_sql_query($sql_count, __FILE__, __LINE__);
  359. $row = Database :: fetch_array($res_count);
  360. $num = $row['num'];
  361. if ($num > 0) {
  362. if ($previous == 0) {
  363. $sql = "SELECT id, next_item_id, display_order
  364. FROM " . $tbl_lp_item . "
  365. WHERE
  366. lp_id = " . $this->get_id() . " AND
  367. parent_item_id = " . $parent . " AND
  368. previous_item_id = 0 OR previous_item_id=" . $parent;
  369. $result = api_sql_query($sql, __FILE__, __LINE__);
  370. $row = Database :: fetch_array($result);
  371. $tmp_previous = 0;
  372. $next = $row['id'];
  373. $display_order = 0;
  374. } else {
  375. $previous = (int) $previous;
  376. $sql = "
  377. SELECT
  378. id,
  379. previous_item_id,
  380. next_item_id,
  381. display_order
  382. FROM " . $tbl_lp_item . "
  383. WHERE
  384. lp_id = " . $this->get_id() . " AND
  385. id = " . $previous;
  386. $result = api_sql_query($sql, __FILE__, __LINE__);
  387. $row = Database :: fetch_array($result);
  388. $tmp_previous = $row['id'];
  389. $next = $row['next_item_id'];
  390. $display_order = $row['display_order'];
  391. }
  392. } else {
  393. $tmp_previous = 0;
  394. $next = 0;
  395. $display_order = 0;
  396. }
  397. $new_item_id = -1;
  398. $id = $this->escape_string($id);
  399. if ($type == 'quiz') {
  400. $sql = 'SELECT SUM(ponderation)
  401. FROM ' . Database :: get_course_table(TABLE_QUIZ_QUESTION) . ' as quiz_question
  402. INNER JOIN ' . Database :: get_course_table(TABLE_QUIZ_TEST_QUESTION) . ' as quiz_rel_question
  403. ON quiz_question.id = quiz_rel_question.question_id
  404. AND quiz_rel_question.exercice_id = ' . $id;
  405. $rsQuiz = api_sql_query($sql, __FILE__, __LINE__);
  406. $max_score = Database :: result($rsQuiz, 0, 0);
  407. } else {
  408. $max_score = 100;
  409. }
  410. if ($prerequisites != 0) {
  411. $sql_ins = "
  412. INSERT INTO " . $tbl_lp_item . " (
  413. lp_id,
  414. item_type,
  415. ref,
  416. title,
  417. description,
  418. path,
  419. max_score,
  420. parent_item_id,
  421. previous_item_id,
  422. next_item_id,
  423. display_order,
  424. prerequisite,
  425. max_time_allowed
  426. ) VALUES (
  427. " . $this->get_id() . ",
  428. '" . $type . "',
  429. '',
  430. '" . $title . "',
  431. '" . $description . "',
  432. '" . $id . "',
  433. '" . $max_score . "',
  434. " . $parent . ",
  435. " . $previous . ",
  436. " . $next . ",
  437. " . ($display_order +1) . ",
  438. " . $prerequisites . ",
  439. " . $max_time_allowed . "
  440. )";
  441. } else {
  442. //insert new item
  443. $sql_ins = "
  444. INSERT INTO " . $tbl_lp_item . " (
  445. lp_id,
  446. item_type,
  447. ref,
  448. title,
  449. description,
  450. path,
  451. max_score,
  452. parent_item_id,
  453. previous_item_id,
  454. next_item_id,
  455. display_order,
  456. max_time_allowed
  457. ) VALUES (
  458. " . $this->get_id() . ",
  459. '" . $type . "',
  460. '',
  461. '" . $title . "',
  462. '" . $description . "',
  463. '" . $id . "',
  464. '" . $max_score . "',
  465. " . $parent . ",
  466. " . $previous . ",
  467. " . $next . ",
  468. " . ($display_order +1) . ",
  469. " . $max_time_allowed . "
  470. )";
  471. }
  472. if ($this->debug > 2) {
  473. error_log('New LP - Inserting dokeos_chapter: ' . $sql_ins, 0);
  474. }
  475. $res_ins = api_sql_query($sql_ins, __FILE__, __LINE__);
  476. if ($res_ins > 0) {
  477. $new_item_id = Database :: get_last_insert_id($res_ins);
  478. //update the item that should come after the new item
  479. $sql_update_next = "
  480. UPDATE " . $tbl_lp_item . "
  481. SET previous_item_id = " . $new_item_id . "
  482. WHERE id = " . $next;
  483. $res_update_next = api_sql_query($sql_update_next, __FILE__, __LINE__);
  484. //update the item that should be before the new item
  485. $sql_update_previous = "
  486. UPDATE " . $tbl_lp_item . "
  487. SET next_item_id = " . $new_item_id . "
  488. WHERE id = " . $tmp_previous;
  489. $res_update_previous = api_sql_query($sql_update_previous, __FILE__, __LINE__);
  490. //update all the items after the new item
  491. $sql_update_order = "
  492. UPDATE " . $tbl_lp_item . "
  493. SET display_order = display_order + 1
  494. WHERE
  495. lp_id = " . $this->get_id() . " AND
  496. id <> " . $new_item_id . " AND
  497. parent_item_id = " . $parent . " AND
  498. display_order > " . $display_order;
  499. $res_update_previous = api_sql_query($sql_update_order, __FILE__, __LINE__);
  500. //update the item that should come after the new item
  501. $sql_update_ref = "
  502. UPDATE " . $tbl_lp_item . "
  503. SET ref = " . $new_item_id . "
  504. WHERE id = " . $new_item_id;
  505. api_sql_query($sql_update_ref, __FILE__, __LINE__);
  506. }
  507. // upload audio
  508. if (!empty ($_FILES['mp3']['name'])) {
  509. // create the audio folder if it does not exist yet
  510. global $_course;
  511. $filepath = api_get_path('SYS_COURSE_PATH') . $_course['path'] . '/document/';
  512. if (!is_dir($filepath . 'audio')) {
  513. $perm = api_get_setting('permissions_for_new_directories');
  514. $perm = octdec(!empty ($perm) ? $perm : '0770');
  515. mkdir($filepath . 'audio', $perm);
  516. $audio_id = add_document($_course, '/audio', 'folder', 0, 'audio');
  517. api_item_property_update($_course, TOOL_DOCUMENT, $audio_id, 'FolderCreated', api_get_user_id());
  518. }
  519. // upload the file in the documents tool
  520. include_once (api_get_path(LIBRARY_PATH) . 'fileUpload.lib.php');
  521. $file_path = handle_uploaded_document($_course, $_FILES['mp3'], api_get_path('SYS_COURSE_PATH') . $_course['path'] . '/document', '/audio', api_get_user_id(), '', '', '', '', '', false);
  522. // getting the filename only
  523. $file_components = explode('/', $file_path);
  524. $file = $file_components[count($file_components) - 1];
  525. // store the mp3 file in the lp_item table
  526. $sql_insert_audio = "UPDATE $tbl_lp_item SET audio = '" . Database :: escape_string($file) . "' WHERE id = '" . Database :: escape_string($new_item_id) . "'";
  527. api_sql_query($sql_insert_audio, __FILE__, __LINE__);
  528. }
  529. return $new_item_id;
  530. }
  531. /**
  532. * Static admin function allowing addition of a learnpath to a course.
  533. * @param string Course code
  534. * @param string Learnpath name
  535. * @param string Learnpath description string, if provided
  536. * @param string Type of learnpath (default = 'guess', others = 'dokeos', 'aicc',...)
  537. * @param string Type of files origin (default = 'zip', others = 'dir','web_dir',...)
  538. * @param string Zip file containing the learnpath or directory containing the learnpath
  539. * @return integer The new learnpath ID on success, 0 on failure
  540. */
  541. function add_lp($course, $name, $description = '', $learnpath = 'guess', $origin = 'zip', $zipname = '') {
  542. global $charset;
  543. //if($this->debug>0){error_log('New LP - In learnpath::add_lp()',0);}
  544. //TODO
  545. $tbl_lp = Database :: get_course_table(TABLE_LP_MAIN);
  546. //check course code exists
  547. //check lp_name doesn't exist, otherwise append something
  548. $i = 0;
  549. $name = learnpath :: escape_string($name);
  550. $check_name = "SELECT * FROM $tbl_lp WHERE name = '$name'";
  551. //if($this->debug>2){error_log('New LP - Checking the name for new LP: '.$check_name,0);}
  552. $res_name = api_sql_query($check_name, __FILE__, __LINE__);
  553. while (Database :: num_rows($res_name)) {
  554. //there is already one such name, update the current one a bit
  555. $i++;
  556. $name = $name . ' - ' . $i;
  557. $check_name = "SELECT * FROM $tbl_lp WHERE name = '$name'";
  558. //if($this->debug>2){error_log('New LP - Checking the name for new LP: '.$check_name,0);}
  559. $res_name = api_sql_query($check_name, __FILE__, __LINE__);
  560. }
  561. //new name does not exist yet; keep it
  562. //escape description
  563. $description = learnpath :: escape_string(api_htmlentities($description, ENT_QUOTES, $charset)); //Kevin: added htmlentities()
  564. $type = 1;
  565. switch ($learnpath) {
  566. case 'guess' :
  567. break;
  568. case 'dokeos' :
  569. $type = 1;
  570. break;
  571. case 'aicc' :
  572. break;
  573. }
  574. switch ($origin) {
  575. case 'zip' :
  576. //check zipname string. If empty, we are currently creating a new Dokeos learnpath
  577. break;
  578. case 'manual' :
  579. default :
  580. $get_max = "SELECT MAX(display_order) FROM $tbl_lp";
  581. $res_max = api_sql_query($get_max, __FILE__, __LINE__);
  582. if (Database :: num_rows($res_max) < 1) {
  583. $dsp = 1;
  584. } else {
  585. $row = Database :: fetch_array($res_max);
  586. $dsp = $row[0] + 1;
  587. }
  588. $sql_insert = "INSERT INTO $tbl_lp " .
  589. "(lp_type,name,description,path,default_view_mod," .
  590. "default_encoding,display_order,content_maker," .
  591. "content_local,js_lib) " .
  592. "VALUES ($type,'$name','$description','','embedded'," .
  593. "'UTF-8','$dsp','Dokeos'," .
  594. "'local','')";
  595. //if($this->debug>2){error_log('New LP - Inserting new lp '.$sql_insert,0);}
  596. $res_insert = api_sql_query($sql_insert, __FILE__, __LINE__);
  597. $id = Database :: get_last_insert_id();
  598. if ($id > 0) {
  599. //insert into item_property
  600. api_item_property_update(api_get_course_info(), TOOL_LEARNPATH, $id, 'LearnpathAdded', api_get_user_id());
  601. return $id;
  602. }
  603. break;
  604. }
  605. }
  606. /**
  607. * Appends a message to the message attribute
  608. * @param string Message to append.
  609. */
  610. function append_message($string) {
  611. if ($this->debug > 0) {
  612. error_log('New LP - In learnpath::append_message()', 0);
  613. }
  614. $this->message .= $string;
  615. }
  616. /**
  617. * Autocompletes the parents of an item in case it's been completed or passed
  618. * @param integer Optional ID of the item from which to look for parents
  619. */
  620. function autocomplete_parents($item) {
  621. if ($this->debug > 0) {
  622. error_log('New LP - In learnpath::autocomplete_parents()', 0);
  623. }
  624. if (empty ($item)) {
  625. $item = $this->current;
  626. }
  627. $parent_id = $this->items[$item]->get_parent();
  628. if ($this->debug > 2) {
  629. error_log('New LP - autocompleting parent of item ' . $item . ' (item ' . $parent_id . ')', 0);
  630. }
  631. if (is_object($this->items[$item]) and !empty ($parent_id)) {
  632. //if $item points to an object and there is a parent
  633. if ($this->debug > 2) {
  634. error_log('New LP - ' . $item . ' is an item, proceed', 0);
  635. }
  636. $current_item = & $this->items[$item];
  637. $parent = & $this->items[$parent_id]; //get the parent
  638. //new experiment including failed and browsed in completed status
  639. $current_status = $current_item->get_status();
  640. if ($current_item->is_done() || $current_status == 'browsed' || $current_status == 'failed') {
  641. //if the current item is completed or passes or succeeded
  642. $completed = true;
  643. if ($this->debug > 2) {
  644. error_log('New LP - Status of current item is alright', 0);
  645. }
  646. foreach ($parent->get_children() as $child) {
  647. //check all his brothers (his parent's children) for completion status
  648. if ($child != $item) {
  649. if ($this->debug > 2) {
  650. error_log('New LP - Looking at brother with ID ' . $child . ', status is ' . $this->items[$child]->get_status(), 0);
  651. }
  652. //if($this->items[$child]->status_is(array('completed','passed','succeeded')))
  653. //Trying completing parents of failed and browsed items as well
  654. if ($this->items[$child]->status_is(array (
  655. 'completed',
  656. 'passed',
  657. 'succeeded',
  658. 'browsed',
  659. 'failed'
  660. ))) {
  661. //keep completion status to true
  662. } else {
  663. if ($this->debug > 2) {
  664. error_log('New LP - Found one incomplete child of ' . $parent_id . ': ' . $child . ' is ' . $this->items[$child]->get_status(), 0);
  665. }
  666. $completed = false;
  667. }
  668. }
  669. }
  670. if ($completed == true) { //if all the children were completed
  671. $parent->set_status('completed');
  672. $parent->save(false, $this->prerequisites_match($parent->get_id()));
  673. $this->update_queue[$parent->get_id()] = $parent->get_status();
  674. if ($this->debug > 2) {
  675. error_log('New LP - Added parent to update queue ' . print_r($this->update_queue, true), 0);
  676. }
  677. $this->autocomplete_parents($parent->get_id()); //recursive call
  678. }
  679. } else {
  680. //error_log('New LP - status of current item is not enough to get bothered with it',0);
  681. }
  682. }
  683. }
  684. /**
  685. * Autosaves the current results into the database for the whole learnpath
  686. */
  687. function autosave() {
  688. if ($this->debug > 0) {
  689. error_log('New LP - In learnpath::autosave()', 0);
  690. }
  691. //TODO add aditionnal save operations for the learnpath itself
  692. }
  693. /**
  694. * Clears the message attribute
  695. */
  696. function clear_message() {
  697. if ($this->debug > 0) {
  698. error_log('New LP - In learnpath::clear_message()', 0);
  699. }
  700. $this->message = '';
  701. }
  702. /**
  703. * Closes the current resource
  704. *
  705. * Stops the timer
  706. * Saves into the database if required
  707. * Clears the current resource data from this object
  708. * @return boolean True on success, false on failure
  709. */
  710. function close() {
  711. if ($this->debug > 0) {
  712. error_log('New LP - In learnpath::close()', 0);
  713. }
  714. if (empty ($this->lp_id)) {
  715. $this->error = 'Trying to close this learnpath but no ID is set';
  716. return false;
  717. }
  718. $this->current_time_stop = time();
  719. if ($this->save) {
  720. $learnpath_view_table = Database :: get_course_table(TABLE_LP_VIEW);
  721. /*
  722. $sql = "UPDATE $learnpath_view_table " .
  723. "SET " .
  724. "stop_time = ".$this->current_time_stop.", " .
  725. "score = ".$this->current_score.", ".
  726. "WHERE learnpath_id = '".$this->lp_id."'";
  727. //$res = Database::query($sql);
  728. $res = api_sql_query($res);
  729. if(mysql_affected_rows($res)<1)
  730. {
  731. $this->error = 'Could not update learnpath_view table while closing learnpath';
  732. return false;
  733. }
  734. */
  735. }
  736. $this->ordered_items = array ();
  737. $this->index = 0;
  738. unset ($this->lp_id);
  739. //unset other stuff
  740. return true;
  741. }
  742. /**
  743. * Static admin function allowing removal of a learnpath
  744. * @param string Course code
  745. * @param integer Learnpath ID
  746. * @param string Whether to delete data or keep it (default: 'keep', others: 'remove')
  747. * @return boolean True on success, false on failure (might change that to return number of elements deleted)
  748. */
  749. function delete($course = null, $id = null, $delete = 'keep') {
  750. //TODO implement a way of getting this to work when the current object is not set
  751. //In clear: implement this in the item class as well (abstract class) and use the given ID in queries
  752. //if(empty($course)){$course = api_get_course_id();}
  753. //if(empty($id)){$id = $this->get_id();}
  754. //If an ID is specifically given and the current LP is not the same,
  755. //prevent delete
  756. if (!empty ($id) && ($id != $this->lp_id)) {
  757. return false;
  758. }
  759. $lp = Database :: get_course_table(TABLE_LP_MAIN);
  760. $lp_item = Database :: get_course_table(TABLE_LP_ITEM); // Proposed by Christophe (clefevre), see below.
  761. $lp_view = Database :: get_course_table(TABLE_LP_VIEW);
  762. $lp_item_view = Database :: get_course_table(TABLE_LP_ITEM_VIEW);
  763. //if($this->debug>0){error_log('New LP - In learnpath::delete()',0);}
  764. //delete lp item id
  765. foreach ($this->items as $id => $dummy) {
  766. //$this->items[$id]->delete();
  767. $sql_del_view = "DELETE FROM $lp_item_view WHERE lp_item_id = '" . $id . "'";
  768. $res_del_item_view = api_sql_query($sql_del_view, __FILE__, __LINE__);
  769. }
  770. // Proposed by Christophe (nickname: clefevre), see http://www.dokeos.com/forum/viewtopic.php?t=29673
  771. $sql_del_item = "DELETE FROM $lp_item WHERE lp_id = " . $this->lp_id;
  772. $res_del_item = api_sql_query($sql_del_item, __FILE__, __LINE__);
  773. $sql_del_view = "DELETE FROM $lp_view WHERE lp_id = " . $this->lp_id;
  774. //if($this->debug>2){error_log('New LP - Deleting views bound to lp '.$this->lp_id.': '.$sql_del_view,0);}
  775. $res_del_view = api_sql_query($sql_del_view, __FILE__, __LINE__);
  776. $this->toggle_publish($this->lp_id, 'i');
  777. //if($this->debug>2){error_log('New LP - Deleting lp '.$this->lp_id.' of type '.$this->type,0);}
  778. if ($this->type == 2 OR $this->type == 3) {
  779. //this is a scorm learning path, delete the files as well
  780. $sql = "SELECT path FROM $lp WHERE id = " . $this->lp_id;
  781. $res = api_sql_query($sql, __FILE__, __LINE__);
  782. if (Database :: num_rows($res) > 0) {
  783. $row = Database :: fetch_array($res);
  784. $path = $row['path'];
  785. $sql = "SELECT id FROM $lp WHERE path = '$path' AND id != " . $this->lp_id;
  786. $res = api_sql_query($sql, __FILE__, __LINE__);
  787. if (Database :: num_rows($res) > 0) { //another learning path uses this directory, so don't delete it
  788. if ($this->debug > 2) {
  789. error_log('New LP - In learnpath::delete(), found other LP using path ' . $path . ', keeping directory', 0);
  790. }
  791. } else {
  792. //no other LP uses that directory, delete it
  793. $course_rel_dir = api_get_course_path() . '/scorm/'; //scorm dir web path starting from /courses
  794. $course_scorm_dir = api_get_path(SYS_COURSE_PATH) . $course_rel_dir; //absolute system path for this course
  795. if ($delete == 'remove' && is_dir($course_scorm_dir . $path) and !empty ($course_scorm_dir)) {
  796. if ($this->debug > 2) {
  797. error_log('New LP - In learnpath::delete(), found SCORM, deleting directory: ' . $course_scorm_dir . $path, 0);
  798. }
  799. // Proposed by Christophe (clefevre).
  800. if (strcmp(substr($path, -2), "/.") == 0) {
  801. $path = substr($path, 0, -1); // Remove "." at the end
  802. }
  803. //exec('rm -rf ' . $course_scorm_dir . $path); // See Bug #5208, this is not OS-portable way.
  804. rmdirr($course_scorm_dir . $path);
  805. }
  806. }
  807. }
  808. }
  809. $sql_del_lp = "DELETE FROM $lp WHERE id = " . $this->lp_id;
  810. //if($this->debug>2){error_log('New LP - Deleting lp '.$this->lp_id.': '.$sql_del_lp,0);}
  811. $res_del_lp = api_sql_query($sql_del_lp, __FILE__, __LINE__);
  812. $this->update_display_order(); //updates the display order of all lps
  813. api_item_property_update(api_get_course_info(), TOOL_LEARNPATH, $this->lp_id, 'delete', api_get_user_id());
  814. require_once '../gradebook/lib/be.inc.php';
  815. $tbl_grade_link = Database :: get_main_table(TABLE_MAIN_GRADEBOOK_LINK);
  816. //delete link of gradebook tool
  817. $sql = 'SELECT gl.id FROM ' . $tbl_grade_link . ' gl WHERE gl.type="4" AND gl.ref_id="' . $id . '";';
  818. $result = api_sql_query($sql, __FILE__, __LINE__);
  819. $row = Database :: fetch_array($result, 'ASSOC');
  820. $link = LinkFactory :: load($row['id']);
  821. if ($link[0] != null) {
  822. $link[0]->delete();
  823. }
  824. //TODO: also delete items and item-views
  825. if (api_get_setting('search_enabled') == 'true') {
  826. require_once (api_get_path(LIBRARY_PATH) . 'specific_fields_manager.lib.php');
  827. $r = delete_all_values_for_item($this->cc, TOOL_LEARNPATH, $this->lp_id);
  828. }
  829. }
  830. /**
  831. * Removes all the children of one item - dangerous!
  832. * @param integer Element ID of which children have to be removed
  833. * @return integer Total number of children removed
  834. */
  835. function delete_children_items($id) {
  836. if ($this->debug > 0) {
  837. error_log('New LP - In learnpath::delete_children_items(' . $id . ')', 0);
  838. }
  839. $num = 0;
  840. if (empty ($id) || $id != strval(intval($id))) {
  841. return false;
  842. }
  843. $lp_item = Database :: get_course_table(TABLE_LP_ITEM);
  844. $sql = "SELECT * FROM $lp_item WHERE parent_item_id = $id";
  845. $res = api_sql_query($sql, __FILE__, __LINE__);
  846. while ($row = Database :: fetch_array($res)) {
  847. $num += $this->delete_children_items($row['id']);
  848. $sql_del = "DELETE FROM $lp_item WHERE id = " . $row['id'];
  849. $res_del = api_sql_query($sql_del, __FILE__, __LINE__);
  850. $num++;
  851. }
  852. return $num;
  853. }
  854. /**
  855. * Removes an item from the current learnpath
  856. * @param integer Elem ID (0 if first)
  857. * @param integer Whether to remove the resource/data from the system or leave it (default: 'keep', others 'remove')
  858. * @return integer Number of elements moved
  859. * @todo implement resource removal
  860. */
  861. function delete_item($id, $remove = 'keep') {
  862. if ($this->debug > 0) {
  863. error_log('New LP - In learnpath::delete_item()', 0);
  864. }
  865. //TODO - implement the resource removal
  866. if (empty ($id) || $id != strval(intval($id))) {
  867. return false;
  868. }
  869. //first select item to get previous, next, and display order
  870. $lp_item = Database :: get_course_table(TABLE_LP_ITEM);
  871. $sql_sel = "SELECT * FROM $lp_item WHERE id = $id";
  872. $res_sel = api_sql_query($sql_sel, __FILE__, __LINE__);
  873. if (Database :: num_rows($res_sel) < 1) {
  874. return false;
  875. }
  876. $row = Database :: fetch_array($res_sel);
  877. $previous = $row['previous_item_id'];
  878. $next = $row['next_item_id'];
  879. $display = $row['display_order'];
  880. $parent = $row['parent_item_id'];
  881. $lp = $row['lp_id'];
  882. //delete children items
  883. $num = $this->delete_children_items($id);
  884. if ($this->debug > 2) {
  885. error_log('New LP - learnpath::delete_item() - deleted ' . $num . ' children of element ' . $id, 0);
  886. }
  887. //now delete the item
  888. $sql_del = "DELETE FROM $lp_item WHERE id = $id";
  889. if ($this->debug > 2) {
  890. error_log('New LP - Deleting item: ' . $sql_del, 0);
  891. }
  892. $res_del = api_sql_query($sql_del, __FILE__, __LINE__);
  893. //now update surrounding items
  894. $sql_upd = "UPDATE $lp_item SET next_item_id = $next WHERE id = $previous";
  895. $res_upd = api_sql_query($sql_upd, __FILE__, __LINE__);
  896. $sql_upd = "UPDATE $lp_item SET previous_item_id = $previous WHERE id = $next";
  897. $res_upd = api_sql_query($sql_upd, __FILE__, __LINE__);
  898. //now update all following items with new display order
  899. $sql_all = "UPDATE $lp_item SET display_order = display_order-1 WHERE lp_id = $lp AND parent_item_id = $parent AND display_order > $display";
  900. $res_all = api_sql_query($sql_all, __FILE__, __LINE__);
  901. // remove from search engine if enabled
  902. if (api_get_setting('search_enabled') == 'true') {
  903. $tbl_se_ref = Database :: get_main_table(TABLE_MAIN_SEARCH_ENGINE_REF);
  904. $sql = 'SELECT * FROM %s WHERE course_code=\'%s\' AND tool_id=\'%s\' AND ref_id_high_level=%s AND ref_id_second_level=%d LIMIT 1';
  905. $sql = sprintf($sql, $tbl_se_ref, $this->cc, TOOL_LEARNPATH, $lp, $id);
  906. $res = api_sql_query($sql, __FILE__, __LINE__);
  907. if (Database :: num_rows($res) > 0) {
  908. $row2 = Database :: fetch_array($res);
  909. require_once (api_get_path(LIBRARY_PATH) . 'search/DokeosIndexer.class.php');
  910. $di = new DokeosIndexer();
  911. $di->remove_document((int) $row2['search_did']);
  912. }
  913. $sql = 'DELETE FROM %s WHERE course_code=\'%s\' AND tool_id=\'%s\' AND ref_id_high_level=%s AND ref_id_second_level=%d LIMIT 1';
  914. $sql = sprintf($sql, $tbl_se_ref, $this->cc, TOOL_LEARNPATH, $lp, $id);
  915. api_sql_query($sql, __FILE__, __LINE__);
  916. }
  917. }
  918. /**
  919. * Updates an item's content in place
  920. * @param integer Element ID
  921. * @param integer Parent item ID
  922. * @param integer Previous item ID
  923. * @param string Item title
  924. * @param string Item description
  925. * @param string Prerequisites (optional)
  926. * @param string Indexing terms (optional)
  927. * @param array The array resulting of the $_FILES[mp3] element
  928. * @return boolean True on success, false on error
  929. */
  930. function edit_item($id, $parent, $previous, $title, $description, $prerequisites = 0, $audio = NULL, $max_time_allowed = 0) {
  931. global $charset;
  932. if ($this->debug > 0) {
  933. error_log('New LP - In learnpath::edit_item()', 0);
  934. }
  935. if (empty ($max_time_allowed)) {
  936. $max_time_allowed = 0;
  937. }
  938. if (empty ($id) or ($id != strval(intval($id))) or empty ($title)) {
  939. return false;
  940. }
  941. $title = api_convert_encoding($title, $this->encoding, $charset);
  942. $description = api_convert_encoding($description, $this->encoding, $charset);
  943. $tbl_lp_item = Database :: get_course_table(TABLE_LP_ITEM);
  944. $sql_select = "SELECT * FROM " . $tbl_lp_item . " WHERE id = " . $id;
  945. $res_select = api_sql_query($sql_select, __FILE__, __LINE__);
  946. $row_select = Database :: fetch_array($res_select);
  947. $audio_update_sql = '';
  948. if (is_array($audio) && !empty ($audio['tmp_name']) && $audio['error'] === 0) {
  949. // create the audio folder if it does not exist yet
  950. global $_course;
  951. $filepath = api_get_path('SYS_COURSE_PATH') . $_course['path'] . '/document/';
  952. if (!is_dir($filepath . 'audio')) {
  953. $perm = api_get_setting('permissions_for_new_directories');
  954. $perm = octdec(!empty ($perm) ? $perm : '0770');
  955. mkdir($filepath . 'audio', $perm);
  956. $audio_id = add_document($_course, '/audio', 'folder', 0, 'audio');
  957. api_item_property_update($_course, TOOL_DOCUMENT, $audio_id, 'FolderCreated', api_get_user_id());
  958. }
  959. //upload file in documents
  960. $pi = pathinfo($audio['name']);
  961. if ($pi['extension'] == 'mp3') {
  962. $c_det = api_get_course_info($this->cc);
  963. $bp = api_get_path(SYS_COURSE_PATH) . $c_det['path'] . '/document';
  964. $path = handle_uploaded_document($c_det, $audio, $bp, '/audio', api_get_user_id(), 0, null, '', 0, 'rename', false, 0);
  965. $path = substr($path, 7);
  966. //update reference in lp_item - audio path is the path from inside de document/audio/ dir
  967. $audio_update_sql = ", audio = '" . Database :: escape_string($path) . "' ";
  968. }
  969. }
  970. $same_parent = ($row_select['parent_item_id'] == $parent) ? true : false;
  971. $same_previous = ($row_select['previous_item_id'] == $previous) ? true : false;
  972. //TODO: htmlspecialchars to be checked for encoding related problems.
  973. if ($same_parent && $same_previous) {
  974. //only update title and description
  975. $sql_update = " UPDATE " . $tbl_lp_item . "
  976. SET title = '" . $this->escape_string($title) . "',
  977. prerequisite = '" . $prerequisites . "',
  978. description = '" . $this->escape_string($description) . "'
  979. " . $audio_update_sql . ",
  980. max_time_allowed = '" . $this->escape_string($max_time_allowed) . "'
  981. WHERE id = " . $id;
  982. $res_update = api_sql_query($sql_update, __FILE__, __LINE__);
  983. } else {
  984. $old_parent = $row_select['parent_item_id'];
  985. $old_previous = $row_select['previous_item_id'];
  986. $old_next = $row_select['next_item_id'];
  987. $old_order = $row_select['display_order'];
  988. $old_prerequisite = $row_select['prerequisite'];
  989. $old_max_time_allowed = $row_select['max_time_allowed'];
  990. /* BEGIN -- virtually remove the current item id */
  991. /* for the next and previous item it is like the current item doesn't exist anymore */
  992. if ($old_previous != 0) {
  993. $sql_update_next = "
  994. UPDATE " . $tbl_lp_item . "
  995. SET next_item_id = " . $old_next . "
  996. WHERE id = " . $old_previous;
  997. $res_update_next = api_sql_query($sql_update_next, __FILE__, __LINE__);
  998. //echo '<p>' . $sql_update_next . '</p>';
  999. }
  1000. if ($old_next != 0) {
  1001. $sql_update_previous = "
  1002. UPDATE " . $tbl_lp_item . "
  1003. SET previous_item_id = " . $old_previous . "
  1004. WHERE id = " . $old_next;
  1005. $res_update_previous = api_sql_query($sql_update_previous, __FILE__, __LINE__);
  1006. //echo '<p>' . $sql_update_previous . '</p>';
  1007. }
  1008. //display_order - 1 for every item with a display_order bigger then the display_order of the current item
  1009. $sql_update_order = "
  1010. UPDATE " . $tbl_lp_item . "
  1011. SET display_order = display_order - 1
  1012. WHERE
  1013. display_order > " . $old_order . " AND
  1014. parent_item_id = " . $old_parent;
  1015. $res_update_order = api_sql_query($sql_update_order, __FILE__, __LINE__);
  1016. //echo '<p>' . $sql_update_order . '</p>';
  1017. /* END -- virtually remove the current item id */
  1018. /* BEGIN -- update the current item id to his new location */
  1019. if ($previous == 0) {
  1020. //select the data of the item that should come after the current item
  1021. $sql_select_old = "
  1022. SELECT
  1023. id,
  1024. display_order
  1025. FROM " . $tbl_lp_item . "
  1026. WHERE
  1027. lp_id = " . $this->lp_id . " AND
  1028. parent_item_id = " . $parent . " AND
  1029. previous_item_id = " . $previous;
  1030. $res_select_old = api_sql_query($sql_select_old, __FILE__, __LINE__);
  1031. $row_select_old = Database :: fetch_array($res_select_old);
  1032. //echo '<p>' . $sql_select_old . '</p>';
  1033. //if the new parent didn't have children before
  1034. if (Database :: num_rows($res_select_old) == 0) {
  1035. $new_next = 0;
  1036. $new_order = 1;
  1037. } else {
  1038. $new_next = $row_select_old['id'];
  1039. $new_order = $row_select_old['display_order'];
  1040. }
  1041. //echo 'New next_item_id of current item: ' . $new_next . '<br />';
  1042. //echo 'New previous_item_id of current item: ' . $previous . '<br />';
  1043. //echo 'New display_order of current item: ' . $new_order . '<br />';
  1044. } else {
  1045. //select the data of the item that should come before the current item
  1046. $sql_select_old = "
  1047. SELECT
  1048. next_item_id,
  1049. display_order
  1050. FROM " . $tbl_lp_item . "
  1051. WHERE id = " . $previous;
  1052. $res_select_old = api_sql_query($sql_select_old, __FILE__, __LINE__);
  1053. $row_select_old = Database :: fetch_array($res_select_old);
  1054. //echo '<p>' . $sql_select_old . '</p>';
  1055. //echo 'New next_item_id of current item: ' . $row_select_old['next_item_id'] . '<br />';
  1056. //echo 'New previous_item_id of current item: ' . $previous . '<br />';
  1057. //echo 'New display_order of current item: ' . ($row_select_old['display_order'] + 1) . '<br />';
  1058. $new_next = $row_select_old['next_item_id'];
  1059. $new_order = $row_select_old['display_order'] + 1;
  1060. }
  1061. //TODO: htmlspecialchars to be checked for encoding related problems.
  1062. //update the current item with the new data
  1063. $sql_update = "
  1064. UPDATE " . $tbl_lp_item . "
  1065. SET
  1066. title = '" . $this->escape_string($title) . "',
  1067. description = '" . $this->escape_string($description) . "',
  1068. parent_item_id = " . $parent . ",
  1069. previous_item_id = " . $previous . ",
  1070. next_item_id = " . $new_next . ",
  1071. display_order = " . $new_order . "
  1072. " . $audio_update_sql . "
  1073. WHERE id = " . $id;
  1074. $res_update_next = api_sql_query($sql_update, __FILE__, __LINE__);
  1075. //echo '<p>' . $sql_update . '</p>';
  1076. if ($previous != 0) {
  1077. //update the previous item's next_item_id
  1078. $sql_update_previous = "
  1079. UPDATE " . $tbl_lp_item . "
  1080. SET next_item_id = " . $id . "
  1081. WHERE id = " . $previous;
  1082. $res_update_next = api_sql_query($sql_update_previous, __FILE__, __LINE__);
  1083. //echo '<p>' . $sql_update_previous . '</p>';
  1084. }
  1085. if ($new_next != 0) {
  1086. //update the next item's previous_item_id
  1087. $sql_update_next = "
  1088. UPDATE " . $tbl_lp_item . "
  1089. SET previous_item_id = " . $id . "
  1090. WHERE id = " . $new_next;
  1091. $res_update_next = api_sql_query($sql_update_next, __FILE__, __LINE__);
  1092. //echo '<p>' . $sql_update_next . '</p>';
  1093. }
  1094. if ($old_prerequisite != $prerequisites) {
  1095. $sql_update_next = "
  1096. UPDATE " . $tbl_lp_item . "
  1097. SET prerequisite = " . $prerequisites . "
  1098. WHERE id = " . $id;
  1099. $res_update_next = api_sql_query($sql_update_next, __FILE__, __LINE__);
  1100. }
  1101. if ($old_max_time_allowed != $max_time_allowed) {
  1102. $sql_update_max_time_allowed = "
  1103. UPDATE " . $tbl_lp_item . "
  1104. SET max_time_allowed = " . $max_time_allowed . "
  1105. WHERE id = " . $id;
  1106. $res_update_max_time_allowed = api_sql_query($sql_update_max_time_allowed, __FILE__, __LINE__);
  1107. }
  1108. //update all the items with the same or a bigger display_order than
  1109. //the current item
  1110. $sql_update_order = "
  1111. UPDATE " . $tbl_lp_item . "
  1112. SET display_order = display_order + 1
  1113. WHERE
  1114. lp_id = " . $this->get_id() . " AND
  1115. id <> " . $id . " AND
  1116. parent_item_id = " . $parent . " AND
  1117. display_order >= " . $new_order;
  1118. $res_update_next = api_sql_query($sql_update_order, __FILE__, __LINE__);
  1119. //echo '<p>' . $sql_update_order . '</p>';
  1120. /* END -- update the current item id to his new location */
  1121. }
  1122. }
  1123. /**
  1124. * Updates an item's prereq in place
  1125. * @param integer Element ID
  1126. * @param string Prerequisite Element ID
  1127. * @param string Prerequisite item type
  1128. * @param string Prerequisite min score
  1129. * @param string Prerequisite max score
  1130. * @return boolean True on success, false on error
  1131. */
  1132. function edit_item_prereq($id, $prerequisite_id, $mastery_score = 0, $max_score = 100) {
  1133. if ($this->debug > 0) {
  1134. error_log('New LP - In learnpath::edit_item_prereq(' . $id . ',' . $prerequisite_id . ',' . $mastery_score . ',' . $max_score . ')', 0);
  1135. }
  1136. if (empty ($id) or ($id != strval(intval($id))) or empty ($prerequisite_id)) {
  1137. return false;
  1138. }
  1139. $prerequisite_id = $this->escape_string($prerequisite_id);
  1140. $tbl_lp_item = Database :: get_course_table(TABLE_LP_ITEM);
  1141. if (!is_numeric($mastery_score) || $mastery_score < 0)
  1142. $mastery_score = 0;
  1143. if (!is_numeric($max_score) || $max_score < 0)
  1144. $max_score = 100;
  1145. if ($mastery_score > $max_score)
  1146. $max_score = $mastery_score;
  1147. if (!is_numeric($prerequisite_id))
  1148. $prerequisite_id = 'NULL';
  1149. $sql_upd = " UPDATE " . $tbl_lp_item . "
  1150. SET prerequisite = " . $prerequisite_id . " WHERE id = " . $id;
  1151. $res_upd = api_sql_query($sql_upd, __FILE__, __LINE__);
  1152. if ($prerequisite_id != 'NULL' && $prerequisite_id != '') {
  1153. $sql_upd = " UPDATE " . $tbl_lp_item . " SET
  1154. mastery_score = " . $mastery_score .
  1155. //", max_score = " . $max_score . " " . //max score cannot be changed in the form anyway - see display_item_prerequisites_form()
  1156. " WHERE ref = '" . $prerequisite_id . "'"; //will this be enough to ensure unicity?
  1157. $res_upd = api_sql_query($sql_upd, __FILE__, __LINE__);
  1158. }
  1159. //TODO update the item object (can be ignored for now because refreshed)
  1160. return true;
  1161. }
  1162. /**
  1163. * Escapes a string with the available database escape function
  1164. * @param string String to escape
  1165. * @return string String escaped
  1166. */
  1167. function escape_string($string) {
  1168. //if($this->debug>0){error_log('New LP - In learnpath::escape_string('.$string.')',0);}
  1169. return Database :: escape_string($string);
  1170. }
  1171. /**
  1172. * Static admin function exporting a learnpath into a zip file
  1173. * @param string Export type (scorm, zip, cd)
  1174. * @param string Course code
  1175. * @param integer Learnpath ID
  1176. * @param string Zip file name
  1177. * @return string Zip file path (or false on error)
  1178. */
  1179. function export_lp($type, $course, $id, $zipname) {
  1180. //if($this->debug>0){error_log('New LP - In learnpath::export_lp()',0);}
  1181. //TODO
  1182. if (empty ($type) OR empty ($course) OR empty ($id) OR empty ($zipname)) {
  1183. return false;
  1184. }
  1185. $url = '';
  1186. switch ($type) {
  1187. case 'scorm' :
  1188. break;
  1189. case 'zip' :
  1190. break;
  1191. case 'cdrom' :
  1192. break;
  1193. }
  1194. return $url;
  1195. }
  1196. /**
  1197. * Gets all the chapters belonging to the same parent as the item/chapter given
  1198. * Can also be called as abstract method
  1199. * @param integer Item ID
  1200. * @return array A list of all the "brother items" (or an empty array on failure)
  1201. */
  1202. function get_brother_chapters($id) {
  1203. if ($this->debug > 0) {
  1204. error_log('New LP - In learnpath::get_brother_chapters()', 0);
  1205. }
  1206. if (empty ($id) OR $id != strval(intval($id))) {
  1207. return array ();
  1208. }
  1209. $lp_item = Database :: get_course_table(TABLE_LP_ITEM);
  1210. $sql_parent = "SELECT * FROM $lp_item WHERE id = $id AND item_type='dokeos_chapter'";
  1211. $res_parent = api_sql_query($sql_parent, __FILE__, __LINE__);
  1212. if (Database :: num_rows($res_parent) > 0) {
  1213. $row_parent = Database :: fetch_array($res_parent);
  1214. $parent = $row_parent['parent_item_id'];
  1215. $sql_bros = "SELECT * FROM $lp_item WHERE parent_item_id = $parent AND id = $id AND item_type='dokeos_chapter' ORDER BY display_order";
  1216. $res_bros = api_sql_query($sql_bros, __FILE__, __LINE__);
  1217. $list = array ();
  1218. while ($row_bro = Database :: fetch_array($res_bros)) {
  1219. $list[] = $row_bro;
  1220. }
  1221. return $list;
  1222. }
  1223. return array ();
  1224. }
  1225. /**
  1226. * Gets all the items belonging to the same parent as the item given
  1227. * Can also be called as abstract method
  1228. * @param integer Item ID
  1229. * @return array A list of all the "brother items" (or an empty array on failure)
  1230. */
  1231. function get_brother_items($id) {
  1232. if ($this->debug > 0) {
  1233. error_log('New LP - In learnpath::get_brother_items(' . $id . ')', 0);
  1234. }
  1235. if (empty ($id) OR $id != strval(intval($id))) {
  1236. return array ();
  1237. }
  1238. $lp_item = Database :: get_course_table(TABLE_LP_ITEM);
  1239. $sql_parent = "SELECT * FROM $lp_item WHERE id = $id";
  1240. $res_parent = api_sql_query($sql_parent, __FILE__, __LINE__);
  1241. if (Database :: num_rows($res_parent) > 0) {
  1242. $row_parent = Database :: fetch_array($res_parent);
  1243. $parent = $row_parent['parent_item_id'];
  1244. $sql_bros = "SELECT * FROM $lp_item WHERE parent_item_id = $parent ORDER BY display_order";
  1245. $res_bros = api_sql_query($sql_bros, __FILE__, __LINE__);
  1246. $list = array ();
  1247. while ($row_bro = Database :: fetch_array($res_bros)) {
  1248. $list[] = $row_bro;
  1249. }
  1250. return $list;
  1251. }
  1252. return array ();
  1253. }
  1254. /**
  1255. * Get the specific prefix index terms of this learning path
  1256. * @return array Array of terms
  1257. */
  1258. function get_common_index_terms_by_prefix($prefix) {
  1259. require_once api_get_path(LIBRARY_PATH) . 'specific_fields_manager.lib.php';
  1260. $terms = get_specific_field_values_list_by_prefix($prefix, $this->cc, TOOL_LEARNPATH, $this->lp_id);
  1261. $prefix_terms = array ();
  1262. foreach ($terms as $term) {
  1263. $prefix_terms[] = $term['value'];
  1264. }
  1265. return $prefix_terms;
  1266. }
  1267. /**
  1268. * Gets the number of items currently completed
  1269. * @return integer The number of items currently completed
  1270. */
  1271. function get_complete_items_count() {
  1272. if ($this->debug > 0) {
  1273. error_log('New LP - In learnpath::get_complete_items_count()', 0);
  1274. }
  1275. $i = 0;
  1276. foreach ($this->items as $id => $dummy) {
  1277. //if($this->items[$id]->status_is(array('completed','passed','succeeded'))){
  1278. //Trying failed and browsed considered "progressed" as well
  1279. if ($this->items[$id]->status_is(array (
  1280. 'completed',
  1281. 'passed',
  1282. 'succeeded',
  1283. 'browsed',
  1284. 'failed'
  1285. )) && $this->items[$id]->get_type() != 'dokeos_chapter' && $this->items[$id]->get_type() != 'dir') {
  1286. $i++;
  1287. }
  1288. }
  1289. return $i;
  1290. }
  1291. /**
  1292. * Gets the current item ID
  1293. * @return integer The current learnpath item id
  1294. */
  1295. function get_current_item_id() {
  1296. $current = 0;
  1297. if ($this->debug > 0) {
  1298. error_log('New LP - In learnpath::get_current_item_id()', 0);
  1299. }
  1300. if (!empty ($this->current)) {
  1301. $current = $this->current;
  1302. }
  1303. if ($this->debug > 2) {
  1304. error_log('New LP - In learnpath::get_current_item_id() - Returning ' . $current, 0);
  1305. }
  1306. return $current;
  1307. }
  1308. /** Force to get the first learnpath item id
  1309. * @return integer The current learnpath item id
  1310. */
  1311. function get_first_item_id() {
  1312. $current = 0;
  1313. if (is_array($this->ordered_items)) {
  1314. $current = $this->ordered_items[0];
  1315. }
  1316. return $current;
  1317. }
  1318. /**
  1319. * Gets the total number of items available for viewing in this SCORM
  1320. * @return integer The total number of items
  1321. */
  1322. function get_total_items_count() {
  1323. if ($this->debug > 0) {
  1324. error_log('New LP - In learnpath::get_total_items_count()', 0);
  1325. }
  1326. return count($this->items);
  1327. }
  1328. /**
  1329. * Gets the total number of items available for viewing in this SCORM but without chapters
  1330. * @return integer The total no-chapters number of items
  1331. */
  1332. function get_total_items_count_without_chapters() {
  1333. if ($this->debug > 0) {
  1334. error_log('New LP - In learnpath::get_total_items_count_without_chapters()', 0);
  1335. }
  1336. $total = 0;
  1337. foreach ($this->items as $temp => $temp2) {
  1338. if (!in_array($temp2->get_type(), array (
  1339. 'dokeos_chapter',
  1340. 'chapter',
  1341. 'dir'
  1342. )))
  1343. $total++;
  1344. }
  1345. return $total;
  1346. }
  1347. /**
  1348. * Gets the first element URL.
  1349. * @return string URL to load into the viewer
  1350. */
  1351. function first() {
  1352. if ($this->debug > 0) {
  1353. error_log('New LP - In learnpath::first()', 0);
  1354. }
  1355. //test if the last_item_seen exists and is not a dir
  1356. if (count($this->ordered_items) == 0) {
  1357. $this->index = 0;
  1358. }
  1359. if (!empty ($this->last_item_seen) && !empty ($this->items[$this->last_item_seen]) && $this->items[$this->last_item_seen]->get_type() != 'dir' && $this->items[$this->last_item_seen]->get_type() != 'dokeos_chapter' && $this->items[$this->last_item_seen]->is_done() != true) {
  1360. if ($this->debug > 2) {
  1361. error_log('New LP - In learnpath::first() - Last item seen is ' . $this->last_item_seen . ' of type ' . $this->items[$this->last_item_seen]->get_type(), 0);
  1362. }
  1363. $index = -1;
  1364. foreach ($this->ordered_items as $myindex => $item_id) {
  1365. if ($item_id == $this->last_item_seen) {
  1366. $index = $myindex;
  1367. break;
  1368. }
  1369. }
  1370. if ($index == -1) {
  1371. //index hasn't changed, so item not found - panic (this shouldn't happen)
  1372. if ($this->debug > 2) {
  1373. error_log('New LP - Last item (' . $this->last_item_seen . ') was found in items but not in ordered_items, panic!', 0);
  1374. }
  1375. return false;
  1376. } else {
  1377. $this->last = $this->last_item_seen;
  1378. $this->current = $this->last_item_seen;
  1379. $this->index = $index;
  1380. }
  1381. } else {
  1382. if ($this->debug > 2) {
  1383. error_log('New LP - In learnpath::first() - No last item seen', 0);
  1384. }
  1385. $index = 0;
  1386. //loop through all ordered items and stop at the first item that is
  1387. //not a directory *and* that has not been completed yet
  1388. while (!empty ($this->ordered_items[$index]) AND is_a($this->items[$this->ordered_items[$index]], 'learnpathItem') AND ($this->items[$this->ordered_items[$index]]->get_type() == 'dir' OR $this->items[$this->ordered_items[$index]]->get_type() == 'dokeos_chapter' OR $this->items[$this->ordered_items[$index]]->is_done() === true) AND $index < $this->max_ordered_items) {
  1389. $index++;
  1390. }
  1391. $this->last = $this->current;
  1392. //current is
  1393. $this->current = $this->ordered_items[$index];
  1394. $this->index = $index;
  1395. if ($this->debug > 2) {
  1396. error_log('New LP - In learnpath::first() - No last item seen. New last = ' . $this->last . '(' . $this->ordered_items[$index] . ')', 0);
  1397. }
  1398. }
  1399. if ($this->debug > 2) {
  1400. error_log('New LP - In learnpath::first() - First item is ' . $this->get_current_item_id());
  1401. }
  1402. }
  1403. /**
  1404. * Gets the information about an item in a format usable as JavaScript to update
  1405. * the JS API by just printing this content into the <head> section of the message frame
  1406. * @param integer Item ID
  1407. * @return string
  1408. */
  1409. function get_js_info($item_id = '') {
  1410. if ($this->debug > 0) {
  1411. error_log('New LP - In learnpath::get_js_info(' . $item_id . ')', 0);
  1412. }
  1413. $info = '';
  1414. $item_id = $this->escape_string($item_id);
  1415. if (!empty ($item_id) && is_object($this->items[$item_id])) {
  1416. //if item is defined, return values from DB
  1417. $oItem = $this->items[$item_id];
  1418. $info .= '<script language="javascript">';
  1419. $info .= "top.set_score(" . $oItem->get_score() . ");\n";
  1420. $info .= "top.set_max(" . $oItem->get_max() . ");\n";
  1421. $info .= "top.set_min(" . $oItem->get_min() . ");\n";
  1422. $info .= "top.set_lesson_status('" . $oItem->get_status() . "');";
  1423. $info .= "top.set_session_time('" . $oItem->get_scorm_time('js') . "');";
  1424. $info .= "top.set_suspend_data('" . $oItem->get_suspend_data() . "');";
  1425. $info .= "top.set_saved_lesson_status('" . $oItem->get_status() . "');";
  1426. $info .= "top.set_flag_synchronized();";
  1427. $info .= '</script>';
  1428. if ($this->debug > 2) {
  1429. error_log('New LP - in learnpath::get_js_info(' . $item_id . ') - returning: ' . $info, 0);
  1430. }
  1431. return $info;
  1432. } else {
  1433. //if item_id is empty, just update to default SCORM data
  1434. $info .= '<script language="javascript">';
  1435. $info .= "top.set_score(" . learnpathItem :: get_score() . ");\n";
  1436. $info .= "top.set_max(" . learnpathItem :: get_max() . ");\n";
  1437. $info .= "top.set_min(" . learnpathItem :: get_min() . ");\n";
  1438. $info .= "top.set_lesson_status('" . learnpathItem :: get_status() . "');";
  1439. $info .= "top.set_session_time('" . learnpathItem :: get_scorm_time('js') . "');";
  1440. $info .= "top.set_suspend_data('" . learnpathItem :: get_suspend_data() . "');";
  1441. $info .= "top.set_saved_lesson_status('" . learnpathItem :: get_status() . "');";
  1442. $info .= "top.set_flag_synchronized();";
  1443. $info .= '</script>';
  1444. if ($this->debug > 2) {
  1445. error_log('New LP - in learnpath::get_js_info(' . $item_id . ') - returning: ' . $info, 0);
  1446. }
  1447. return $info;
  1448. }
  1449. }
  1450. /**
  1451. * Gets the js library from the database
  1452. * @return string The name of the javascript library to be used
  1453. */
  1454. function get_js_lib() {
  1455. $lib = '';
  1456. if (!empty ($this->js_lib)) {
  1457. $lib = $this->js_lib;
  1458. }
  1459. return $lib;
  1460. }
  1461. /**
  1462. * Gets the learnpath database ID
  1463. * @return integer Learnpath ID in the lp table
  1464. */
  1465. function get_id() {
  1466. //if($this->debug>0){error_log('New LP - In learnpath::get_id()',0);}
  1467. if (!empty ($this->lp_id)) {
  1468. return $this->lp_id;
  1469. } else {
  1470. return 0;
  1471. }
  1472. }
  1473. /**
  1474. * Gets the last element URL.
  1475. * @return string URL to load into the viewer
  1476. */
  1477. function get_last() {
  1478. if ($this->debug > 0) {
  1479. error_log('New LP - In learnpath::get_last()', 0);
  1480. }
  1481. $this->index = count($this->ordered_items) - 1;
  1482. return $this->ordered_items[$this->index];
  1483. }
  1484. /**
  1485. * Gets the navigation bar for the learnpath display screen
  1486. * @return string The HTML string to use as a navigation bar
  1487. */
  1488. function get_navigation_bar() {
  1489. if ($this->debug > 0) {
  1490. error_log('New LP - In learnpath::get_navigation_bar()', 0);
  1491. }
  1492. //TODO find a good value for the following variables
  1493. $file = '';
  1494. $openDir = '';
  1495. $edoceo = '';
  1496. $time = 0;
  1497. $navbar = '';
  1498. $RequestUri = '';
  1499. $mycurrentitemid = $this->get_current_item_id();
  1500. if ($this->mode == 'fullscreen') {
  1501. $navbar = '<table cellpadding="0" cellspacing="0" align="left">' . "\n" .
  1502. ' <tr> ' . "\n" .
  1503. ' <td>' . "\n" .
  1504. ' <div class="buttons">' . "\n" .
  1505. ' <a href="lp_controller.php?action=stats" onclick="window.parent.API.save_asset();return true;" target="content_name_blank" title="stats" id="stats_link"><img border="0" src="../img/lp_stats.gif" title="' . get_lang('Reporting') . '"></a>' . "\n" .
  1506. ' <a href="" onclick="dokeos_xajax_handler.switch_item(' . $mycurrentitemid . ',\'previous\');return false;" title="previous"><img border="0" src="../img/lp_leftarrow.gif" title="' . get_lang('ScormPrevious') . '"></a>' . "\n" .
  1507. ' <a href="" onclick="dokeos_xajax_handler.switch_item(' . $mycurrentitemid . ',\'next\');return false;" title="next" ><img border="0" src="../img/lp_rightarrow.gif" title="' . get_lang('ScormNext') . '"></a>' . "\n" .
  1508. //' <a href="lp_controller.php?action=mode&mode=embedded" target="_top" title="embedded mode"><img border="0" src="../img/view_choose.gif" title="'.get_lang('ScormExitFullScreen').'"></a>'."\n" .
  1509. //' <a href="lp_controller.php?action=list" target="_top" title="learnpaths list"><img border="0" src="../img/exit.png" title="Exit"></a>'."\n" .
  1510. ' </div>' . "\n" .
  1511. ' </td>' . "\n" .
  1512. ' </tr>' . "\n" .
  1513. '</table>' . "\n";
  1514. } else {
  1515. $navbar = '<table cellpadding="0" cellspacing="0" align="left">' . "\n" .
  1516. ' <tr> ' . "\n" .
  1517. ' <td>' . "\n" .
  1518. ' <div class="buttons">' . "\n" .
  1519. ' <a href="lp_controller.php?action=stats" onclick="window.parent.API.save_asset();return true;" target="content_name" title="stats" id="stats_link"><img border="0" src="../img/lp_stats.gif" title="' . get_lang('Reporting') . '"></a>' . "\n" .
  1520. ' <a href="" onclick="dokeos_xajax_handler.switch_item(' . $mycurrentitemid . ',\'previous\');return false;" title="previous"><img border="0" src="../img/lp_leftarrow.gif" title="' . get_lang('ScormPrevious') . '"></a>' . "\n" .
  1521. ' <a href="" onclick="dokeos_xajax_handler.switch_item(' . $mycurrentitemid . ',\'next\');return false;" title="next" ><img border="0" src="../img/lp_rightarrow.gif" title="' . get_lang('ScormNext') . '"></a>' . "\n" .
  1522. // ' <a href="lp_controller.php?action=mode&mode=fullscreen" target="_top" title="fullscreen"><img border="0" src="../img/view_fullscreen.gif" width="18" height="18" title="'.get_lang('ScormFullScreen').'"></a>'."\n" .
  1523. ' </div>' . "\n" .
  1524. ' </td>' . "\n" .
  1525. ' </tr>' . "\n" .
  1526. '</table>' . "\n";
  1527. }
  1528. return $navbar;
  1529. }
  1530. /**
  1531. * Gets the next resource in queue (url).
  1532. * @return string URL to load into the viewer
  1533. */
  1534. function get_next_index() {
  1535. if ($this->debug > 0) {
  1536. error_log('New LP - In learnpath::get_next_index()', 0);
  1537. }
  1538. //TODO
  1539. $index = $this->index;
  1540. $index++;
  1541. if ($this->debug > 2) {
  1542. error_log('New LP - Now looking at ordered_items[' . ($index) . '] - type is ' . $this->items[$this->ordered_items[$index]]->type, 0);
  1543. }
  1544. while (!empty ($this->ordered_items[$index]) AND ($this->items[$this->ordered_items[$index]]->get_type() == 'dir' || $this->items[$this->ordered_items[$index]]->get_type() == 'dokeos_chapter') AND $index < $this->max_ordered_items) {
  1545. $index++;
  1546. if ($index == $this->max_ordered_items) {
  1547. return $this->index;
  1548. }
  1549. }
  1550. if (empty ($this->ordered_items[$index])) {
  1551. return $this->index;
  1552. }
  1553. if ($this->debug > 2) {
  1554. error_log('New LP - index is now ' . $index, 0);
  1555. }
  1556. return $index;
  1557. }
  1558. /**
  1559. * Gets item_id for the next element
  1560. * @return integer Next item (DB) ID
  1561. */
  1562. function get_next_item_id() {
  1563. if ($this->debug > 0) {
  1564. error_log('New LP - In learnpath::get_next_item_id()', 0);
  1565. }
  1566. $new_index = $this->get_next_index();
  1567. if (!empty ($new_index)) {
  1568. if (isset ($this->ordered_items[$new_index])) {
  1569. if ($this->debug > 2) {
  1570. error_log('New LP - In learnpath::get_next_index() - Returning ' . $this->ordered_items[$new_index], 0);
  1571. }
  1572. return $this->ordered_items[$new_index];
  1573. }
  1574. }
  1575. if ($this->debug > 2) {
  1576. error_log('New LP - In learnpath::get_next_index() - Problem - Returning 0', 0);
  1577. }
  1578. return 0;
  1579. }
  1580. /**
  1581. * Returns the package type ('scorm','aicc','scorm2004','dokeos','ppt'...)
  1582. *
  1583. * Generally, the package provided is in the form of a zip file, so the function
  1584. * has been written to test a zip file. If not a zip, the function will return the
  1585. * default return value: ''
  1586. * @param string the path to the file
  1587. * @param string the original name of the file
  1588. * @return string 'scorm','aicc','scorm2004','dokeos' or '' if the package cannot be recognized
  1589. */
  1590. function get_package_type($file_path, $file_name) {
  1591. //get name of the zip file without the extension
  1592. $file_info = pathinfo($file_name);
  1593. $filename = $file_info['basename']; //name including extension
  1594. $extension = $file_info['extension']; //extension only
  1595. if (!empty ($_POST['ppt2lp']) && !in_array(strtolower($extension), array (
  1596. 'dll',
  1597. 'exe'
  1598. ))) {
  1599. return 'oogie';
  1600. }
  1601. if (!empty ($_POST['woogie']) && !in_array(strtolower($extension), array (
  1602. 'dll',
  1603. 'exe'
  1604. ))) {
  1605. return 'woogie';
  1606. }
  1607. $file_base_name = str_replace('.' . $extension, '', $filename); //filename without its extension
  1608. $zipFile = new pclZip($file_path);
  1609. // Check the zip content (real size and file extension)
  1610. $zipContentArray = $zipFile->listContent();
  1611. $package_type = '';
  1612. $at_root = false;
  1613. $manifest = '';
  1614. //the following loop should be stopped as soon as we found the right imsmanifest.xml (how to recognize it?)
  1615. if (is_array($zipContentArray) && count($zipContentArray) > 0) {
  1616. foreach ($zipContentArray as $thisContent) {
  1617. if (preg_match('~.(php.*|phtml)$~i', $thisContent['filename'])) {
  1618. //New behaviour: Don't do anything. These files will be removed in scorm::import_package
  1619. }
  1620. elseif (stristr($thisContent['filename'], 'imsmanifest.xml') !== FALSE) {
  1621. $manifest = $thisContent['filename']; //just the relative directory inside scorm/
  1622. $package_type = 'scorm';
  1623. break; //exit the foreach loop
  1624. }
  1625. elseif (preg_match('/aicc\//i', $thisContent['filename']) != false) {
  1626. //if found an aicc directory... (!= false means it cannot be false (error) or 0 (no match))
  1627. $package_type = 'aicc';
  1628. //break;//don't exit the loop, because if we find an imsmanifest afterwards, we want it, not the AICC
  1629. } else {
  1630. $package_type = '';
  1631. }
  1632. }
  1633. }
  1634. return $package_type;
  1635. }
  1636. /**
  1637. * Gets the previous resource in queue (url). Also initialises time values for this viewing
  1638. * @return string URL to load into the viewer
  1639. */
  1640. function get_previous_index() {
  1641. if ($this->debug > 0) {
  1642. error_log('New LP - In learnpath::get_previous_index()', 0);
  1643. }
  1644. $index = $this->index;
  1645. if (isset ($this->ordered_items[$index -1])) {
  1646. $index--;
  1647. while (isset ($this->ordered_items[$index]) AND ($this->items[$this->ordered_items[$index]]->get_type() == 'dir' || $this->items[$this->ordered_items[$index]]->get_type() == 'dokeos_chapter')) {
  1648. $index--;
  1649. if ($index < 0) {
  1650. return $this->index;
  1651. }
  1652. }
  1653. } else {
  1654. if ($this->debug > 2) {
  1655. error_log('New LP - get_previous_index() - there was no previous index available, reusing ' . $index, 0);
  1656. }
  1657. //no previous item
  1658. }
  1659. return $index;
  1660. }
  1661. /**
  1662. * Gets item_id for the next element
  1663. * @return integer Previous item (DB) ID
  1664. */
  1665. function get_previous_item_id() {
  1666. if ($this->debug > 0) {
  1667. error_log('New LP - In learnpath::get_previous_item_id()', 0);
  1668. }
  1669. $new_index = $this->get_previous_index();
  1670. return $this->ordered_items[$new_index];
  1671. }
  1672. /**
  1673. * Gets the progress value from the progress_db attribute
  1674. * @return integer Current progress value
  1675. */
  1676. function get_progress() {
  1677. if ($this->debug > 0) {
  1678. error_log('New LP - In learnpath::get_progress()', 0);
  1679. }
  1680. if (!empty ($this->progress_db)) {
  1681. return $this->progress_db;
  1682. }
  1683. return 0;
  1684. }
  1685. /**
  1686. * Gets the progress value from the progress field in the database (allows use as abstract method)
  1687. * @param integer Learnpath ID
  1688. * @param integer User ID
  1689. * @param string Mode of display ('%','abs' or 'both')
  1690. * @param string Course database name (optional, defaults to '')
  1691. * @param boolean Whether to return null if no record was found (true), or 0 (false) (optional, defaults to false)
  1692. * @return integer Current progress value as found in the database
  1693. */
  1694. function get_db_progress($lp_id, $user_id, $mode = '%', $course_db = '', $sincere = false) {
  1695. //if($this->debug>0){error_log('New LP - In learnpath::get_db_progress()',0);}
  1696. $table = Database :: get_course_table(TABLE_LP_VIEW, $course_db);
  1697. $sql = "SELECT * FROM $table WHERE lp_id = $lp_id AND user_id = $user_id";
  1698. $res = api_sql_query($sql, __FILE__, __LINE__);
  1699. $view_id = 0;
  1700. if (Database :: num_rows($res) > 0) {
  1701. $row = Database :: fetch_array($res);
  1702. $progress = $row['progress'];
  1703. $view_id = $row['id'];
  1704. } else {
  1705. if ($sincere) {
  1706. return null;
  1707. }
  1708. }
  1709. if (empty ($progress)) {
  1710. $progress = '0';
  1711. }
  1712. if ($mode == '%') {
  1713. return $progress . '%';
  1714. } else {
  1715. //get the number of items completed and the number of items total
  1716. $tbl = Database :: get_course_table(TABLE_LP_ITEM, $course_db);
  1717. $sql = "SELECT count(*) FROM $tbl WHERE lp_id = " . $lp_id . "
  1718. AND item_type NOT IN('dokeos_chapter','chapter','dir')";
  1719. $res = api_sql_query($sql, __FILE__, __LINE__);
  1720. $row = Database :: fetch_array($res);
  1721. $total = $row[0];
  1722. $tbl_item_view = Database :: get_course_table(TABLE_LP_ITEM_VIEW, $course_db);
  1723. $tbl_item = Database :: get_course_table(TABLE_LP_ITEM, $course_db);
  1724. //$sql = "SELECT count(distinct(lp_item_id)) FROM $tbl WHERE lp_view_id = ".$view_id." AND status IN ('passed','completed','succeeded')";
  1725. //trying as also counting browsed and failed items
  1726. $sql = "SELECT count(distinct(lp_item_id))
  1727. FROM $tbl_item_view as item_view
  1728. INNER JOIN $tbl_item as item
  1729. ON item.id = item_view.lp_item_id
  1730. AND item_type NOT IN('dokeos_chapter','chapter','dir')
  1731. WHERE lp_view_id = " . $view_id . "
  1732. AND status IN ('passed','completed','succeeded','browsed','failed')";
  1733. $res = api_sql_query($sql, __FILE__, __LINE__);
  1734. $row = Database :: fetch_array($res);
  1735. $completed = $row[0];
  1736. if ($mode == 'abs') {
  1737. return $completed . '/' . $total;
  1738. }
  1739. elseif ($mode == 'both') {
  1740. if ($progress < ($completed / ($total ? $total : 1))) {
  1741. $progress = number_format(($completed / ($total ? $total : 1)) * 100, 0);
  1742. }
  1743. return $progress . '% (' . $completed . '/' . $total . ')';
  1744. }
  1745. }
  1746. return $progress;
  1747. }
  1748. /**
  1749. * Returns the HTML necessary to print a mediaplayer block inside a page
  1750. * @return string The mediaplayer HTML
  1751. */
  1752. function get_mediaplayer($autostart='true') {
  1753. global $_course;
  1754. // Database table definition
  1755. $tbl_lp_item = Database :: get_course_table(TABLE_LP_ITEM);
  1756. $tbl_lp_item_view = Database :: get_course_table(TABLE_LP_ITEM_VIEW);
  1757. // getting all the information about the item
  1758. $sql = "SELECT * FROM " . $tbl_lp_item . " as lp inner join " . $tbl_lp_item_view . " as lp_view on lp.id = lp_view.lp_item_id " .
  1759. "WHERE lp.id = '" . $_SESSION['oLP']->current . "'";
  1760. $result = api_sql_query($sql, __FILE__, __LINE__);
  1761. $row = mysql_fetch_assoc($result);
  1762. $output = '';
  1763. if (!empty ($row['audio'])) {
  1764. $list = $_SESSION['oLP']->get_toc();
  1765. $type_quiz = false;
  1766. foreach($list as $toc) {
  1767. if ($toc['id'] == $_SESSION['oLP']->current && ($toc['type']=='quiz') ) {
  1768. $type_quiz = true;
  1769. }
  1770. }
  1771. if ($type_quiz) {
  1772. if ($_SESSION['oLP']->prevent_reinit == 1) {
  1773. $row['status'] === 'completed' ? $autostart_audio = 'false' : $autostart_audio = 'true';
  1774. } else {
  1775. $autostart_audio = $autostart;
  1776. }
  1777. } else {
  1778. $autostart_audio = 'true';
  1779. }
  1780. // the mp3 player
  1781. $output = '<div id="container">';
  1782. $output .= '<script type="text/javascript" src="../inc/lib/mediaplayer/swfobject.js"></script>';
  1783. $output .= '<script type="text/javascript">
  1784. var s1 = new SWFObject("../inc/lib/mediaplayer/player.swf","ply","250","20","9","#FFFFFF");
  1785. s1.addParam("allowscriptaccess","always");
  1786. s1.addParam("flashvars","file=' . api_get_path(WEB_COURSE_PATH) . $_course['path'] . '/document/audio/' . $row['audio'] . '&autostart=' . $autostart_audio.'");
  1787. s1.write("container");
  1788. </script></div>';
  1789. }
  1790. return $output;
  1791. }
  1792. /**
  1793. * Gets a progress bar for the learnpath by counting the number of items in it and the number of items
  1794. * completed so far.
  1795. * @param string Mode in which we want the values
  1796. * @param integer Progress value to display (optional but mandatory if used in abstract context)
  1797. * @param string Text to display near the progress value (optional but mandatory in abstract context)
  1798. * @param boolean true if it comes from a Diplay LP view
  1799. * @return string HTML string containing the progress bar
  1800. */
  1801. function get_progress_bar($mode = '', $percentage = -1, $text_add = '', $from_lp = false) {
  1802. //if($this->debug>0){error_log('New LP - In learnpath::get_progress_bar()',0);}
  1803. global $lp_theme_css;
  1804. // Setting up the CSS path of the current style if exists
  1805. if (!empty ($lp_theme_css)) {
  1806. $css_path = api_get_path(WEB_CODE_PATH) . 'css/' . $lp_theme_css . '/images/';
  1807. } else {
  1808. $css_path = '../img/';
  1809. }
  1810. //if($this->debug>0){error_log('New LP - In learnpath::get_progress_bar()',0);}
  1811. if (isset ($this) && is_object($this) && ($percentage == '-1' OR $text_add == '')) {
  1812. list ($percentage, $text_add) = $this->get_progress_bar_text($mode);
  1813. }
  1814. $text = $percentage . $text_add;
  1815. //Default progress bar config
  1816. // times that will be greater or shorter
  1817. $factor = 1.2;
  1818. if ($from_lp)
  1819. $progress_height = '26';
  1820. else
  1821. $progress_height = '16';
  1822. $size = str_replace('%', '', $percentage);
  1823. $output = ''
  1824. //.htmlentities(get_lang('ScormCompstatus'),ENT_QUOTES,'ISO-8859-1')."<br />"
  1825. . '<table border="0" cellpadding="0" cellspacing="0"><tr><td>' .
  1826. '<img id="progress_img_limit_left" src="' . $css_path . 'bar_1.gif" width="1" height="' . $progress_height . '">' .
  1827. '<img id="progress_img_full" src="' . $css_path . 'bar_1u.gif" width="' . $size * $factor . 'px" height="' . $progress_height . '" id="full_portion">' .
  1828. '<img id="progress_img_limit_middle" src="' . $css_path . 'bar_1m.gif" width="1" height="' . $progress_height . '">';
  1829. if ($percentage <= 98) {
  1830. $output .= '<img id="progress_img_empty" src="' . $css_path . 'bar_1r.gif" width="' . (100 - $size) * $factor . 'px" height="' . $progress_height . '" id="empty_portion">';
  1831. } else {
  1832. $output .= '<img id="progress_img_empty" src="' . $css_path . 'bar_1r.gif" width="0" height="' . $progress_height . '" id="empty_portion">';
  1833. }
  1834. $output .= '<img id="progress_bar_img_limit_right" src="' . $css_path . 'bar_1.gif" width="1" height="' . $progress_height . '"></td></tr></table>' .
  1835. '<div class="progresstext" id="progress_text">' . $text . '</div>';
  1836. return $output;
  1837. }
  1838. /**
  1839. * Gets the progress bar info to display inside the progress bar. Also used by scorm_api.php
  1840. * @param string Mode of display (can be '%' or 'abs').abs means we display a number of completed elements per total elements
  1841. * //@param integer Additional steps to fake as completed
  1842. * @return list Percentage or number and symbol (% or /xx)
  1843. */
  1844. function get_progress_bar_text($mode = '', $add = 0) {
  1845. if ($this->debug > 0) {
  1846. error_log('New LP - In learnpath::get_progress_bar_text()', 0);
  1847. }
  1848. if (empty ($mode)) {
  1849. $mode = $this->progress_bar_mode;
  1850. }
  1851. $total_items = $this->get_total_items_count_without_chapters();
  1852. if ($this->debug > 2) {
  1853. error_log('New LP - Total items available in this learnpath: ' . $total_items, 0);
  1854. }
  1855. $i = $this->get_complete_items_count();
  1856. if ($this->debug > 2) {
  1857. error_log('New LP - Items completed so far: ' . $i, 0);
  1858. }
  1859. if ($add != 0) {
  1860. $i += $add;
  1861. if ($this->debug > 2) {
  1862. error_log('New LP - Items completed so far (+modifier): ' . $i, 0);
  1863. }
  1864. }
  1865. $text = '';
  1866. if ($i > $total_items) {
  1867. $i = $total_items;
  1868. }
  1869. if ($mode == '%') {
  1870. if ($total_items > 0) {
  1871. $percentage = ((float) $i / (float) $total_items) * 100;
  1872. } else {
  1873. $percentage = 0;
  1874. }
  1875. $percentage = number_format($percentage, 0);
  1876. $text = '%';
  1877. }
  1878. elseif ($mode == 'abs') {
  1879. $percentage = $i;
  1880. $text = '/' . $total_items;
  1881. }
  1882. return array (
  1883. $percentage,
  1884. $text
  1885. );
  1886. }
  1887. /**
  1888. * Gets the progress bar mode
  1889. * @return string The progress bar mode attribute
  1890. */
  1891. function get_progress_bar_mode() {
  1892. if ($this->debug > 0) {
  1893. error_log('New LP - In learnpath::get_progress_bar_mode()', 0);
  1894. }
  1895. if (!empty ($this->progress_bar_mode)) {
  1896. return $this->progress_bar_mode;
  1897. } else {
  1898. return '%';
  1899. }
  1900. }
  1901. /**
  1902. * Gets the learnpath proximity (remote or local)
  1903. * @return string Learnpath proximity
  1904. */
  1905. function get_proximity() {
  1906. if ($this->debug > 0) {
  1907. error_log('New LP - In learnpath::get_proximity()', 0);
  1908. }
  1909. if (!empty ($this->proximity)) {
  1910. return $this->proximity;
  1911. } else {
  1912. return '';
  1913. }
  1914. }
  1915. /**
  1916. * Gets the learnpath theme (remote or local)
  1917. * @return string Learnpath theme
  1918. */
  1919. function get_theme() {
  1920. if ($this->debug > 0) {
  1921. error_log('New LP - In learnpath::get_theme()', 0);
  1922. }
  1923. if (!empty ($this->theme)) {
  1924. return $this->theme;
  1925. } else {
  1926. return '';
  1927. }
  1928. }
  1929. /**
  1930. * Gets the learnpath image
  1931. * @return string Web URL of the LP image
  1932. */
  1933. function get_preview_image() {
  1934. if ($this->debug > 0) {
  1935. error_log('New LP - In learnpath::get_preview_image()', 0);
  1936. }
  1937. if (!empty ($this->preview_image)) {
  1938. return $this->preview_image;
  1939. } else {
  1940. return '';
  1941. }
  1942. }
  1943. /**
  1944. * Gets the learnpath author
  1945. * @return string LP's author
  1946. */
  1947. function get_author() {
  1948. if ($this->debug > 0) {
  1949. error_log('New LP - In learnpath::get_author()', 0);
  1950. }
  1951. if (!empty ($this->author)) {
  1952. return $this->author;
  1953. } else {
  1954. return '';
  1955. }
  1956. }
  1957. /**
  1958. * Generate a new prerequisites string for a given item. If this item was a sco and
  1959. * its prerequisites were strings (instead of IDs), then transform those strings into
  1960. * IDs, knowing that SCORM IDs are kept in the "ref" field of the lp_item table.
  1961. * Prefix all item IDs that end-up in the prerequisites string by "ITEM_" to use the
  1962. * same rule as the scorm_export() method
  1963. * @param integer Item ID
  1964. * @return string Prerequisites string ready for the export as SCORM
  1965. */
  1966. function get_scorm_prereq_string($item_id) {
  1967. if ($this->debug > 0) {
  1968. error_log('New LP - In learnpath::get_scorm_prereq_string()', 0);
  1969. }
  1970. if (!is_object($this->items[$item_id])) {
  1971. return false;
  1972. }
  1973. $oItem = $this->items[$item_id];
  1974. $prereq = $oItem->get_prereq_string();
  1975. if (empty ($prereq)) {
  1976. return '';
  1977. }
  1978. if (preg_match('/^\d+$/', $prereq) && is_object($this->items[$prereq])) { //if the prerequisite is a simple integer ID and this ID exists as an item ID,
  1979. //then simply return it (with the ITEM_ prefix)
  1980. return 'ITEM_' . $prereq;
  1981. } else {
  1982. if (isset ($this->refs_list[$prereq])) {
  1983. //it's a simple string item from which the ID can be found in the refs list
  1984. //so we can transform it directly to an ID for export
  1985. return 'ITEM_' . $this->refs_list[$prereq];
  1986. } else {
  1987. //last case, if it's a complex form, then find all the IDs (SCORM strings)
  1988. //and replace them, one by one, by the internal IDs (dokeos db)
  1989. //TODO modify the '*' replacement to replace the multiplier in front of it
  1990. //by a space as well
  1991. $find = array (
  1992. '&',
  1993. '|',
  1994. '~',
  1995. '=',
  1996. '<>',
  1997. '{',
  1998. '}',
  1999. '*',
  2000. '(',
  2001. ')'
  2002. );
  2003. $replace = array (
  2004. ' ',
  2005. ' ',
  2006. ' ',
  2007. ' ',
  2008. ' ',
  2009. ' ',
  2010. ' ',
  2011. ' ',
  2012. ' ',
  2013. ' '
  2014. );
  2015. $prereq_mod = str_replace($find, $replace, $prereq);
  2016. $ids = split(' ', $prereq_mod);
  2017. foreach ($ids as $id) {
  2018. $id = trim($id);
  2019. if (isset ($this->refs_list[$id])) {
  2020. $prereq = preg_replace('/[^a-zA-Z_0-9](' . $id . ')[^a-zA-Z_0-9]/', 'ITEM_' . $this->refs_list[$id], $prereq);
  2021. }
  2022. }
  2023. error_log('New LP - In learnpath::get_scorm_prereq_string(): returning modified string: ' . $prereq, 0);
  2024. return $prereq;
  2025. }
  2026. }
  2027. }
  2028. /**
  2029. * Returns the XML DOM document's node
  2030. * @param resource Reference to a list of objects to search for the given ITEM_*
  2031. * @param string The identifier to look for
  2032. * @return mixed The reference to the element found with that identifier. False if not found
  2033. */
  2034. function get_scorm_xml_node(& $children, $id) {
  2035. for ($i = 0; $i < $children->length; $i++) {
  2036. $item_temp = $children->item($i);
  2037. if ($item_temp->nodeName == 'item') {
  2038. if ($item_temp->getAttribute('identifier') == $id) {
  2039. return $item_temp;
  2040. }
  2041. }
  2042. $subchildren = $item_temp->childNodes;
  2043. if ($subchildren->length > 0) {
  2044. $val = $this->get_scorm_xml_node($subchildren, $id);
  2045. if (is_object($val)) {
  2046. return $val;
  2047. }
  2048. }
  2049. }
  2050. return false;
  2051. }
  2052. /**
  2053. * Returns a usable array of stats related to the current learnpath and user
  2054. * @return array Well-formatted array containing status for the current learnpath
  2055. */
  2056. function get_stats() {
  2057. if ($this->debug > 0) {
  2058. error_log('New LP - In learnpath::get_stats()', 0);
  2059. }
  2060. //TODO
  2061. }
  2062. /**
  2063. * Static method. Can be re-implemented by children. Gives an array of statistics for
  2064. * the given course (for all learnpaths and all users)
  2065. * @param string Course code
  2066. * @return array Well-formatted array containing status for the course's learnpaths
  2067. */
  2068. function get_stats_course($course) {
  2069. //if($this->debug>0){error_log('New LP - In learnpath::get_stats_course()',0);}
  2070. //TODO
  2071. }
  2072. /**
  2073. * Static method. Can be re-implemented by children. Gives an array of statistics for
  2074. * the given course and learnpath (for all users)
  2075. * @param string Course code
  2076. * @param integer Learnpath ID
  2077. * @return array Well-formatted array containing status for the specified learnpath
  2078. */
  2079. function get_stats_lp($course, $lp) {
  2080. //if($this->debug>0){error_log('New LP - In learnpath::get_stats_lp()',0);}
  2081. //TODO
  2082. }
  2083. /**
  2084. * Static method. Can be re-implemented by children. Gives an array of statistics for
  2085. * the given course, learnpath and user.
  2086. * @param string Course code
  2087. * @param integer Learnpath ID
  2088. * @param integer User ID
  2089. * @return array Well-formatted array containing status for the specified learnpath and user
  2090. */
  2091. function get_stats_lp_user($course, $lp, $user) {
  2092. //if($this->debug>0){error_log('New LP - In learnpath::get_stats_lp_user()',0);}
  2093. //TODO
  2094. }
  2095. /**
  2096. * Static method. Can be re-implemented by children. Gives an array of statistics for
  2097. * the given course and learnpath (for all users)
  2098. * @param string Course code
  2099. * @param integer User ID
  2100. * @return array Well-formatted array containing status for the user's learnpaths
  2101. */
  2102. function get_stats_user($course, $user) {
  2103. //if($this->debug>0){error_log('New LP - In learnpath::get_stats_user()',0);}
  2104. //TODO
  2105. }
  2106. /**
  2107. * Gets the status list for all LP's items
  2108. * @return array Array of [index] => [item ID => current status]
  2109. */
  2110. function get_items_status_list() {
  2111. if ($this->debug > 0) {
  2112. error_log('New LP - In learnpath::get_items_status_list()', 0);
  2113. }
  2114. $list = array ();
  2115. foreach ($this->ordered_items as $item_id) {
  2116. $list[] = array (
  2117. $item_id => $this->items[$item_id]->get_status()
  2118. );
  2119. }
  2120. return $list;
  2121. }
  2122. /**
  2123. * Return the number of interactions for the given learnpath Item View ID.
  2124. * This method can be used as static.
  2125. * @param integer Item View ID
  2126. * @return integer Number of interactions
  2127. */
  2128. function get_interactions_count_from_db($lp_iv_id = 0) {
  2129. if (empty ($lp_iv_id)) {
  2130. return -1;
  2131. }
  2132. $table = Database :: get_course_table(TABLE_LP_IV_INTERACTION);
  2133. $sql = "SELECT count(*) FROM $table WHERE lp_iv_id = $lp_iv_id";
  2134. $res = api_sql_query($sql, __FILE__, __LINE__);
  2135. $row = Database :: fetch_array($res);
  2136. $num = $row[0];
  2137. return $num;
  2138. }
  2139. /**
  2140. * Return the interactions as an array for the given lp_iv_id.
  2141. * This method can be used as static.
  2142. * @param integer Learnpath Item View ID
  2143. * @return array
  2144. * @todo Transcode labels instead of switching to HTML (which requires to know the encoding of the LP)
  2145. */
  2146. function get_iv_interactions_array($lp_iv_id = 0) {
  2147. $charset = api_get_setting('platform_charset');
  2148. $list = array ();
  2149. $table = Database :: get_course_table(TABLE_LP_IV_INTERACTION);
  2150. $sql = "SELECT * FROM $table WHERE lp_iv_id = $lp_iv_id ORDER BY order_id ASC";
  2151. $res = api_sql_query($sql, __FILE__, __LINE__);
  2152. $num = Database :: num_rows($res);
  2153. if ($num > 0) {
  2154. $list[] = array (
  2155. "order_id" => api_htmlentities(get_lang('Order'), ENT_QUOTES, $charset),
  2156. "id" => api_htmlentities(get_lang('InteractionID'), ENT_QUOTES, $charset),
  2157. "type" => api_htmlentities(get_lang('Type'), ENT_QUOTES, $charset),
  2158. "time" => api_htmlentities(get_lang('TimeFinished'), ENT_QUOTES, $charset),
  2159. "correct_responses" => api_htmlentities(get_lang('CorrectAnswers'), ENT_QUOTES, $charset),
  2160. "student_response" => api_htmlentities(get_lang('StudentResponse'), ENT_QUOTES, $charset),
  2161. "result" => api_htmlentities(get_lang('Result'), ENT_QUOTES, $charset),
  2162. "latency" => api_htmlentities(get_lang('LatencyTimeSpent'), ENT_QUOTES, $charset)
  2163. );
  2164. while ($row = Database :: fetch_array($res)) {
  2165. $list[] = array (
  2166. "order_id" => ($row['order_id'] + 1),
  2167. "id" => urldecode($row['interaction_id']), //urldecode because they often have %2F or stuff like that
  2168. "type" => $row['interaction_type'],
  2169. "time" => $row['completion_time'],
  2170. //"correct_responses"=>$row['correct_responses'],
  2171. //hide correct responses from students
  2172. "correct_responses" => '',
  2173. "student_response" => $row['student_response'],
  2174. "result" => $row['result'],
  2175. "latency" => $row['latency']
  2176. );
  2177. }
  2178. }
  2179. return $list;
  2180. }
  2181. /**
  2182. * Return the number of objectives for the given learnpath Item View ID.
  2183. * This method can be used as static.
  2184. * @param integer Item View ID
  2185. * @return integer Number of objectives
  2186. */
  2187. function get_objectives_count_from_db($lp_iv_id = 0) {
  2188. if (empty ($lp_iv_id)) {
  2189. return -1;
  2190. }
  2191. $table = Database :: get_course_table(TABLE_LP_IV_OBJECTIVE);
  2192. $sql = "SELECT count(*) FROM $table WHERE lp_iv_id = $lp_iv_id";
  2193. $res = api_sql_query($sql, __FILE__, __LINE__);
  2194. $row = Database :: fetch_array($res);
  2195. $num = $row[0];
  2196. return $num;
  2197. }
  2198. /**
  2199. * Return the objectives as an array for the given lp_iv_id.
  2200. * This method can be used as static.
  2201. * @param integer Learnpath Item View ID
  2202. * @return array
  2203. * @todo Translate labels
  2204. */
  2205. function get_iv_objectives_array($lp_iv_id = 0) {
  2206. global $chatset;
  2207. $list = array ();
  2208. $table = Database :: get_course_table(TABLE_LP_IV_OBJECTIVE);
  2209. $sql = "SELECT * FROM $table WHERE lp_iv_id = $lp_iv_id ORDER BY order_id ASC";
  2210. $res = api_sql_query($sql, __FILE__, __LINE__);
  2211. $num = Database :: num_rows($res);
  2212. if ($num > 0) {
  2213. $list[] = array (
  2214. "order_id" => api_htmlentities(get_lang('Order'), ENT_QUOTES, $charset),
  2215. "objective_id" => api_htmlentities(get_lang('ObjectiveID'), ENT_QUOTES, $charset),
  2216. "score_raw" => api_htmlentities(get_lang('ObjectiveRawScore'), ENT_QUOTES, $charset),
  2217. "score_max" => api_htmlentities(get_lang('ObjectiveMaxScore'), ENT_QUOTES, $charset),
  2218. "score_min" => api_htmlentities(get_lang('ObjectiveMinScore'), ENT_QUOTES, $charset),
  2219. "status" => api_htmlentities(get_lang('ObjectiveStatus'), ENT_QUOTES, $charset)
  2220. );
  2221. while ($row = Database :: fetch_array($res)) {
  2222. $list[] = array (
  2223. "order_id" => ($row['order_id'] + 1),
  2224. "objective_id" => urldecode($row['objective_id']), //urldecode because they often have %2F or stuff like that
  2225. "score_raw" => $row['score_raw'],
  2226. "score_max" => $row['score_max'],
  2227. "score_min" => $row['score_min'],
  2228. "status" => $row['status']
  2229. );
  2230. }
  2231. }
  2232. return $list;
  2233. }
  2234. /**
  2235. * Generate and return the table of contents for this learnpath. The (flat) table returned can be
  2236. * used by get_html_toc() to be ready to display
  2237. * @return array TOC as a table with 4 elements per row: title, link, status and level
  2238. */
  2239. function get_toc() {
  2240. if ($this->debug > 0) {
  2241. error_log('New LP - In learnpath::get_toc()', 0);
  2242. }
  2243. $toc = array ();
  2244. //echo "<pre>".print_r($this->items,true)."</pre>";
  2245. foreach ($this->ordered_items as $item_id) {
  2246. if ($this->debug > 2) {
  2247. error_log('New LP - learnpath::get_toc(): getting info for item ' . $item_id, 0);
  2248. }
  2249. //TODO change this link generation and use new function instead
  2250. $toc[] = array (
  2251. 'id' => $item_id,
  2252. 'title' => $this->items[$item_id]->get_title(),
  2253. //'link'=>get_addedresource_link_in_learnpath('document',$item_id,1),
  2254. 'status' => $this->items[$item_id]->get_status(),
  2255. 'level' => $this->items[$item_id]->get_level(),
  2256. 'type' => $this->items[$item_id]->get_type(),
  2257. 'description' => $this->items[$item_id]->get_description(),
  2258. 'path' => $this->items[$item_id]->get_path(),
  2259. );
  2260. }
  2261. if ($this->debug > 2) {
  2262. error_log('New LP - In learnpath::get_toc() - TOC array: ' . print_r($toc, true), 0);
  2263. }
  2264. return $toc;
  2265. }
  2266. /**
  2267. * Gets the learning path type
  2268. * @param boolean Return the name? If false, return the ID. Default is false.
  2269. * @return mixed Type ID or name, depending on the parameter
  2270. */
  2271. function get_type($get_name = false) {
  2272. $res = false;
  2273. if ($this->debug > 0) {
  2274. error_log('New LP - In learnpath::get_type()', 0);
  2275. }
  2276. if (!empty ($this->type)) {
  2277. if ($get_name) {
  2278. //get it from the lp_type table in main db
  2279. } else {
  2280. $res = $this->type;
  2281. }
  2282. }
  2283. if ($this->debug > 2) {
  2284. error_log('New LP - In learnpath::get_type() - Returning ' . ($res == false ? 'false' : $res), 0);
  2285. }
  2286. return $res;
  2287. }
  2288. /**
  2289. * Gets the learning path type as static method
  2290. * @param boolean Return the name? If false, return the ID. Default is false.
  2291. * @return mixed Type ID or name, depending on the parameter
  2292. */
  2293. function get_type_static($lp_id = 0) {
  2294. $tbl_lp = Database :: get_course_table(TABLE_LP_MAIN);
  2295. $sql = "SELECT lp_type FROM $tbl_lp WHERE id = '" . $lp_id . "'";
  2296. $res = api_sql_query($sql, __FILE__, __LINE__);
  2297. if ($res === false) {
  2298. return null;
  2299. }
  2300. if (Database :: num_rows($res) <= 0) {
  2301. return null;
  2302. }
  2303. $row = Database :: fetch_array($res);
  2304. return $row['lp_type'];
  2305. }
  2306. /**
  2307. * Gets a flat list of item IDs ordered for display (level by level ordered by order_display)
  2308. * This method can be used as abstract and is recursive
  2309. * @param integer Learnpath ID
  2310. * @param integer Parent ID of the items to look for
  2311. * @return mixed Ordered list of item IDs or false on error
  2312. */
  2313. function get_flat_ordered_items_list($lp, $parent = 0) {
  2314. //if($this->debug>0){error_log('New LP - In learnpath::get_flat_ordered_items_list('.$lp.','.$parent.')',0);}
  2315. $list = array ();
  2316. if (empty ($lp)) {
  2317. return false;
  2318. }
  2319. $tbl_lp_item = Database :: get_course_table(TABLE_LP_ITEM);
  2320. $sql = "SELECT * FROM $tbl_lp_item WHERE lp_id = $lp AND parent_item_id = $parent ORDER BY display_order";
  2321. $res = api_sql_query($sql, __FILE__, __LINE__);
  2322. while ($row = Database :: fetch_array($res)) {
  2323. $sublist = learnpath :: get_flat_ordered_items_list($lp, $row['id']);
  2324. $list[] = $row['id'];
  2325. foreach ($sublist as $item) {
  2326. $list[] = $item;
  2327. }
  2328. }
  2329. return $list;
  2330. }
  2331. /**
  2332. * Uses the table generated by get_toc() and returns an HTML-formatted string ready to display
  2333. * @return string HTML TOC ready to display
  2334. */
  2335. /*function get_html_toc()
  2336. {
  2337. if($this->debug>0){error_log('New LP - In learnpath::get_html_toc()',0);}
  2338. $list = $this->get_toc();
  2339. //echo $this->current;
  2340. //$parent = $this->items[$this->current]->get_parent();
  2341. //if(empty($parent)){$parent = $this->ordered_items[$this->items[$this->current]->get_previous_index()];}
  2342. $html = '<div class="inner_lp_toc">'."\n" ;
  2343. // " onchange=\"javascript:document.getElementById('toc_$parent').focus();\">\n";
  2344. require_once('resourcelinker.inc.php');
  2345. //temp variables
  2346. $mycurrentitemid = $this->get_current_item_id();
  2347. foreach($list as $item)
  2348. {
  2349. if($this->debug>2){error_log('New LP - learnpath::get_html_toc(): using item '.$item['id'],0);}
  2350. //TODO complete this
  2351. $icon_name = array('not attempted' => '../img/notattempted.gif',
  2352. 'incomplete' => '../img/incomplete.gif',
  2353. 'failed' => '../img/failed.gif',
  2354. 'completed' => '../img/completed.gif',
  2355. 'passed' => '../img/passed.gif',
  2356. 'succeeded' => '../img/succeeded.gif',
  2357. 'browsed' => '../img/completed.gif');
  2358. $style = 'scorm_item';
  2359. if($item['id'] == $this->current){
  2360. $style = 'scorm_item_highlight';
  2361. }
  2362. //the anchor will let us center the TOC on the currently viewed item &^D
  2363. $html .= '<a name="atoc_'.$item['id'].'" /><div class="'.$style.'" style="padding-left: '.($item['level']/2).'em; padding-right:'.($item['level']/2).'em" id="toc_'.$item['id'].'" >' .
  2364. '<img id="toc_img_'.$item['id'].'" class="scorm_status_img" src="'.$icon_name[$item['status']].'" alt="'.substr($item['status'],0,1).'" />';
  2365. //$title = htmlspecialchars($item['title'],ENT_QUOTES,$this->encoding);
  2366. $title = $item['title'];
  2367. if(empty($title)){
  2368. $title = rl_get_resource_name(api_get_course_id(),$this->get_id(),$item['id']);
  2369. $title = htmlspecialchars($title,ENT_QUOTES,$this->encoding);
  2370. }
  2371. if(empty($title))$title = '-';
  2372. if($item['type']!='dokeos_chapter' and $item['type']!='dir'){
  2373. //$html .= "<a href='lp_controller.php?".api_get_cidReq()."&action=content&lp_id=".$this->get_id()."&item_id=".$item['id']."' target='lp_content_frame_name'>".$title."</a>" ;
  2374. $url = $this->get_link('http',$item['id']);
  2375. //$html .= '<a href="'.$url.'" target="content_name" onclick="top.load_item('.$item['id'].',\''.$url.'\');">'.$title.'</a>' ;
  2376. //$html .= '<a href="" onclick="top.load_item('.$item['id'].',\''.$url.'\');return false;">'.$title.'</a>' ;
  2377. $html .= '<a href="" onclick="dokeos_xajax_handler.switch_item(' .
  2378. $mycurrentitemid.',' .
  2379. $item['id'].');' .
  2380. 'return false;" >'.$title.'</a>' ;
  2381. }else{
  2382. $html .= $title;
  2383. }
  2384. $html .= "</div>\n";
  2385. }
  2386. $html .= "</div>\n";
  2387. return $html;
  2388. }*/
  2389. /**
  2390. * Uses the table generated by get_toc() and returns an HTML-formatted string ready to display
  2391. * @return string HTML TOC ready to display
  2392. */
  2393. function get_html_toc() {
  2394. $charset = api_get_setting('platform_charset');
  2395. $display_action_links_with_icons = false;
  2396. if ($this->debug > 0) {
  2397. error_log('New LP - In learnpath::get_html_toc()', 0);
  2398. }
  2399. $list = $this->get_toc();
  2400. //echo $this->current;
  2401. //$parent = $this->items[$this->current]->get_parent();
  2402. //if(empty($parent)){$parent = $this->ordered_items[$this->items[$this->current]->get_previous_index()];}
  2403. $html = '<div class="scorm_title"><div class="scorm_title_text">' . Security::remove_XSS(api_convert_encoding($this->get_name(), $this->encoding, $charset)) . '</div></div>';
  2404. // build, display
  2405. if (api_is_allowed_to_edit()) {
  2406. $gradebook = Security :: remove_XSS($_GET['gradebook']);
  2407. $html .= '<div class="actions_lp">';
  2408. if ($display_action_links_with_icons) {
  2409. $html .= "<a href='lp_controller.php?" . api_get_cidreq() . "&amp;action=build&amp;lp_id=" . $this->lp_id . "' target='_parent'>" . Display :: return_icon('learnpath_build.gif', get_lang('Build')) . ' ' . get_lang('Build') . "</a>";
  2410. $html .= "<a href='lp_controller.php?" . api_get_cidreq() . "&amp;action=admin_view&amp;lp_id=" . $this->lp_id . "' target='_parent'>" . Display :: return_icon('learnpath_organize.gif', get_lang('BasicOverview')) . ' ' . get_lang('BasicOverview') . "</a>";
  2411. $html .= '<span>' . Display :: return_icon('learnpath_view_na.gif', get_lang("Display")) . ' <b>' . get_lang("Display") . '</b></span>';
  2412. } else {
  2413. $html .= "<a href='lp_controller.php?" . api_get_cidreq() . "&amp;gradebook=$gradebook&amp;action=build&amp;lp_id=" . $this->lp_id . "' target='_parent'>" . get_lang('Build') . "</a>";
  2414. $html .= "<a href='lp_controller.php?" . api_get_cidreq() . "&amp;action=admin_view&amp;lp_id=" . $this->lp_id . "' target='_parent'>" . get_lang('BasicOverview') . "</a>";
  2415. $html .= '<span><b>' . get_lang('Display') . '</b></span>';
  2416. }
  2417. $html .= '</div>';
  2418. }
  2419. $html .= '<div id="inner_lp_toc" class="inner_lp_toc">' . "\n";
  2420. require_once ('resourcelinker.inc.php');
  2421. //temp variables
  2422. $mycurrentitemid = $this->get_current_item_id();
  2423. $color_counter = 0;
  2424. $i = 0;
  2425. foreach ($list as $item) {
  2426. if ($this->debug > 2) {
  2427. error_log('New LP - learnpath::get_html_toc(): using item ' . $item['id'], 0);
  2428. }
  2429. //TODO complete this
  2430. $icon_name = array (
  2431. 'not attempted' => '../img/notattempted.gif',
  2432. 'incomplete' => '../img/incomplete.gif',
  2433. 'failed' => '../img/failed.gif',
  2434. 'completed' => '../img/completed.gif',
  2435. 'passed' => '../img/passed.gif',
  2436. 'succeeded' => '../img/succeeded.gif',
  2437. 'browsed' => '../img/completed.gif'
  2438. );
  2439. $style = 'scorm_item';
  2440. $scorm_color_background = 'scorm_item';
  2441. $style_item = 'scorm_item';
  2442. $current = false;
  2443. if ($item['id'] == $this->current) {
  2444. $style = 'scorm_item_highlight';
  2445. $scorm_color_background = 'scorm_item_highlight';
  2446. } else
  2447. if ($color_counter % 2 == 0) {
  2448. $scorm_color_background = 'scorm_item_1';
  2449. } else {
  2450. $scorm_color_background = 'scorm_item_2';
  2451. }
  2452. if ($scorm_color_background != '') {
  2453. $html .= '<div id="toc_' . $item['id'] . '" class="' . $scorm_color_background . '">';
  2454. }
  2455. //the anchor will let us center the TOC on the currently viewed item &^D
  2456. if ($item['type'] != 'dokeos_module' AND $item['type'] != 'dokeos_chapter') {
  2457. $html .= '<a name="atoc_' . $item['id'] . '" />';
  2458. $html .= '<div class="' . $style_item . '" style="padding-left: ' . ($item['level'] * 1.5) . 'em; padding-right:' . ($item['level'] / 2) . 'em" title="' . $item['description'] . '" >';
  2459. } else {
  2460. $html .= '<div class="' . $style_item . '" style="padding-left: ' . ($item['level'] * 2) . 'em; padding-right:' . ($item['level'] * 1.5) . 'em" title="' . $item['description'] . '" >';
  2461. }
  2462. $title = $item['title'];
  2463. if (empty ($title)) {
  2464. $title = rl_get_resource_name(api_get_course_id(), $this->get_id(), $item['id']);
  2465. }
  2466. //$title = api_htmlentities($title, ENT_QUOTES, $this->encoding);
  2467. $title = Security::remove_XSS($title);
  2468. if ($item['type'] != 'dokeos_chapter' and $item['type'] != 'dir' AND $item['type'] != 'dokeos_module') {
  2469. //$html .= "<a href='lp_controller.php?".api_get_cidreq()."&action=content&lp_id=".$this->get_id()."&item_id=".$item['id']."' target='lp_content_frame_name'>".$title."</a>" ;
  2470. $url = $this->get_link('http', $item['id']);
  2471. //$html .= '<a href="'.$url.'" target="content_name" onclick="top.load_item('.$item['id'].',\''.$url.'\');">'.$title.'</a>' ;
  2472. //$html .= '<a href="" onclick="top.load_item('.$item['id'].',\''.$url.'\');return false;">'.$title.'</a>' ;
  2473. //<img align="absbottom" width="13" height="13" src="../img/lp_document.png">&nbsp;background:#aaa;
  2474. $html .= '<a href="" onclick="dokeos_xajax_handler.switch_item(' .
  2475. $mycurrentitemid . ',' .
  2476. $item['id'] . ');' .
  2477. 'return false;" >' . stripslashes($title) . '</a>';
  2478. } elseif ($item['type'] == 'dokeos_module' || $item['type'] == 'dokeos_chapter') {
  2479. $html .= "<img align='absbottom' width='13' height='13' src='../img/lp_dokeos_module.png'>&nbsp;" . stripslashes($title);
  2480. } elseif ($item['type'] == 'dir') {
  2481. $html .= stripslashes($title);
  2482. }
  2483. $tbl_track_e_exercises = Database :: get_statistic_table(TABLE_STATISTIC_TRACK_E_EXERCICES);
  2484. $tbl_lp_item = Database :: get_course_table(TABLE_LP_ITEM);
  2485. $user_id = api_get_user_id();
  2486. $course_id = api_get_course_id();
  2487. $sql = "SELECT path FROM $tbl_track_e_exercises, $tbl_lp_item
  2488. WHERE path = '" . $item['path'] . "' AND exe_user_id = '$user_id' AND exe_cours_id = '$course_id' AND path = exe_exo_id AND status <> 'incomplete'";
  2489. $result = api_sql_query($sql, __FILE__, __LINE__);
  2490. $count = Database :: num_rows($result);
  2491. if ($item['type'] == 'quiz') {
  2492. if ($item['status'] == 'completed') {
  2493. $html .= "&nbsp;<img id='toc_img_" . $item['id'] . "' src='" . $icon_name[$item['status']] . "' alt='" . substr($item['status'], 0, 1) . "' width='12' height='12' />";
  2494. }
  2495. } else {
  2496. if ($item['type'] != 'dokeos_chapter' && $item['type'] != 'dokeos_module' && $item['type'] != 'dir') {
  2497. $html .= "&nbsp;<img id='toc_img_" . $item['id'] . "' src='" . $icon_name[$item['status']] . "' alt='" . substr($item['status'], 0, 1) . "' width='12' height='12' />";
  2498. }
  2499. }
  2500. $html .= "</div>";
  2501. if ($scorm_color_background != '') {
  2502. $html .= '</div>';
  2503. }
  2504. $color_counter++;
  2505. }
  2506. $html .= "</div>\n";
  2507. return $html;
  2508. }
  2509. /**
  2510. * Gets the learnpath maker name - generally the editor's name
  2511. * @return string Learnpath maker name
  2512. */
  2513. function get_maker() {
  2514. if ($this->debug > 0) {
  2515. error_log('New LP - In learnpath::get_maker()', 0);
  2516. }
  2517. if (!empty ($this->maker)) {
  2518. return $this->maker;
  2519. } else {
  2520. return '';
  2521. }
  2522. }
  2523. /**
  2524. * Gets the user-friendly message stored in $this->message
  2525. * @return string Message
  2526. */
  2527. function get_message() {
  2528. if ($this->debug > 0) {
  2529. error_log('New LP - In learnpath::get_message()', 0);
  2530. }
  2531. return $this->message;
  2532. }
  2533. /**
  2534. * Gets the learnpath name/title
  2535. * @return string Learnpath name/title
  2536. */
  2537. function get_name() {
  2538. if ($this->debug > 0) {
  2539. error_log('New LP - In learnpath::get_name()', 0);
  2540. }
  2541. if (!empty ($this->name)) {
  2542. return $this->name;
  2543. } else {
  2544. return 'N/A';
  2545. }
  2546. }
  2547. /**
  2548. * Gets a link to the resource from the present location, depending on item ID.
  2549. * @param string Type of link expected
  2550. * @param integer Learnpath item ID
  2551. * @return string Link to the lp_item resource
  2552. */
  2553. function get_link($type = 'http', $item_id = null) {
  2554. if ($this->debug > 0) {
  2555. error_log('New LP - In learnpath::get_link(' . $type . ',' . $item_id . ')', 0);
  2556. }
  2557. if (empty($item_id)) {
  2558. if ($this->debug > 2) {
  2559. error_log('New LP - In learnpath::get_link() - no item id given in learnpath::get_link(), using current: ' . $this->get_current_item_id(), 0);
  2560. }
  2561. $item_id = $this->get_current_item_id();
  2562. }
  2563. if (empty ($item_id)) {
  2564. if ($this->debug > 2) {
  2565. error_log('New LP - In learnpath::get_link() - no current item id found in learnpath object', 0);
  2566. }
  2567. //still empty, this means there was no item_id given and we are not in an object context or
  2568. //the object property is empty, return empty link
  2569. $item_id = $this->first();
  2570. return '';
  2571. }
  2572. $file = '';
  2573. $lp_table = Database :: get_course_table(TABLE_LP_MAIN);
  2574. $lp_item_table = Database :: get_course_table(TABLE_LP_ITEM);
  2575. $lp_item_view_table = Database :: get_course_table(TABLE_LP_ITEM_VIEW);
  2576. $item_id = Database::escape_string($item_id);
  2577. $sel = "SELECT l.lp_type as ltype, l.path as lpath, li.item_type as litype, li.path as lipath, li.parameters as liparams " .
  2578. "FROM $lp_table l, $lp_item_table li WHERE li.id = $item_id AND li.lp_id = l.id";
  2579. if ($this->debug > 2) {
  2580. error_log('New LP - In learnpath::get_link() - selecting item ' . $sel, 0);
  2581. }
  2582. $res = api_sql_query($sel, __FILE__, __LINE__);
  2583. if (Database :: num_rows($res) > 0) {
  2584. $row = Database :: fetch_array($res);
  2585. //var_dump($row);
  2586. $lp_type = $row['ltype'];
  2587. $lp_path = $row['lpath'];
  2588. $lp_item_type = $row['litype'];
  2589. $lp_item_path = $row['lipath'];
  2590. $lp_item_params = $row['liparams'];
  2591. if (empty ($lp_item_params) && strpos($lp_item_path, '?') !== false) {
  2592. list ($lp_item_path, $lp_item_params) = explode('?', $lp_item_path);
  2593. }
  2594. //$lp_item_params = '?'.$lp_item_params;
  2595. //add ? if none - left commented to give freedom to scorm implementation
  2596. //if(substr($lp_item_params,0,1)!='?'){
  2597. // $lp_item_params = '?'.$lp_item_params;
  2598. //}
  2599. $sys_course_path = api_get_path(SYS_COURSE_PATH) . api_get_course_path();
  2600. if ($type == 'http') {
  2601. $course_path = api_get_path(WEB_COURSE_PATH) . api_get_course_path(); //web path
  2602. } else {
  2603. $course_path = $sys_course_path; //system path
  2604. }
  2605. //now go through the specific cases to get the end of the path
  2606. switch ($lp_type) {
  2607. case 1 :
  2608. if ($lp_item_type == 'dokeos_chapter') {
  2609. $file = 'lp_content.php?type=dir';
  2610. } else {
  2611. require_once ('resourcelinker.inc.php');
  2612. $file = rl_get_resource_link_for_learnpath(api_get_course_id(), $this->get_id(), $item_id);
  2613. // check how much attempts of a exercise exits in lp
  2614. $lp_item_id = $this->get_current_item_id();
  2615. $lp_view_id = $this->get_view_id();
  2616. $prevent_reinit = $this->items[$this->current]->get_prevent_reinit();
  2617. $list = $this->get_toc();
  2618. $type_quiz = false;
  2619. foreach ($list as $toc) {
  2620. if ($toc['id'] == $lp_item_id && ($toc['type'] == 'quiz')) {
  2621. $type_quiz = true;
  2622. }
  2623. }
  2624. if ($type_quiz) {
  2625. $lp_item_id = Database :: escape_string($lp_item_id);
  2626. $lp_view_id = Database :: escape_string($lp_view_id);
  2627. $sql = "SELECT count(*) FROM $lp_item_view_table WHERE lp_item_id='" . (int) $lp_item_id . "' AND lp_view_id ='" . (int) $lp_view_id . "' AND status='completed'";
  2628. $result = api_sql_query($sql, __FILE__, __LINE__);
  2629. $row_count = Database :: fetch_row($result);
  2630. $count_item_view = (int) $row_count[0];
  2631. $not_multiple_attempt = 0;
  2632. if ($prevent_reinit === 1 && $count_item_view > 0) {
  2633. $not_multiple_attempt = 1;
  2634. }
  2635. $file .= '&not_multiple_attempt=' . $not_multiple_attempt;
  2636. }
  2637. $tmp_array = explode("/", $file);
  2638. $document_name = $tmp_array[count($tmp_array) - 1];
  2639. if (strpos($document_name, '_DELETED_')) {
  2640. $file = 'blank.php?error=document_deleted';
  2641. }
  2642. }
  2643. break;
  2644. case 2 :
  2645. if ($this->debug > 2) {
  2646. error_log('New LP - In learnpath::get_link() ' . __LINE__ . ' - Item type: ' . $lp_item_type, 0);
  2647. }
  2648. if ($lp_item_type != 'dir') {
  2649. //Quite complex here:
  2650. //we want to make sure 'http://' (and similar) links can
  2651. //be loaded as is (withouth the Dokeos path in front) but
  2652. //some contents use this form: resource.htm?resource=http://blablabla
  2653. //which means we have to find a protocol at the path's start, otherwise
  2654. //it should not be considered as an external URL
  2655. //if($this->prerequisites_match($item_id)){
  2656. if (preg_match('#^[a-zA-Z]{2,5}://#', $lp_item_path) != 0) {
  2657. if ($this->debug > 2) {
  2658. error_log('New LP - In learnpath::get_link() ' . __LINE__ . ' - Found match for protocol in ' . $lp_item_path, 0);
  2659. }
  2660. //distant url, return as is
  2661. $file = $lp_item_path;
  2662. } else {
  2663. if ($this->debug > 2) {
  2664. error_log('New LP - In learnpath::get_link() ' . __LINE__ . ' - No starting protocol in ' . $lp_item_path, 0);
  2665. }
  2666. //prevent getting untranslatable urls
  2667. $lp_item_path = preg_replace('/%2F/', '/', $lp_item_path);
  2668. $lp_item_path = preg_replace('/%3A/', ':', $lp_item_path);
  2669. //prepare the path
  2670. $file = $course_path . '/scorm/' . $lp_path . '/' . $lp_item_path;
  2671. //TODO fix this for urls with protocol header
  2672. $file = str_replace('//', '/', $file);
  2673. $file = str_replace(':/', '://', $file);
  2674. if (substr($lp_path, -1) == '/') {
  2675. $lp_path = substr($lp_path, 0, -1);
  2676. }
  2677. if (!is_file(realpath($sys_course_path . '/scorm/' . $lp_path . '/' . $lp_item_path))) {
  2678. //if file not found
  2679. $decoded = html_entity_decode($lp_item_path);
  2680. list ($decoded) = explode('?', $decoded);
  2681. if (!is_file(realpath($sys_course_path . '/scorm/' . $lp_path . '/' . $decoded))) {
  2682. require_once ('resourcelinker.inc.php');
  2683. $file = rl_get_resource_link_for_learnpath(api_get_course_id(), $this->get_id(), $item_id);
  2684. $tmp_array = explode("/", $file);
  2685. $document_name = $tmp_array[count($tmp_array) - 1];
  2686. if (strpos($document_name, '_DELETED_')) {
  2687. $file = 'blank.php?error=document_deleted';
  2688. }
  2689. } else {
  2690. $file = $course_path . '/scorm/' . $lp_path . '/' . $decoded;
  2691. }
  2692. }
  2693. }
  2694. //}else{
  2695. //prerequisites did not match
  2696. //$file = 'blank.php';
  2697. //}
  2698. //We want to use parameters if they were defined in the imsmanifest
  2699. if ($file != 'blank.php') {
  2700. $file .= (strstr($file, '?') === false ? '?' : '') . $lp_item_params;
  2701. }
  2702. } else {
  2703. $file = 'lp_content.php?type=dir';
  2704. }
  2705. break;
  2706. case 3 :
  2707. if ($this->debug > 2) {
  2708. error_log('New LP - In learnpath::get_link() ' . __LINE__ . ' - Item type: ' . $lp_item_type, 0);
  2709. }
  2710. //formatting AICC HACP append URL
  2711. $aicc_append = '?aicc_sid=' . urlencode(session_id()) . '&aicc_url=' . urlencode(api_get_path(WEB_CODE_PATH) . 'newscorm/aicc_hacp.php') . '&';
  2712. if ($lp_item_type != 'dir') {
  2713. //Quite complex here:
  2714. //we want to make sure 'http://' (and similar) links can
  2715. //be loaded as is (withouth the Dokeos path in front) but
  2716. //some contents use this form: resource.htm?resource=http://blablabla
  2717. //which means we have to find a protocol at the path's start, otherwise
  2718. //it should not be considered as an external URL
  2719. if (preg_match('#^[a-zA-Z]{2,5}://#', $lp_item_path) != 0) {
  2720. if ($this->debug > 2) {
  2721. error_log('New LP - In learnpath::get_link() ' . __LINE__ . ' - Found match for protocol in ' . $lp_item_path, 0);
  2722. }
  2723. //distant url, return as is
  2724. $file = $lp_item_path;
  2725. // Enabled and modified by Ivan Tcholakov, 16-OCT-2008.
  2726. /*
  2727. if(stristr($file,'<servername>')!==false){
  2728. $file = str_replace('<servername>',$course_path.'/scorm/'.$lp_path.'/',$lp_item_path);
  2729. }
  2730. */
  2731. if (stripos($file, '<servername>') !== false) {
  2732. //$file = str_replace('<servername>',$course_path.'/scorm/'.$lp_path.'/',$lp_item_path);
  2733. $web_course_path = str_replace('https://', '', str_replace('http://', '', $course_path));
  2734. $file = str_replace('<servername>', $web_course_path . '/scorm/' . $lp_path, $lp_item_path);
  2735. }
  2736. //
  2737. $file .= $aicc_append;
  2738. } else {
  2739. if ($this->debug > 2) {
  2740. error_log('New LP - In learnpath::get_link() ' . __LINE__ . ' - No starting protocol in ' . $lp_item_path, 0);
  2741. }
  2742. //prevent getting untranslatable urls
  2743. $lp_item_path = preg_replace('/%2F/', '/', $lp_item_path);
  2744. $lp_item_path = preg_replace('/%3A/', ':', $lp_item_path);
  2745. //prepare the path - lp_path might be unusable because it includes the "aicc" subdir name
  2746. $file = $course_path . '/scorm/' . $lp_path . '/' . $lp_item_path;
  2747. //TODO fix this for urls with protocol header
  2748. $file = str_replace('//', '/', $file);
  2749. $file = str_replace(':/', '://', $file);
  2750. $file .= $aicc_append;
  2751. }
  2752. } else {
  2753. $file = 'lp_content.php?type=dir';
  2754. }
  2755. break;
  2756. case 4 :
  2757. break;
  2758. default :
  2759. break;
  2760. }
  2761. }
  2762. if ($this->debug > 2) {
  2763. error_log('New LP - In learnpath::get_link() - returning "' . $file . '" from get_link', 0);
  2764. }
  2765. return $file;
  2766. }
  2767. /**
  2768. * Gets the latest usable view or generate a new one
  2769. * @param integer Optional attempt number. If none given, takes the highest from the lp_view table
  2770. * @return integer DB lp_view id
  2771. */
  2772. function get_view($attempt_num = 0) {
  2773. if ($this->debug > 0) {
  2774. error_log('New LP - In learnpath::get_view()', 0);
  2775. }
  2776. $search = '';
  2777. //use $attempt_num to enable multi-views management (disabled so far)
  2778. if ($attempt_num != 0 AND intval(strval($attempt_num)) == $attempt_num) {
  2779. $search = 'AND view_count = ' . $attempt_num;
  2780. }
  2781. //when missing $attempt_num, search for a unique lp_view record for this lp and user
  2782. $lp_view_table = Database :: get_course_table(TABLE_LP_VIEW);
  2783. $sql = "SELECT id, view_count FROM $lp_view_table " .
  2784. "WHERE lp_id = " . $this->get_id() . " " .
  2785. "AND user_id = " . $this->get_user_id() . " " .
  2786. $search .
  2787. " ORDER BY view_count DESC";
  2788. $res = api_sql_query($sql, __FILE__, __LINE__);
  2789. if (Database :: num_rows($res) > 0) {
  2790. $row = Database :: fetch_array($res);
  2791. $this->lp_view_id = $row['id'];
  2792. } else {
  2793. //no database record, create one
  2794. $sql = "INSERT INTO $lp_view_table(lp_id,user_id,view_count)" .
  2795. "VALUES (" . $this->get_id() . "," . $this->get_user_id() . ",1)";
  2796. $res = api_sql_query($sql, __FILE__, __LINE__);
  2797. $id = Database :: get_last_insert_id();
  2798. $this->lp_view_id = $id;
  2799. }
  2800. return $this->lp_view_id;
  2801. }
  2802. /**
  2803. * Gets the current view id
  2804. * @return integer View ID (from lp_view)
  2805. */
  2806. function get_view_id() {
  2807. if ($this->debug > 0) {
  2808. error_log('New LP - In learnpath::get_view_id()', 0);
  2809. }
  2810. if (!empty ($this->lp_view_id)) {
  2811. return $this->lp_view_id;
  2812. } else {
  2813. return 0;
  2814. }
  2815. }
  2816. /**
  2817. * Gets the update queue
  2818. * @return array Array containing IDs of items to be updated by JavaScript
  2819. */
  2820. function get_update_queue() {
  2821. if ($this->debug > 0) {
  2822. error_log('New LP - In learnpath::get_update_queue()', 0);
  2823. }
  2824. return $this->update_queue;
  2825. }
  2826. /**
  2827. * Gets the user ID
  2828. * @return integer User ID
  2829. */
  2830. function get_user_id() {
  2831. if ($this->debug > 0) {
  2832. error_log('New LP - In learnpath::get_user_id()', 0);
  2833. }
  2834. if (!empty ($this->user_id)) {
  2835. return $this->user_id;
  2836. } else {
  2837. return false;
  2838. }
  2839. }
  2840. /**
  2841. * Checks if any of the items has an audio element attached
  2842. * @return bool True or false
  2843. */
  2844. function has_audio() {
  2845. if ($this->debug > 1) {
  2846. error_log('New LP - In learnpath::has_audio()', 0);
  2847. }
  2848. $has = false;
  2849. foreach ($this->items as $i => $item) {
  2850. if (!empty ($this->items[$i]->audio)) {
  2851. $has = true;
  2852. break;
  2853. }
  2854. }
  2855. return $has;
  2856. }
  2857. /**
  2858. * Logs a message into a file
  2859. * @param string Message to log
  2860. * @return boolean True on success, false on error or if msg empty
  2861. */
  2862. function log($msg) {
  2863. if ($this->debug > 0) {
  2864. error_log('New LP - In learnpath::log()', 0);
  2865. }
  2866. //TODO
  2867. $this->error .= $msg . "\n";
  2868. return true;
  2869. }
  2870. /**
  2871. * Moves an item up and down at its level
  2872. * @param integer Item to move up and down
  2873. * @param string Direction 'up' or 'down'
  2874. * @return integer New display order, or false on error
  2875. */
  2876. function move_item($id, $direction) {
  2877. if ($this->debug > 0) {
  2878. error_log('New LP - In learnpath::move_item(' . $id . ',' . $direction . ')', 0);
  2879. }
  2880. if (empty ($id) or empty ($direction)) {
  2881. return false;
  2882. }
  2883. $tbl_lp_item = Database :: get_course_table(TABLE_LP_ITEM);
  2884. $sql_sel = "
  2885. SELECT *
  2886. FROM " . $tbl_lp_item . "
  2887. WHERE id = " . $id;
  2888. $res_sel = api_sql_query($sql_sel, __FILE__, __LINE__);
  2889. //check if elem exists
  2890. if (Database :: num_rows($res_sel) < 1) {
  2891. return false;
  2892. }
  2893. //gather data
  2894. $row = Database :: fetch_array($res_sel);
  2895. $previous = $row['previous_item_id'];
  2896. $next = $row['next_item_id'];
  2897. $display = $row['display_order'];
  2898. $parent = $row['parent_item_id'];
  2899. $lp = $row['lp_id'];
  2900. //update the item (switch with previous/next one)
  2901. switch ($direction) {
  2902. case 'up' :
  2903. if ($this->debug > 2) {
  2904. error_log('Movement up detected', 0);
  2905. }
  2906. if ($display <= 1) { /*do nothing*/
  2907. } else {
  2908. $sql_sel2 = "SELECT *
  2909. FROM $tbl_lp_item
  2910. WHERE id = $previous";
  2911. if ($this->debug > 2) {
  2912. error_log('Selecting previous: ' . $sql_sel2, 0);
  2913. }
  2914. $res_sel2 = api_sql_query($sql_sel2, __FILE__, __LINE__);
  2915. if (Database :: num_rows($res_sel2) < 1) {
  2916. $previous_previous = 0;
  2917. }
  2918. //gather data
  2919. $row2 = Database :: fetch_array($res_sel2);
  2920. $previous_previous = $row2['previous_item_id'];
  2921. //update previous_previous item (switch "next" with current)
  2922. if ($previous_previous != 0) {
  2923. $sql_upd2 = "UPDATE $tbl_lp_item SET next_item_id = $id WHERE id = $previous_previous";
  2924. if ($this->debug > 2) {
  2925. error_log($sql_upd2, 0);
  2926. }
  2927. $res_upd2 = api_sql_query($sql_upd2, __FILE__, __LINE__);
  2928. }
  2929. //update previous item (switch with current)
  2930. if ($previous != 0) {
  2931. $sql_upd2 = "UPDATE $tbl_lp_item SET next_item_id = $next, previous_item_id = $id, display_order = display_order +1 WHERE id = $previous";
  2932. if ($this->debug > 2) {
  2933. error_log($sql_upd2, 0);
  2934. }
  2935. $res_upd2 = api_sql_query($sql_upd2, __FILE__, __LINE__);
  2936. }
  2937. //update current item (switch with previous)
  2938. if ($id != 0) {
  2939. $sql_upd2 = "UPDATE $tbl_lp_item SET next_item_id = $previous, previous_item_id = $previous_previous, display_order = display_order-1 WHERE id = $id";
  2940. if ($this->debug > 2) {
  2941. error_log($sql_upd2, 0);
  2942. }
  2943. $res_upd2 = api_sql_query($sql_upd2, __FILE__, __LINE__);
  2944. }
  2945. //update next item (new previous item)
  2946. if ($next != 0) {
  2947. $sql_upd2 = "UPDATE $tbl_lp_item SET previous_item_id = $previous WHERE id = $next";
  2948. if ($this->debug > 2) {
  2949. error_log($sql_upd2, 0);
  2950. }
  2951. $res_upd2 = api_sql_query($sql_upd2, __FILE__, __LINE__);
  2952. }
  2953. $display = $display -1;
  2954. }
  2955. break;
  2956. case 'down' :
  2957. if ($this->debug > 2) {
  2958. error_log('Movement down detected', 0);
  2959. }
  2960. if ($next == 0) { /*do nothing*/
  2961. } else {
  2962. $sql_sel2 = "SELECT * FROM $tbl_lp_item WHERE id = $next";
  2963. if ($this->debug > 2) {
  2964. error_log('Selecting next: ' . $sql_sel2, 0);
  2965. }
  2966. $res_sel2 = api_sql_query($sql_sel2, __FILE__, __LINE__);
  2967. if (Database :: num_rows($res_sel2) < 1) {
  2968. $next_next = 0;
  2969. }
  2970. //gather data
  2971. $row2 = Database :: fetch_array($res_sel2);
  2972. $next_next = $row2['next_item_id'];
  2973. //update previous item (switch with current)
  2974. if ($previous != 0) {
  2975. $sql_upd2 = "UPDATE $tbl_lp_item SET next_item_id = $next WHERE id = $previous";
  2976. $res_upd2 = api_sql_query($sql_upd2, __FILE__, __LINE__);
  2977. }
  2978. //update current item (switch with previous)
  2979. if ($id != 0) {
  2980. $sql_upd2 = "UPDATE $tbl_lp_item SET previous_item_id = $next, next_item_id = $next_next, display_order = display_order+1 WHERE id = $id";
  2981. $res_upd2 = api_sql_query($sql_upd2, __FILE__, __LINE__);
  2982. }
  2983. //update next item (new previous item)
  2984. if ($next != 0) {
  2985. $sql_upd2 = "UPDATE $tbl_lp_item SET previous_item_id = $previous, next_item_id = $id, display_order = display_order-1 WHERE id = $next";
  2986. $res_upd2 = api_sql_query($sql_upd2, __FILE__, __LINE__);
  2987. }
  2988. //update next_next item (switch "previous" with current)
  2989. if ($next_next != 0) {
  2990. $sql_upd2 = "UPDATE $tbl_lp_item SET previous_item_id = $id WHERE id = $next_next";
  2991. $res_upd2 = api_sql_query($sql_upd2, __FILE__, __LINE__);
  2992. }
  2993. $display = $display +1;
  2994. }
  2995. break;
  2996. default :
  2997. return false;
  2998. }
  2999. return $display;
  3000. }
  3001. /**
  3002. * Move a learnpath up (display_order)
  3003. * @param integer Learnpath ID
  3004. */
  3005. function move_up($lp_id) {
  3006. $lp_table = Database :: get_course_table(TABLE_LP_MAIN);
  3007. $sql = "SELECT * FROM $lp_table ORDER BY display_order";
  3008. $res = api_sql_query($sql, __FILE__, __LINE__);
  3009. if ($res === false)
  3010. return false;
  3011. $lps = array ();
  3012. $lp_order = array ();
  3013. $num = Database :: num_rows($res);
  3014. //first check the order is correct, globally (might be wrong because
  3015. //of versions < 1.8.4)
  3016. if ($num > 0) {
  3017. $i = 1;
  3018. while ($row = Database :: fetch_array($res)) {
  3019. if ($row['display_order'] != $i) { //if we find a gap in the order, we need to fix it
  3020. $need_fix = true;
  3021. $sql_u = "UPDATE $lp_table SET display_order = $i WHERE id = " . $row['id'];
  3022. $res_u = api_sql_query($sql_u, __FILE__, __LINE__);
  3023. }
  3024. $row['display_order'] = $i;
  3025. $lps[$row['id']] = $row;
  3026. $lp_order[$i] = $row['id'];
  3027. $i++;
  3028. }
  3029. }
  3030. if ($num > 1) //if there's only one element, no need to sort
  3031. {
  3032. $order = $lps[$lp_id]['display_order'];
  3033. if ($order > 1) //if it's the first element, no need to move up
  3034. {
  3035. $sql_u1 = "UPDATE $lp_table SET display_order = $order WHERE id = " . $lp_order[$order -1];
  3036. $res_u1 = api_sql_query($sql_u1, __FILE__, __LINE__);
  3037. $sql_u2 = "UPDATE $lp_table SET display_order = " . ($order -1) . " WHERE id = " . $lp_id;
  3038. $res_u2 = api_sql_query($sql_u2, __FILE__, __LINE__);
  3039. }
  3040. }
  3041. }
  3042. /**
  3043. * Move a learnpath down (display_order)
  3044. * @param integer Learnpath ID
  3045. */
  3046. function move_down($lp_id) {
  3047. $lp_table = Database :: get_course_table(TABLE_LP_MAIN);
  3048. $sql = "SELECT * FROM $lp_table ORDER BY display_order";
  3049. $res = api_sql_query($sql, __FILE__, __LINE__);
  3050. if ($res === false)
  3051. return false;
  3052. $lps = array ();
  3053. $lp_order = array ();
  3054. $num = Database :: num_rows($res);
  3055. $max = 0;
  3056. //first check the order is correct, globally (might be wrong because
  3057. //of versions < 1.8.4)
  3058. if ($num > 0) {
  3059. $i = 1;
  3060. while ($row = Database :: fetch_array($res)) {
  3061. $max = $i;
  3062. if ($row['display_order'] != $i) { //if we find a gap in the order, we need to fix it
  3063. $need_fix = true;
  3064. $sql_u = "UPDATE $lp_table SET display_order = $i WHERE id = " . $row['id'];
  3065. $res_u = api_sql_query($sql_u, __FILE__, __LINE__);
  3066. }
  3067. $row['display_order'] = $i;
  3068. $lps[$row['id']] = $row;
  3069. $lp_order[$i] = $row['id'];
  3070. $i++;
  3071. }
  3072. }
  3073. if ($num > 1) //if there's only one element, no need to sort
  3074. {
  3075. $order = $lps[$lp_id]['display_order'];
  3076. if ($order < $max) //if it's the first element, no need to move up
  3077. {
  3078. $sql_u1 = "UPDATE $lp_table SET display_order = $order WHERE id = " . $lp_order[$order +1];
  3079. $res_u1 = api_sql_query($sql_u1, __FILE__, __LINE__);
  3080. $sql_u2 = "UPDATE $lp_table SET display_order = " . ($order +1) . " WHERE id = " . $lp_id;
  3081. $res_u2 = api_sql_query($sql_u2, __FILE__, __LINE__);
  3082. }
  3083. }
  3084. }
  3085. /**
  3086. * Updates learnpath attributes to point to the next element
  3087. * The last part is similar to set_current_item but processing the other way around
  3088. */
  3089. function next() {
  3090. if ($this->debug > 0) {
  3091. error_log('New LP - In learnpath::next()', 0);
  3092. }
  3093. $this->last = $this->get_current_item_id();
  3094. $this->items[$this->last]->save(false, $this->prerequisites_match($this->last));
  3095. $this->autocomplete_parents($this->last);
  3096. $new_index = $this->get_next_index();
  3097. if ($this->debug > 2) {
  3098. error_log('New LP - New index: ' . $new_index, 0);
  3099. }
  3100. $this->index = $new_index;
  3101. if ($this->debug > 2) {
  3102. error_log('New LP - Now having orderedlist[' . $new_index . '] = ' . $this->ordered_items[$new_index], 0);
  3103. }
  3104. $this->current = $this->ordered_items[$new_index];
  3105. if ($this->debug > 2) {
  3106. error_log('New LP - new item id is ' . $this->current . '-' . $this->get_current_item_id(), 0);
  3107. }
  3108. }
  3109. /**
  3110. * Open a resource = initialise all local variables relative to this resource. Depending on the child
  3111. * class, this might be redefined to allow several behaviours depending on the document type.
  3112. * @param integer Resource ID
  3113. * @return boolean True on success, false otherwise
  3114. */
  3115. function open($id) {
  3116. if ($this->debug > 0) {
  3117. error_log('New LP - In learnpath::open()', 0);
  3118. }
  3119. //TODO
  3120. //set the current resource attribute to this resource
  3121. //switch on element type (redefine in child class?)
  3122. //set status for this item to "opened"
  3123. //start timer
  3124. //initialise score
  3125. $this->index = 0; //or = the last item seen (see $this->last)
  3126. }
  3127. /**
  3128. * Check that all prerequisites are fulfilled. Returns true and an empty string on succes, returns false
  3129. * and the prerequisite string on error.
  3130. * This function is based on the rules for aicc_script language as described in the SCORM 1.2 CAM documentation page 108.
  3131. * @param integer Optional item ID. If none given, uses the current open item.
  3132. * @return boolean True if prerequisites are matched, false otherwise
  3133. * @return string Empty string if true returned, prerequisites string otherwise.
  3134. */
  3135. function prerequisites_match($item = null) {
  3136. if ($this->debug > 0) {
  3137. error_log('New LP - In learnpath::prerequisites_match()', 0);
  3138. }
  3139. if (empty ($item)) {
  3140. $item = $this->current;
  3141. }
  3142. if (is_object($this->items[$item])) {
  3143. $prereq_string = $this->items[$item]->get_prereq_string();
  3144. if (empty ($prereq_string)) {
  3145. return true;
  3146. }
  3147. //clean spaces
  3148. $prereq_string = str_replace(' ', '', $prereq_string);
  3149. if ($this->debug > 0) {
  3150. error_log('Found prereq_string: ' . $prereq_string, 0);
  3151. }
  3152. //now send to the parse_prereq() function that will check this component's prerequisites
  3153. $result = $this->items[$item]->parse_prereq($prereq_string, $this->items, $this->refs_list, $this->get_user_id());
  3154. if ($result === false) {
  3155. $this->set_error_msg($this->items[$item]->prereq_alert);
  3156. }
  3157. } else {
  3158. $result = true;
  3159. if ($this->debug > 1) {
  3160. error_log('New LP - $this->items[' . $item . '] was not an object', 0);
  3161. }
  3162. }
  3163. if ($this->debug > 1) {
  3164. error_log('New LP - End of prerequisites_match(). Error message is now ' . $this->error, 0);
  3165. }
  3166. return $result;
  3167. }
  3168. /**
  3169. * Updates learnpath attributes to point to the previous element
  3170. * The last part is similar to set_current_item but processing the other way around
  3171. */
  3172. function previous() {
  3173. if ($this->debug > 0) {
  3174. error_log('New LP - In learnpath::previous()', 0);
  3175. }
  3176. $this->last = $this->get_current_item_id();
  3177. $this->items[$this->last]->save(false, $this->prerequisites_match($this->last));
  3178. $this->autocomplete_parents($this->last);
  3179. $new_index = $this->get_previous_index();
  3180. $this->index = $new_index;
  3181. $this->current = $this->ordered_items[$new_index];
  3182. }
  3183. /**
  3184. * Publishes a learnpath. This basically means show or hide the learnpath
  3185. * to normal users.
  3186. * Can be used as abstract
  3187. * @param integer Learnpath ID
  3188. * @param string New visibility
  3189. */
  3190. function toggle_visibility($lp_id, $set_visibility = 1) {
  3191. //if($this->debug>0){error_log('New LP - In learnpath::toggle_visibility()',0);}
  3192. $action = 'visible';
  3193. if ($set_visibility != 1) {
  3194. $action = 'invisible';
  3195. }
  3196. return api_item_property_update(api_get_course_info(), TOOL_LEARNPATH, $lp_id, $action, api_get_user_id());
  3197. }
  3198. /**
  3199. * Publishes a learnpath. This basically means show or hide the learnpath
  3200. * on the course homepage
  3201. * Can be used as abstract
  3202. * @param integer Learnpath ID
  3203. * @param string New visibility
  3204. */
  3205. function toggle_publish($lp_id, $set_visibility = 'v') {
  3206. //if($this->debug>0){error_log('New LP - In learnpath::toggle_publish()',0);}
  3207. $tbl_lp = Database :: get_course_table(TABLE_LP_MAIN);
  3208. $sql = "SELECT * FROM $tbl_lp where id=$lp_id";
  3209. $result = api_sql_query($sql, __FILE__, __LINE__);
  3210. $row = Database :: fetch_array($result);
  3211. $name = domesticate($row['name']);
  3212. if ($set_visibility == 'i') {
  3213. $s = $name . " " . get_lang('_no_published');
  3214. $dialogBox = $s;
  3215. $v = 0;
  3216. }
  3217. if ($set_visibility == 'v') {
  3218. $s = $name . " " . get_lang('_published');
  3219. $dialogBox = $s;
  3220. $v = 1;
  3221. }
  3222. $tbl_tool = Database :: get_course_table(TABLE_TOOL_LIST);
  3223. $link = 'newscorm/lp_controller.php?action=view&lp_id=' . $lp_id;
  3224. $sql = "SELECT * FROM $tbl_tool where name='$name' and image='scormbuilder.gif' and link LIKE '$link%'";
  3225. $result = api_sql_query($sql, __FILE__, __LINE__);
  3226. $num = Database :: num_rows($result);
  3227. $row2 = Database :: fetch_array($result);
  3228. //if($this->debug>2){error_log('New LP - '.$sql.' - '.$num,0);}
  3229. if (($set_visibility == 'i') && ($num > 0)) {
  3230. $sql = "DELETE FROM $tbl_tool WHERE (name='$name' and image='scormbuilder.gif' and link LIKE '$link%')";
  3231. }
  3232. elseif (($set_visibility == 'v') && ($num == 0)) {
  3233. $sql = "INSERT INTO $tbl_tool (name, link, image, visibility, admin, address, added_tool) VALUES ('$name','newscorm/lp_controller.php?action=view&lp_id=$lp_id','scormbuilder.gif','$v','0','pastillegris.gif',0)";
  3234. } else {
  3235. //parameter and database incompatible, do nothing
  3236. }
  3237. $result = api_sql_query($sql, __FILE__, __LINE__);
  3238. //if($this->debug>2){error_log('New LP - Leaving learnpath::toggle_visibility: '.$sql,0);}
  3239. }
  3240. /**
  3241. * Restart the whole learnpath. Return the URL of the first element.
  3242. * Make sure the results are saved with anoter method. This method should probably be
  3243. * redefined in children classes.
  3244. * To use a similar method statically, use the create_new_attempt() method
  3245. * @return string URL to load in the viewer
  3246. */
  3247. function restart() {
  3248. if ($this->debug > 0) {
  3249. error_log('New LP - In learnpath::restart()', 0);
  3250. }
  3251. //TODO
  3252. //call autosave method to save the current progress
  3253. //$this->index = 0;
  3254. $lp_view_table = Database :: get_course_table(TABLE_LP_VIEW);
  3255. $sql = "INSERT INTO $lp_view_table (lp_id, user_id, view_count) " .
  3256. "VALUES (" . $this->lp_id . "," . $this->get_user_id() . "," . ($this->attempt + 1) . ")";
  3257. if ($this->debug > 2) {
  3258. error_log('New LP - Inserting new lp_view for restart: ' . $sql, 0);
  3259. }
  3260. $res = api_sql_query($sql, __FILE__, __LINE__);
  3261. if ($view_id = Database :: get_last_insert_id($res)) {
  3262. $this->lp_view_id = $view_id;
  3263. $this->attempt = $this->attempt + 1;
  3264. } else {
  3265. $this->error = 'Could not insert into item_view table...';
  3266. return false;
  3267. }
  3268. $this->autocomplete_parents($this->current);
  3269. foreach ($this->items as $index => $dummy) {
  3270. $this->items[$index]->restart();
  3271. $this->items[$index]->set_lp_view($this->lp_view_id);
  3272. }
  3273. $this->first();
  3274. return true;
  3275. }
  3276. /**
  3277. * Saves the current item
  3278. * @return boolean
  3279. */
  3280. function save_current() {
  3281. if ($this->debug > 0) {
  3282. error_log('New LP - In learnpath::save_current()', 0);
  3283. }
  3284. //TODO do a better check on the index pointing to the right item (it is supposed to be working
  3285. // on $ordered_items[] but not sure it's always safe to use with $items[])
  3286. if ($this->debug > 2) {
  3287. error_log('New LP - save_current() saving item ' . $this->current, 0);
  3288. }
  3289. if ($this->debug > 2) {
  3290. error_log('' . print_r($this->items, true), 0);
  3291. }
  3292. if (is_object($this->items[$this->current])) {
  3293. //$res = $this->items[$this->current]->save(false);
  3294. $res = $this->items[$this->current]->save(false, $this->prerequisites_match($this->current));
  3295. $this->autocomplete_parents($this->current);
  3296. $status = $this->items[$this->current]->get_status();
  3297. $this->append_message('new_item_status: ' . $status);
  3298. $this->update_queue[$this->current] = $status;
  3299. return $res;
  3300. }
  3301. return false;
  3302. }
  3303. /**
  3304. * Saves the given item
  3305. * @param integer Item ID. Optional (will take from $_REQUEST if null)
  3306. * @param boolean Save from url params (true) or from current attributes (false). Optional. Defaults to true
  3307. * @return boolean
  3308. */
  3309. function save_item($item_id = null, $from_outside = true) {
  3310. if ($this->debug > 0) {
  3311. error_log('New LP - In learnpath::save_item(' . $item_id . ',' . $from_outside . ')', 0);
  3312. }
  3313. //TODO do a better check on the index pointing to the right item (it is supposed to be working
  3314. // on $ordered_items[] but not sure it's always safe to use with $items[])
  3315. if (empty ($item_id)) {
  3316. $item_id = $this->escape_string($_REQUEST['id']);
  3317. }
  3318. if (empty ($item_id)) {
  3319. $item_id = $this->get_current_item_id();
  3320. }
  3321. if ($this->debug > 2) {
  3322. error_log('New LP - save_current() saving item ' . $item_id, 0);
  3323. }
  3324. if (is_object($this->items[$item_id])) {
  3325. $res = $this->items[$item_id]->save($from_outside, $this->prerequisites_match($item_id));
  3326. //$res = $this->items[$item_id]->save($from_outside);
  3327. $this->autocomplete_parents($item_id);
  3328. $status = $this->items[$item_id]->get_status();
  3329. $this->append_message('new_item_status: ' . $status);
  3330. $this->update_queue[$item_id] = $status;
  3331. return $res;
  3332. }
  3333. return false;
  3334. }
  3335. /**
  3336. * Saves the last item seen's ID only in case
  3337. */
  3338. function save_last()
  3339. {
  3340. if ($this->debug > 0) {
  3341. error_log('New LP - In learnpath::save_last()', 0);
  3342. }
  3343. $table = Database :: get_course_table(TABLE_LP_VIEW);
  3344. if (isset ($this->current)) {
  3345. if ($this->debug > 2) {
  3346. error_log('New LP - Saving current item (' . $this->current . ') for later review', 0);
  3347. }
  3348. $sql = "UPDATE $table SET last_item = " . Database::escape_string($this->get_current_item_id()). " " .
  3349. "WHERE lp_id = " . $this->get_id() . " AND user_id = " . $this->get_user_id();
  3350. if ($this->debug > 2) {
  3351. error_log('New LP - Saving last item seen : ' . $sql, 0);
  3352. }
  3353. $res = api_sql_query($sql, __FILE__, __LINE__);
  3354. }
  3355. //save progress
  3356. list ($progress, $text) = $this->get_progress_bar_text('%');
  3357. if ($progress >= 0 AND $progress <= 100) {
  3358. $progress = (int) $progress;
  3359. $sql = "UPDATE $table SET progress = $progress " .
  3360. "WHERE lp_id = " . $this->get_id() . " AND " .
  3361. "user_id = " . $this->get_user_id();
  3362. $res = api_sql_query($sql, __FILE__, __LINE__); //ignore errors as some tables might not have the progress field just yet
  3363. $this->progress_db = $progress;
  3364. }
  3365. }
  3366. /**
  3367. * Sets the current item ID (checks if valid and authorized first)
  3368. * @param integer New item ID. If not given or not authorized, defaults to current
  3369. */
  3370. function set_current_item($item_id = null) {
  3371. if ($this->debug > 0) {
  3372. error_log('New LP - In learnpath::set_current_item(' . $item_id . ')', 0);
  3373. }
  3374. if (empty ($item_id)) {
  3375. if ($this->debug > 2) {
  3376. error_log('New LP - No new current item given, ignore...', 0);
  3377. }
  3378. //do nothing
  3379. } else {
  3380. if ($this->debug > 2) {
  3381. error_log('New LP - New current item given is ' . $item_id . '...', 0);
  3382. }
  3383. if (is_numeric($item_id)) {
  3384. $item_id = $this->escape_string($item_id);
  3385. //TODO check in database here
  3386. $this->last = $this->current;
  3387. $this->current = $item_id;
  3388. //TODO update $this->index as well
  3389. foreach ($this->ordered_items as $index => $item) {
  3390. if ($item == $this->current) {
  3391. $this->index = $index;
  3392. break;
  3393. }
  3394. }
  3395. if ($this->debug > 2) {
  3396. error_log('New LP - set_current_item(' . $item_id . ') done. Index is now : ' . $this->index, 0);
  3397. }
  3398. } else {
  3399. error_log('New LP - set_current_item(' . $item_id . ') failed. Not a numeric value: ', 0);
  3400. }
  3401. }
  3402. }
  3403. /**
  3404. * Sets the encoding
  3405. * @param string New encoding
  3406. */
  3407. function set_encoding($enc = 'ISO-8859-15') {
  3408. if ($this->debug > 0) {
  3409. error_log('New LP - In learnpath::set_encoding()', 0);
  3410. }
  3411. $enc = strtoupper($enc);
  3412. $encodings = array (
  3413. 'UTF-8',
  3414. 'ISO-8859-1',
  3415. 'ISO-8859-15',
  3416. 'cp1251',
  3417. 'cp1252',
  3418. 'KOI8-R',
  3419. 'BIG5',
  3420. 'GB2312',
  3421. 'Shift_JIS',
  3422. 'EUC-JP',
  3423. ''
  3424. );
  3425. if (in_array($enc, $encodings)) {
  3426. $lp = $this->get_id();
  3427. if ($lp != 0) {
  3428. $tbl_lp = Database :: get_course_table(TABLE_LP_MAIN);
  3429. $sql = "UPDATE $tbl_lp SET default_encoding = '$enc' WHERE id = " . $lp;
  3430. $res = api_sql_query($sql, __FILE__, __LINE__);
  3431. return $res;
  3432. }
  3433. }
  3434. return false;
  3435. }
  3436. /**
  3437. * Sets the JS lib setting in the database directly.
  3438. * This is the JavaScript library file this lp needs to load on startup
  3439. * @param string Proximity setting
  3440. */
  3441. function set_jslib($lib = '') {
  3442. if ($this->debug > 0) {
  3443. error_log('New LP - In learnpath::set_jslib()', 0);
  3444. }
  3445. $lp = $this->get_id();
  3446. if ($lp != 0) {
  3447. $tbl_lp = Database :: get_course_table(TABLE_LP_MAIN);
  3448. $sql = "UPDATE $tbl_lp SET js_lib = '$lib' WHERE id = " . $lp;
  3449. $res = api_sql_query($sql, __FILE__, __LINE__);
  3450. return $res;
  3451. } else {
  3452. return false;
  3453. }
  3454. }
  3455. /**
  3456. * Sets the name of the LP maker (publisher) (and save)
  3457. * @param string Optional string giving the new content_maker of this learnpath
  3458. */
  3459. function set_maker($name = '') {
  3460. if ($this->debug > 0) {
  3461. error_log('New LP - In learnpath::set_maker()', 0);
  3462. }
  3463. if (empty ($name))
  3464. return false;
  3465. $this->maker = $this->escape_string($name);
  3466. $lp_table = Database :: get_course_table(TABLE_LP_MAIN);
  3467. $lp_id = $this->get_id();
  3468. $sql = "UPDATE $lp_table SET content_maker = '" . $this->maker . "' WHERE id = '$lp_id'";
  3469. if ($this->debug > 2) {
  3470. error_log('New LP - lp updated with new content_maker : ' . $this->maker, 0);
  3471. }
  3472. //$res = Database::query($sql);
  3473. $res = api_sql_query($sql, __FILE__, __LINE__);
  3474. return true;
  3475. }
  3476. /**
  3477. * Sets the name of the current learnpath (and save)
  3478. * @param string Optional string giving the new name of this learnpath
  3479. */
  3480. function set_name($name = '') {
  3481. if ($this->debug > 0) {
  3482. error_log('New LP - In learnpath::set_name()', 0);
  3483. }
  3484. if (empty ($name))
  3485. return false;
  3486. $this->name = $this->escape_string($name);
  3487. $lp_table = Database :: get_course_table(TABLE_LP_MAIN);
  3488. $lp_id = $this->get_id();
  3489. $sql = "UPDATE $lp_table SET name = '" . $this->name . "' WHERE id = '$lp_id'";
  3490. if ($this->debug > 2) {
  3491. error_log('New LP - lp updated with new name : ' . $this->name, 0);
  3492. }
  3493. //$res = Database::query($sql);
  3494. $res = api_sql_query($sql, __FILE__, __LINE__);
  3495. // if the lp is visible on the homepage, change his name there
  3496. if (mysql_affected_rows()) {
  3497. $table = Database :: get_course_table(TABLE_TOOL_LIST);
  3498. $sql = 'UPDATE ' . $table . ' SET
  3499. name = "' . $this->name . '"
  3500. WHERE link = "newscorm/lp_controller.php?action=view&lp_id=' . $lp_id . '"';
  3501. api_sql_query($sql, __FILE__, __LINE__);
  3502. }
  3503. return true;
  3504. }
  3505. /**
  3506. * Set index specified prefix terms for all items in this path
  3507. * @param string Comma-separated list of terms
  3508. * @param char Xapian term prefix
  3509. * @return boolean False on error, true otherwise
  3510. */
  3511. function set_terms_by_prefix($terms_string, $prefix) {
  3512. if (api_get_setting('search_enabled') !== 'true')
  3513. return FALSE;
  3514. $terms_string = trim($terms_string);
  3515. $terms = explode(',', $terms_string);
  3516. array_walk($terms, 'trim_value');
  3517. $stored_terms = $this->get_common_index_terms_by_prefix($prefix);
  3518. //var_dump($stored_terms);
  3519. //var_dump($terms);
  3520. // don't do anything if no change, verify only at DB, not the search engine
  3521. if ((count(array_diff($terms, $stored_terms)) == 0) && (count(array_diff($stored_terms, $terms)) == 0))
  3522. return FALSE;
  3523. require_once ('xapian.php'); //TODO try catch every xapian use or make wrappers on api
  3524. require_once (api_get_path(LIBRARY_PATH) . 'search/DokeosIndexer.class.php');
  3525. require_once (api_get_path(LIBRARY_PATH) . 'search/xapian/XapianQuery.php');
  3526. require_once (api_get_path(LIBRARY_PATH) . 'search/IndexableChunk.class.php');
  3527. $items_table = Database :: get_course_table(TABLE_LP_ITEM);
  3528. //TODO: make query secure agains XSS : use member attr instead of post var
  3529. $lp_id = intval($_POST['lp_id']);
  3530. $sql = "SELECT * FROM $items_table WHERE lp_id = $lp_id";
  3531. $result = api_sql_query($sql);
  3532. $di = new DokeosIndexer();
  3533. while ($lp_item = Database :: fetch_array($result)) {
  3534. // get search_did
  3535. $tbl_se_ref = Database :: get_main_table(TABLE_MAIN_SEARCH_ENGINE_REF);
  3536. $sql = 'SELECT * FROM %s WHERE course_code=\'%s\' AND tool_id=\'%s\' AND ref_id_high_level=%s AND ref_id_second_level=%d LIMIT 1';
  3537. $sql = sprintf($sql, $tbl_se_ref, $this->cc, TOOL_LEARNPATH, $lp_id, $lp_item['id']);
  3538. $res = api_sql_query($sql, __FILE__, __LINE__);
  3539. if (Database::num_rows($res)>0) {
  3540. $se_ref = Database :: fetch_array($res);
  3541. // compare terms
  3542. $doc = $di->get_document($se_ref['search_did']);
  3543. $xapian_terms = xapian_get_doc_terms($doc, $prefix);
  3544. //var_dump($xapian_terms);
  3545. $xterms = array ();
  3546. foreach ($xapian_terms as $xapian_term)
  3547. $xterms[] = substr($xapian_term['name'], 1);
  3548. $dterms = $terms;
  3549. //var_dump($xterms);
  3550. //var_dump($dterms);
  3551. $missing_terms = array_diff($dterms, $xterms);
  3552. $deprecated_terms = array_diff($xterms, $dterms);
  3553. // save it to search engine
  3554. foreach ($missing_terms as $term) {
  3555. $doc->add_term($prefix . $term, 1);
  3556. }
  3557. foreach ($deprecated_terms as $term) {
  3558. $doc->remove_term($prefix . $term);
  3559. }
  3560. $di->getDb()->replace_document((int) $se_ref['search_did'], $doc);
  3561. $di->getDb()->flush();
  3562. }
  3563. }
  3564. return true;
  3565. }
  3566. /**
  3567. * Sets the theme of the LP (local/remote) (and save)
  3568. * @param string Optional string giving the new theme of this learnpath
  3569. * @return bool returns true if theme name is not empty
  3570. */
  3571. function set_theme($name = '') {
  3572. if ($this->debug > 0) {
  3573. error_log('New LP - In learnpath::set_theme()', 0);
  3574. }
  3575. $this->theme = $this->escape_string($name);
  3576. $lp_table = Database :: get_course_table(TABLE_LP_MAIN);
  3577. $lp_id = $this->get_id();
  3578. $sql = "UPDATE $lp_table SET theme = '" . $this->theme . "' WHERE id = '$lp_id'";
  3579. if ($this->debug > 2) {
  3580. error_log('New LP - lp updated with new theme : ' . $this->theme, 0);
  3581. }
  3582. //$res = Database::query($sql);
  3583. $res = api_sql_query($sql, __FILE__, __LINE__);
  3584. return true;
  3585. }
  3586. /**
  3587. * Sets the image of an LP (and save)
  3588. * @param string Optional string giving the new image of this learnpath
  3589. * @return bool returns true if theme name is not empty
  3590. */
  3591. function set_preview_image($name = '') {
  3592. if ($this->debug > 0) {
  3593. error_log('New LP - In learnpath::set_preview_image()', 0);
  3594. }
  3595. $this->preview_image = $this->escape_string($name);
  3596. $lp_table = Database :: get_course_table(TABLE_LP_MAIN);
  3597. $lp_id = $this->get_id();
  3598. $sql = "UPDATE $lp_table SET preview_image = '" . $this->preview_image . "' WHERE id = '$lp_id'";
  3599. if ($this->debug > 2) {
  3600. error_log('New LP - lp updated with new preview image : ' . $this->preview_image, 0);
  3601. }
  3602. $res = api_sql_query($sql, __FILE__, __LINE__);
  3603. return true;
  3604. }
  3605. /**
  3606. * Sets the author of a LP (and save)
  3607. * @param string Optional string giving the new author of this learnpath
  3608. * @return bool returns true if author's name is not empty
  3609. */
  3610. function set_author($name = '') {
  3611. if ($this->debug > 0) {
  3612. error_log('New LP - In learnpath::set_author()', 0);
  3613. }
  3614. $this->author = $this->escape_string($name);
  3615. $lp_table = Database :: get_course_table(TABLE_LP_MAIN);
  3616. $lp_id = $this->get_id();
  3617. $sql = "UPDATE $lp_table SET author = '" . $this->author . "' WHERE id = '$lp_id'";
  3618. if ($this->debug > 2) {
  3619. error_log('New LP - lp updated with new preview author : ' . $this->author, 0);
  3620. }
  3621. $res = api_sql_query($sql, __FILE__, __LINE__);
  3622. return true;
  3623. }
  3624. /**
  3625. * Sets the location/proximity of the LP (local/remote) (and save)
  3626. * @param string Optional string giving the new location of this learnpath
  3627. */
  3628. function set_proximity($name = '') {
  3629. if ($this->debug > 0) {
  3630. error_log('New LP - In learnpath::set_proximity()', 0);
  3631. }
  3632. if (empty ($name))
  3633. return false;
  3634. $this->proximity = $this->escape_string($name);
  3635. $lp_table = Database :: get_course_table(TABLE_LP_MAIN);
  3636. $lp_id = $this->get_id();
  3637. $sql = "UPDATE $lp_table SET content_local = '" . $this->proximity . "' WHERE id = '$lp_id'";
  3638. if ($this->debug > 2) {
  3639. error_log('New LP - lp updated with new proximity : ' . $this->proximity, 0);
  3640. }
  3641. //$res = Database::query($sql);
  3642. $res = api_sql_query($sql, __FILE__, __LINE__);
  3643. return true;
  3644. }
  3645. /**
  3646. * Sets the previous item ID to a given ID. Generally, this should be set to the previous 'current' item
  3647. * @param integer DB ID of the item
  3648. */
  3649. function set_previous_item($id) {
  3650. if ($this->debug > 0) {
  3651. error_log('New LP - In learnpath::set_previous_item()', 0);
  3652. }
  3653. $this->last = $id;
  3654. }
  3655. /**
  3656. * Sets the object's error message
  3657. * @param string Error message. If empty, reinits the error string
  3658. * @return void
  3659. */
  3660. function set_error_msg($error = '') {
  3661. if ($this->debug > 0) {
  3662. error_log('New LP - In learnpath::set_error_msg()', 0);
  3663. }
  3664. if (empty ($error)) {
  3665. $this->error = '';
  3666. } else {
  3667. $this->error .= $error;
  3668. }
  3669. }
  3670. /**
  3671. * Launches the current item if not 'sco' (starts timer and make sure there is a record ready in the DB)
  3672. *
  3673. */
  3674. function start_current_item($allow_new_attempt = false) {
  3675. if ($this->debug > 0) {
  3676. error_log('New LP - In learnpath::start_current_item()', 0);
  3677. }
  3678. if ($this->current != 0 AND is_object($this->items[$this->current])) {
  3679. $type = $this->get_type();
  3680. $item_type = $this->items[$this->current]->get_type();
  3681. if (($type == 2 && $item_type != 'sco') OR ($type == 3 && $item_type != 'au') OR ($type == 1 && $item_type != TOOL_QUIZ && $item_type != TOOL_HOTPOTATOES)) {
  3682. $this->items[$this->current]->open($allow_new_attempt);
  3683. $this->autocomplete_parents($this->current);
  3684. $prereq_check = $this->prerequisites_match($this->current);
  3685. $this->items[$this->current]->save(false, $prereq_check);
  3686. //$this->update_queue[$this->last] = $this->items[$this->last]->get_status();
  3687. } else {
  3688. //if sco, then it is supposed to have been updated by some other call
  3689. }
  3690. if ($item_type == 'sco') {
  3691. $this->items[$this->current]->restart();
  3692. }
  3693. }
  3694. if ($this->debug > 0) {
  3695. error_log('New LP - End of learnpath::start_current_item()', 0);
  3696. }
  3697. return true;
  3698. }
  3699. /**
  3700. * Stops the processing and counters for the old item (as held in $this->last)
  3701. * @param
  3702. */
  3703. function stop_previous_item() {
  3704. if ($this->debug > 0) {
  3705. error_log('New LP - In learnpath::stop_previous_item()', 0);
  3706. }
  3707. if ($this->last != 0 AND $this->last != $this->current AND is_object($this->items[$this->last])) {
  3708. if ($this->debug > 2) {
  3709. error_log('New LP - In learnpath::stop_previous_item() - ' . $this->last . ' is object', 0);
  3710. }
  3711. switch ($this->get_type()) {
  3712. case '3' :
  3713. if ($this->items[$this->last]->get_type() != 'au') {
  3714. if ($this->debug > 2) {
  3715. error_log('New LP - In learnpath::stop_previous_item() - ' . $this->last . ' in lp_type 3 is <> au', 0);
  3716. }
  3717. $this->items[$this->last]->close();
  3718. //$this->autocomplete_parents($this->last);
  3719. //$this->update_queue[$this->last] = $this->items[$this->last]->get_status();
  3720. } else {
  3721. if ($this->debug > 2) {
  3722. error_log('New LP - In learnpath::stop_previous_item() - Item is an AU, saving is managed by AICC signals', 0);
  3723. }
  3724. }
  3725. case '2' :
  3726. if ($this->items[$this->last]->get_type() != 'sco') {
  3727. if ($this->debug > 2) {
  3728. error_log('New LP - In learnpath::stop_previous_item() - ' . $this->last . ' in lp_type 2 is <> sco', 0);
  3729. }
  3730. $this->items[$this->last]->close();
  3731. //$this->autocomplete_parents($this->last);
  3732. //$this->update_queue[$this->last] = $this->items[$this->last]->get_status();
  3733. } else {
  3734. if ($this->debug > 2) {
  3735. error_log('New LP - In learnpath::stop_previous_item() - Item is a SCO, saving is managed by SCO signals', 0);
  3736. }
  3737. }
  3738. break;
  3739. case '1' :
  3740. default :
  3741. if ($this->debug > 2) {
  3742. error_log('New LP - In learnpath::stop_previous_item() - ' . $this->last . ' in lp_type 1 is asset', 0);
  3743. }
  3744. $this->items[$this->last]->close();
  3745. break;
  3746. }
  3747. } else {
  3748. if ($this->debug > 2) {
  3749. error_log('New LP - In learnpath::stop_previous_item() - No previous element found, ignoring...', 0);
  3750. }
  3751. return false;
  3752. }
  3753. return true;
  3754. }
  3755. /**
  3756. * Updates the default view mode from fullscreen to embedded and inversely
  3757. * @return string The current default view mode ('fullscreen' or 'embedded')
  3758. */
  3759. function update_default_view_mode() {
  3760. if ($this->debug > 0) {
  3761. error_log('New LP - In learnpath::update_default_view_mode()', 0);
  3762. }
  3763. $lp_table = Database :: get_course_table(TABLE_LP_MAIN);
  3764. $sql = "SELECT * FROM $lp_table WHERE id = " . $this->get_id();
  3765. $res = api_sql_query($sql, __FILE__, __LINE__);
  3766. if (Database :: num_rows($res) > 0) {
  3767. $row = Database :: fetch_array($res);
  3768. $view_mode = $row['default_view_mod'];
  3769. if ($view_mode == 'fullscreen') {
  3770. $view_mode = 'embedded';
  3771. }
  3772. elseif ($view_mode == 'embedded') {
  3773. $view_mode = 'fullscreen';
  3774. }
  3775. $sql = "UPDATE $lp_table SET default_view_mod = '$view_mode' WHERE id = " . $this->get_id();
  3776. $res = api_sql_query($sql, __FILE__, __LINE__);
  3777. $this->mode = $view_mode;
  3778. return $view_mode;
  3779. } else {
  3780. if ($this->debug > 2) {
  3781. error_log('New LP - Problem in update_default_view() - could not find LP ' . $this->get_id() . ' in DB', 0);
  3782. }
  3783. }
  3784. return -1;
  3785. }
  3786. /**
  3787. * Updates the default behaviour about auto-commiting SCORM updates
  3788. * @return boolean True if auto-commit has been set to 'on', false otherwise
  3789. */
  3790. function update_default_scorm_commit() {
  3791. if ($this->debug > 0) {
  3792. error_log('New LP - In learnpath::update_default_scorm_commit()', 0);
  3793. }
  3794. $lp_table = Database :: get_course_table(TABLE_LP_MAIN);
  3795. $sql = "SELECT * FROM $lp_table WHERE id = " . $this->get_id();
  3796. $res = api_sql_query($sql, __FILE__, __LINE__);
  3797. if (Database :: num_rows($res) > 0) {
  3798. $row = Database :: fetch_array($res);
  3799. $force = $row['force_commit'];
  3800. if ($force == 1) {
  3801. $force = 0;
  3802. $force_return = false;
  3803. }
  3804. elseif ($force == 0) {
  3805. $force = 1;
  3806. $force_return = true;
  3807. }
  3808. $sql = "UPDATE $lp_table SET force_commit = $force WHERE id = " . $this->get_id();
  3809. $res = api_sql_query($sql, __FILE__, __LINE__);
  3810. $this->force_commit = $force_return;
  3811. return $force_return;
  3812. } else {
  3813. if ($this->debug > 2) {
  3814. error_log('New LP - Problem in update_default_scorm_commit() - could not find LP ' . $this->get_id() . ' in DB', 0);
  3815. }
  3816. }
  3817. return -1;
  3818. }
  3819. /**
  3820. * Updates the order of learning paths (goes through all of them by order and fills the gaps)
  3821. * @return bool True on success, false on failure
  3822. */
  3823. function update_display_order() {
  3824. $lp_table = Database :: get_course_table(TABLE_LP_MAIN);
  3825. $sql = "SELECT * FROM $lp_table ORDER BY display_order";
  3826. $res = api_sql_query($sql, __FILE__, __LINE__);
  3827. if ($res === false)
  3828. return false;
  3829. $lps = array ();
  3830. $lp_order = array ();
  3831. $num = Database :: num_rows($res);
  3832. //first check the order is correct, globally (might be wrong because
  3833. //of versions < 1.8.4)
  3834. if ($num > 0) {
  3835. $i = 1;
  3836. while ($row = Database :: fetch_array($res)) {
  3837. if ($row['display_order'] != $i) { //if we find a gap in the order, we need to fix it
  3838. $need_fix = true;
  3839. $sql_u = "UPDATE $lp_table SET display_order = $i WHERE id = " . $row['id'];
  3840. $res_u = api_sql_query($sql_u, __FILE__, __LINE__);
  3841. }
  3842. $i++;
  3843. }
  3844. }
  3845. return true;
  3846. }
  3847. /**
  3848. * Updates the "prevent_reinit" value that enables control on reinitialising items on second view
  3849. * @return boolean True if prevent_reinit has been set to 'on', false otherwise (or 1 or 0 in this case)
  3850. */
  3851. function update_reinit() {
  3852. if ($this->debug > 0) {
  3853. error_log('New LP - In learnpath::update_reinit()', 0);
  3854. }
  3855. $lp_table = Database :: get_course_table(TABLE_LP_MAIN);
  3856. $sql = "SELECT * FROM $lp_table WHERE id = " . $this->get_id();
  3857. $res = api_sql_query($sql, __FILE__, __LINE__);
  3858. if (Database :: num_rows($res) > 0) {
  3859. $row = Database :: fetch_array($res);
  3860. $force = $row['prevent_reinit'];
  3861. if ($force == 1) {
  3862. $force = 0;
  3863. }
  3864. elseif ($force == 0) {
  3865. $force = 1;
  3866. }
  3867. $sql = "UPDATE $lp_table SET prevent_reinit = $force WHERE id = " . $this->get_id();
  3868. $res = api_sql_query($sql, __FILE__, __LINE__);
  3869. $this->prevent_reinit = $force;
  3870. return $force;
  3871. } else {
  3872. if ($this->debug > 2) {
  3873. error_log('New LP - Problem in update_reinit() - could not find LP ' . $this->get_id() . ' in DB', 0);
  3874. }
  3875. }
  3876. return -1;
  3877. }
  3878. /**
  3879. * Updates the "scorm_debug" value that shows or hide the debug window
  3880. * @return boolean True if scorm_debug has been set to 'on', false otherwise (or 1 or 0 in this case)
  3881. */
  3882. function update_scorm_debug() {
  3883. if ($this->debug > 0) {
  3884. error_log('New LP - In learnpath::update_scorm_debug()', 0);
  3885. }
  3886. $lp_table = Database :: get_course_table(TABLE_LP_MAIN);
  3887. $sql = "SELECT * FROM $lp_table WHERE id = " . $this->get_id();
  3888. $res = api_sql_query($sql, __FILE__, __LINE__);
  3889. if (Database :: num_rows($res) > 0) {
  3890. $row = Database :: fetch_array($res);
  3891. $force = $row['debug'];
  3892. if ($force == 1) {
  3893. $force = 0;
  3894. }
  3895. elseif ($force == 0) {
  3896. $force = 1;
  3897. }
  3898. $sql = "UPDATE $lp_table SET debug = $force WHERE id = " . $this->get_id();
  3899. $res = api_sql_query($sql, __FILE__, __LINE__);
  3900. $this->scorm_debug = $force;
  3901. return $force;
  3902. } else {
  3903. if ($this->debug > 2) {
  3904. error_log('New LP - Problem in update_scorm_debug() - could not find LP ' . $this->get_id() . ' in DB', 0);
  3905. }
  3906. }
  3907. return -1;
  3908. }
  3909. /**
  3910. * Function that makes a call to the function sort_tree_array and create_tree_array
  3911. *
  3912. * @author Kevin Van Den Haute
  3913. *
  3914. * @param unknown_type $array
  3915. */
  3916. function tree_array($array) {
  3917. if ($this->debug > 1) {
  3918. error_log('New LP - In learnpath::tree_array()', 0);
  3919. }
  3920. $array = $this->sort_tree_array($array);
  3921. $this->create_tree_array($array);
  3922. }
  3923. /**
  3924. * Creates an array with the elements of the learning path tree in it
  3925. *
  3926. * @author Kevin Van Den Haute
  3927. *
  3928. * @param array $array
  3929. * @param int $parent
  3930. * @param int $depth
  3931. * @param array $tmp
  3932. */
  3933. function create_tree_array($array, $parent = 0, $depth = -1, $tmp = array ()) {
  3934. if ($this->debug > 1) {
  3935. error_log('New LP - In learnpath::create_tree_array())', 0);
  3936. }
  3937. if (is_array($array)) {
  3938. for ($i = 0; $i < count($array); $i++) {
  3939. if ($array[$i]['parent_item_id'] == $parent) {
  3940. if (!in_array($array[$i]['parent_item_id'], $tmp)) {
  3941. $tmp[] = $array[$i]['parent_item_id'];
  3942. $depth++;
  3943. }
  3944. $preq = (empty ($array[$i]['prerequisite']) ? '' : $array[$i]['prerequisite']);
  3945. $this->arrMenu[] = array (
  3946. 'id' => $array[$i]['id'],
  3947. 'item_type' => $array[$i]['item_type'],
  3948. 'title' => $array[$i]['title'],
  3949. 'path' => $array[$i]['path'],
  3950. 'description' => $array[$i]['description'],
  3951. 'parent_item_id' => $array[$i]['parent_item_id'],
  3952. 'previous_item_id' => $array[$i]['previous_item_id'],
  3953. 'next_item_id' => $array[$i]['next_item_id'],
  3954. 'min_score' => $array[$i]['min_score'],
  3955. 'max_score' => $array[$i]['max_score'],
  3956. 'mastery_score' => $array[$i]['mastery_score'],
  3957. 'display_order' => $array[$i]['display_order'],
  3958. 'prerequisite' => $preq,
  3959. 'depth' => $depth,
  3960. 'audio' => $array[$i]['audio']
  3961. );
  3962. $this->create_tree_array($array, $array[$i]['id'], $depth, $tmp);
  3963. }
  3964. }
  3965. }
  3966. }
  3967. /**
  3968. * Sorts a multi dimensional array by parent id and display order
  3969. * @author Kevin Van Den Haute
  3970. *
  3971. * @param array $array (array with al the learning path items in it)
  3972. *
  3973. * @return array
  3974. */
  3975. function sort_tree_array($array) {
  3976. foreach ($array as $key => $row) {
  3977. $parent[$key] = $row['parent_item_id'];
  3978. $position[$key] = $row['display_order'];
  3979. }
  3980. if (count($array) > 0)
  3981. array_multisort($parent, SORT_ASC, $position, SORT_ASC, $array);
  3982. return $array;
  3983. }
  3984. /**
  3985. * Function that creates a table structure with a learning path his modules, chapters and documents.
  3986. * Also the actions for the modules, chapters and documents are in this table.
  3987. *
  3988. * @author Kevin Van Den Haute
  3989. *
  3990. * @param int $lp_id
  3991. *
  3992. * @return string
  3993. */
  3994. function overview() {
  3995. $platform_charset = api_get_system_encoding();
  3996. if ($this->debug > 0) {
  3997. error_log('New LP - In learnpath::overview()', 0);
  3998. }
  3999. global $charset, $_course;
  4000. $_SESSION['gradebook'] = Security :: remove_XSS($_GET['gradebook']);
  4001. $return = '';
  4002. $tbl_lp_item = Database :: get_course_table(TABLE_LP_ITEM);
  4003. $sql = "SELECT * FROM " . $tbl_lp_item . "
  4004. WHERE lp_id = " . $this->lp_id;
  4005. $result = api_sql_query($sql, __FILE__, __LINE__);
  4006. $arrLP = array ();
  4007. while ($row = Database :: fetch_array($result)) {
  4008. $row['title'] = Security :: remove_XSS(api_convert_encoding($row['title'], $platform_charset, $this->encoding));
  4009. $row['description'] = Security :: remove_XSS(api_convert_encoding($row['description'], $platform_charset, $this->encoding));
  4010. $arrLP[] = array (
  4011. 'id' => $row['id'],
  4012. 'item_type' => $row['item_type'],
  4013. 'title' => $row['title'],
  4014. 'path' => $row['path'],
  4015. 'description' => $row['description'],
  4016. 'parent_item_id' => $row['parent_item_id'],
  4017. 'previous_item_id' => $row['previous_item_id'],
  4018. 'next_item_id' => $row['next_item_id'],
  4019. 'max_score' => $row['max_score'],
  4020. 'min_score' => $row['min_score'],
  4021. 'mastery_score' => $row['mastery_score'],
  4022. 'prerequisite' => $row['prerequisite'],
  4023. 'display_order' => $row['display_order'],
  4024. 'audio' => $row['audio']
  4025. );
  4026. }
  4027. $this->tree_array($arrLP);
  4028. $arrLP = $this->arrMenu;
  4029. unset ($this->arrMenu);
  4030. if (api_is_allowed_to_edit()) {
  4031. $gradebook = Security :: remove_XSS($_GET['gradebook']);
  4032. $return .= '<div class="actions">';
  4033. $return .= '<a href="' . api_get_self() . '?cidReq=' . Security :: remove_XSS($_GET['cidReq']) . '&amp;gradebook=' . $gradebook . '&amp;action=build&amp;lp_id=' . $this->lp_id . '" title="' . get_lang("Build") . '">' . Display :: return_icon('learnpath_build.gif', get_lang('Build')) . ' ' . get_lang('Build') . '</a>';
  4034. $return .= '<span>' . Display :: return_icon('learnpath_organize_na.gif', get_lang("BasicOverview")) . ' <b>' . get_lang('BasicOverview') . '</b></span>';
  4035. $return .= '<a href="lp_controller.php?cidReq=' . Security :: remove_XSS($_GET['cidReq']) . '&action=view&lp_id=' . $this->lp_id . '">' . Display :: return_icon('learnpath_view.gif', get_lang("Display")) . ' ' . get_lang('Display') . '</a> ' . Display :: return_icon('i.gif');
  4036. $return .= '<a href="lp_controller.php?cidReq=' . Security :: remove_XSS($_GET['cidReq']) . '&amp;action=edit&amp;lp_id=' . Security :: remove_XSS($_GET['lp_id']) . '">' . Display :: return_icon('edit.gif', get_lang('CourseSettings')) . ' ' . get_lang('CourseSettings') . '</a>';
  4037. $return .= '<a href="' . api_get_self() . '?cidReq=' . Security :: remove_XSS($_GET['cidReq']) . '&amp;action=' . Security :: remove_XSS($_GET['action']) . '&amp;lp_id=' . Security :: remove_XSS($_GET['lp_id']) . '&amp;updateaudio=true">' . Display :: return_icon('audio.gif', get_lang('UpdateAllAudioFragments')) . ' ' . get_lang('UpdateAllAudioFragments') . '</a>';
  4038. $return .= '<a href="' . api_get_self() . '?cidReq=' . Security :: remove_XSS($_GET['cidReq']) . '&amp;gradebook=' . $gradebook . '&amp;action=add_item&amp;type=chapter&amp;lp_id=' . $_SESSION['oLP']->lp_id . '" title="' . get_lang("NewChapter") . '"><img alt="' . get_lang("NewChapter") . '" src="../img/lp_dokeos_chapter_add.gif" title="' . get_lang("NewChapter") . '" />' . get_lang("NewChapter") . '</a>';
  4039. $return .= '<a href="' . api_get_self() . '?cidReq=' . Security :: remove_XSS($_GET['cidReq']) . '&amp;gradebook=' . $gradebook . '&amp;action=add_item&amp;type=step&amp;lp_id=' . $_SESSION['oLP']->lp_id . '" title="' . get_lang("NewStep") . '"><img alt="' . get_lang("NewStep") . '" src="../img/new_test.gif" title="' . get_lang("NewStep") . '" />' . get_lang("NewStep") . '</a>';
  4040. $return .= '</div>';
  4041. }
  4042. // we need to start a form when we want to update all the mp3 files
  4043. if ($_GET['updateaudio'] == 'true' AND count($arrLP) <> 0) {
  4044. $return .= '<form action="' . api_get_self() . '?cidReq=' . Security :: remove_XSS($_GET['cidReq']) . '&amp;action=' . Security :: remove_XSS($_GET['action']) . '&amp;lp_id=' . Security :: remove_XSS($_GET['lp_id']) . '" method="post" enctype="multipart/form-data" name="updatemp3" id="updatemp3">';
  4045. $return .= Display :: display_warning_message(get_lang('LeaveEmptyToKeepCurrentFile'));
  4046. }
  4047. $return .= '<table class="data_table">' . "\n";
  4048. $return .= "\t" . '<tr>' . "\n";
  4049. $return .= "\t" . '<th width="60%">' . get_lang("Title") . '</th>' . "\n";
  4050. //$return .= "\t" . '<th>'.get_lang("Description").'</th>' . "\n";
  4051. $return .= "\t" . '<th>' . get_lang("Audio") . '</th>' . "\n";
  4052. $return .= "\t" . '<th>' . get_lang("Move") . '</th>' . "\n";
  4053. $return .= "\t" . '<th>' . get_lang("Actions") . '</th>' . "\n";
  4054. $return .= "\t" . '</tr>' . "\n";
  4055. for ($i = 0; $i < count($arrLP); $i++) {
  4056. $title = $arrLP[$i]['title'];
  4057. if ($arrLP[$i]['description'] == '')
  4058. $arrLP[$i]['description'] = '&nbsp;';
  4059. if (($i % 2) == 0) {
  4060. $oddclass = "row_odd";
  4061. } else {
  4062. $oddclass = "row_even";
  4063. }
  4064. $return .= "\t" . '<tr class="' . $oddclass . '">' . "\n";
  4065. $icon_name = str_replace(' ', '', $arrLP[$i]['item_type']);
  4066. if (file_exists("../img/lp_" . $icon_name . ".png")) {
  4067. $return .= "\t\t" . '<td style="padding-left:' . $arrLP[$i]['depth'] * 10 . 'px;"><img align="left" src="../img/lp_' . $icon_name . '.png" style="margin-right:3px;" />' . $title . '</td>' . "\n";
  4068. } else
  4069. if (file_exists("../img/lp_" . $icon_name . ".gif")) {
  4070. $return .= "\t\t" . '<td style="padding-left:' . $arrLP[$i]['depth'] * 10 . 'px;"><img align="left" src="../img/lp_' . $icon_name . '.gif" style="margin-right:3px;" />' . $title . '</td>' . "\n";
  4071. } else {
  4072. //$return .= "\t\t" . '<td style="padding-left:' . $arrLP[$i]['depth'] * 10 . 'px;">' . Display::display_icon('folder_document.gif','',array('style'=>'margin-right:3px;')) . $title . '</td>' . "\n";
  4073. $return .= "\t\t" . '<td style="padding-left:' . $arrLP[$i]['depth'] * 10 . 'px;"><img align="left" src="../img/folder_document.gif" style="margin-right:3px;" />' . $title . '</td>' . "\n";
  4074. }
  4075. //$return .= "\t\t" . '<td>' . stripslashes($arrLP[$i]['description']) . '</td>' . "\n";
  4076. // The audio column
  4077. $return .= "\t\t" . '<td align="center">';
  4078. if (!$_GET['updateaudio'] OR $_GET['updateaudio'] <> 'true') {
  4079. if (!empty ($arrLP[$i]['audio'])) {
  4080. $return .= '<span id="container' . $i . '"><a href="http://www.macromedia.com/go/getflashplayer">Get the Flash Player</a> to see this player.</span>';
  4081. $return .= '<script type="text/javascript" src="../inc/lib/mediaplayer/swfobject.js"></script>';
  4082. $return .= '<script type="text/javascript">
  4083. var s1 = new SWFObject("../inc/lib/mediaplayer/player.swf","ply","250","20","9","#FFFFFF");
  4084. s1.addParam("allowscriptaccess","always");
  4085. s1.addParam("flashvars","file=../../courses/' . $_course['path'] . '/document/audio/' . $arrLP[$i]['audio'] . '");
  4086. s1.write("container' . $i . '");
  4087. </script>';
  4088. } else {
  4089. $return .= ' - ';
  4090. }
  4091. } else {
  4092. if ($arrLP[$i]['item_type'] != 'dokeos_chapter' && $arrLP[$i]['item_type'] != 'dokeos_module' && $arrLP[$i]['item_type'] != 'dir') {
  4093. $return .= ' <input type="file" name="mp3file' . $arrLP[$i]['id'] . '" id="mp3file" />';
  4094. if (!empty ($arrLP[$i]['audio'])) {
  4095. $return .= '<br /><input type="checkbox" name="removemp3' . $arrLP[$i]['id'] . '" id="checkbox' . $arrLP[$i]['id'] . '" />' . get_lang('RemoveAudio');
  4096. }
  4097. }
  4098. }
  4099. $return .= '</td>' . "\n";
  4100. if (api_is_allowed_to_edit()) {
  4101. $return .= "\t\t" . '<td align="center">' . "\n";
  4102. if ($arrLP[$i]['previous_item_id'] != 0) {
  4103. $return .= "\t\t\t" . '<a href="' . api_get_self() . '?cidReq=' . $_GET['cidReq'] . '&amp;action=move_item&amp;direction=up&amp;id=' . $arrLP[$i]['id'] . '&amp;lp_id=' . $this->lp_id . '">';
  4104. $return .= '<img style="margin:1px;" alt="" src="../img/arrow_up_' . ($arrLP[$i]['depth'] % 3) . '.gif" title="' . get_lang('MoveUp') . '"/>';
  4105. $return .= '</a>' . "\n";
  4106. } else
  4107. $return .= "\t\t\t" . '<img alt="" src="../img/blanco.png" title="" />' . "\n";
  4108. if ($arrLP[$i]['next_item_id'] != 0) {
  4109. $return .= "\t\t\t" . '<a href="' . api_get_self() . '?cidReq=' . $_GET['cidReq'] . '&amp;action=move_item&amp;direction=down&amp;id=' . $arrLP[$i]['id'] . '&amp;lp_id=' . $this->lp_id . '">';
  4110. $return .= '<img style="margin:1px;" src="../img/arrow_down_' . ($arrLP[$i]['depth'] % 3) . '.gif" title="' . get_lang('MoveDown') . '" />';
  4111. $return .= '</a>' . "\n";
  4112. } else
  4113. $return .= "\t\t\t" . '<img alt="" src="../img/blanco.png" title="" />' . "\n";
  4114. $return .= "\t\t" . '</td>' . "\n";
  4115. $return .= "\t\t" . '<td align="center">' . "\n";
  4116. if ($arrLP[$i]['item_type'] != 'dokeos_chapter' && $arrLP[$i]['item_type'] != 'dokeos_module') {
  4117. $return .= "\t\t\t" . '<a href="' . api_get_self() . '?cidReq=' . $_GET['cidReq'] . '&amp;action=edit_item&amp;view=build&amp;id=' . $arrLP[$i]['id'] . '&amp;lp_id=' . $this->lp_id . '&amp;path_item=' . $arrLP[$i]['path'] . '">';
  4118. $return .= '<img style="margin:1px;" alt="" src="../img/edit.gif" title="' . get_lang('_edit_learnpath_module') . '" />';
  4119. $return .= '</a>' . "\n";
  4120. } else {
  4121. $return .= "\t\t\t" . '<a href="' . api_get_self() . '?cidReq=' . $_GET['cidReq'] . '&amp;action=edit_item&amp;id=' . $arrLP[$i]['id'] . '&amp;lp_id=' . $this->lp_id . '&amp;path_item=' . $arrLP[$i]['path'] . '">';
  4122. $return .= '<img style="margin:1px;" alt="" src="../img/edit.gif" title="' . get_lang('_edit_learnpath_module') . '" />';
  4123. $return .= '</a>' . "\n";
  4124. }
  4125. $return .= "\t\t\t" . '<a href="' . api_get_self() . '?cidReq=' . $_GET['cidReq'] . '&amp;action=delete_item&amp;id=' . $arrLP[$i]['id'] . '&amp;lp_id=' . $this->lp_id . '" onclick="return confirmation(\'' . addslashes($title) . '\');">';
  4126. $return .= '<img style="margin:1px;" alt="" src="../img/delete.gif" title="' . get_lang('_delete_learnpath_module') . '" />';
  4127. $return .= '</a>' . "\n";
  4128. $return .= "\t\t" . '</td>' . "\n";
  4129. }
  4130. $return .= "\t" . '</tr>' . "\n";
  4131. }
  4132. if (count($arrLP) == 0) {
  4133. $return .= "\t" . '<tr>' . "\n";
  4134. $return .= "\t\t" . '<td colspan="4">' . get_lang("NoItemsInLp") . '</td>' . "\n";
  4135. $return .= "\t" . '</tr>' . "\n";
  4136. }
  4137. $return .= '</table>' . "\n";
  4138. // we need to close the form when we are updating the mp3 files
  4139. if ($_GET['updateaudio'] == 'true') {
  4140. $return .= '<div style="margin:40px 0; float:right;"><button class="save" type="submit" name="save_audio" id="save_audio">' . get_lang('SaveAudioAndOrganization') . '</button></div>'; // TODO: What kind of language variable is this?
  4141. }
  4142. // we need to close the form when we are updating the mp3 files
  4143. if ($_GET['updateaudio'] == 'true' AND count($arrLP) <> 0) {
  4144. $return .= '</form>';
  4145. }
  4146. return $return;
  4147. }
  4148. /**
  4149. * This function builds the action menu
  4150. *
  4151. * */
  4152. function build_action_menu() {
  4153. $gradebook = Security :: remove_XSS($_GET['gradebook']);
  4154. echo '<div class="actions">';
  4155. //echo '<span>'.Display::return_icon('learnpath_build.gif').' '.get_lang('Build').'</span>';
  4156. echo '<span>' . Display :: return_icon('learnpath_build_na.gif', get_lang('Build')) . ' <b>' . get_lang('Build') . '</b></span>';
  4157. echo '<a href="' . api_get_self() . '?cidReq=' . Security :: remove_XSS($_GET['cidReq']) . '&amp;gradebook=' . $gradebook . '&amp;action=admin_view&amp;lp_id=' . $_SESSION['oLP']->lp_id . '" title="' . get_lang("BasicOverview") . '">' . Display :: return_icon('learnpath_organize.gif', get_lang('BasicOverview')) . ' ' . get_lang('BasicOverview') . '</a>';
  4158. echo '<a href="lp_controller.php?cidReq=' . Security :: remove_XSS($_GET['cidReq']) . '&amp;gradebook=' . $gradebook . '&action=view&lp_id=' . $_SESSION['oLP']->lp_id . '">' . Display :: return_icon('learnpath_view.gif', get_lang('Display')) . ' ' . get_lang('Display') . '</a>';
  4159. Display :: display_icon('i.gif');
  4160. echo '<a href="lp_controller.php?cidReq=' . Security :: remove_XSS($_GET['cidReq']) . '&amp;action=edit&amp;lp_id=' . Security :: remove_XSS($_GET['lp_id']) . '">' . Display :: return_icon('edit.gif', get_lang('CourseSettings')) . ' ' . get_lang('CourseSettings') . '</a>';
  4161. echo '<a href="' . api_get_self() . '?cidReq=' . Security :: remove_XSS($_GET['cidReq']) . '&amp;action=admin_view&amp;lp_id=' . Security :: remove_XSS($_GET['lp_id']) . '&amp;updateaudio=true">' . Display :: return_icon('audio.gif', get_lang('UpdateAllAudioFragments')) . ' ' . get_lang('UpdateAllAudioFragments') . '</a>';
  4162. echo '<a href="' . api_get_self() . '?cidReq=' . Security :: remove_XSS($_GET['cidReq']) . '&amp;gradebook=' . $gradebook . '&amp;action=add_item&amp;type=chapter&amp;lp_id=' . $_SESSION['oLP']->lp_id . '" title="' . get_lang("NewChapter") . '">' . Display :: return_icon('lp_dokeos_chapter_add.gif', get_lang('NewChapter')) . ' ' . get_lang("NewChapter") . '</a>';
  4163. echo '<a href="' . api_get_self() . '?cidReq=' . Security :: remove_XSS($_GET['cidReq']) . '&amp;gradebook=' . $gradebook . '&amp;action=add_item&amp;type=step&amp;lp_id=' . $_SESSION['oLP']->lp_id . '" title="' . get_lang("NewStep") . '">' . Display :: return_icon('new_test.gif', get_lang('NewStep')) . ' ' . get_lang("NewStep") . '</a>';
  4164. echo '</div>';
  4165. }
  4166. /**
  4167. * This functions builds the LP tree based on data from the database.
  4168. *
  4169. * @return string
  4170. * @uses dtree.js :: necessary javascript for building this tree
  4171. */
  4172. function build_tree() {
  4173. $platform_charset = api_get_system_encoding();
  4174. $return = "<script type=\"text/javascript\">\n";
  4175. $return .= "\tm = new dTree('m');\n\n";
  4176. $return .= "\tm.config.folderLinks = true;\n";
  4177. $return .= "\tm.config.useCookies = true;\n";
  4178. $return .= "\tm.config.useIcons = true;\n";
  4179. $return .= "\tm.config.useLines = true;\n";
  4180. $return .= "\tm.config.useSelection = true;\n";
  4181. $return .= "\tm.config.useStatustext = false;\n\n";
  4182. $menu = 0;
  4183. $parent = '';
  4184. $return .= "\tm.add(" . $menu . ", -1, '" . addslashes(Security::remove_XSS(($this->name))) . "');\n";
  4185. $tbl_lp_item = Database :: get_course_table(TABLE_LP_ITEM);
  4186. $sql = " SELECT * FROM " . $tbl_lp_item . "
  4187. WHERE lp_id = " . Database :: escape_string($this->lp_id);
  4188. $result = api_sql_query($sql, __FILE__, __LINE__);
  4189. $arrLP = array ();
  4190. while ($row = Database :: fetch_array($result)) {
  4191. $row['title'] = Security :: remove_XSS(api_convert_encoding($row['title'], $platform_charset, $this->encoding));
  4192. $row['description'] = Security :: remove_XSS(api_convert_encoding($row['description'], $platform_charset, $this->encoding));
  4193. //$row['title'] = Security :: remove_XSS($row['title']);
  4194. $arrLP[] = array (
  4195. 'id' => $row['id'],
  4196. 'item_type' => $row['item_type'],
  4197. 'title' => $row['title'],
  4198. 'path' => $row['path'],
  4199. 'description' => $row['description'],
  4200. 'parent_item_id' => $row['parent_item_id'],
  4201. 'previous_item_id' => $row['previous_item_id'],
  4202. 'next_item_id' => $row['next_item_id'],
  4203. 'max_score' => $row['max_score'],
  4204. 'min_score' => $row['min_score'],
  4205. 'mastery_score' => $row['mastery_score'],
  4206. 'display_order' => $row['display_order']
  4207. );
  4208. }
  4209. $this->tree_array($arrLP);
  4210. $arrLP = $this->arrMenu;
  4211. unset ($this->arrMenu);
  4212. $title = '';
  4213. for ($i = 0; $i < count($arrLP); $i++) {
  4214. $title = addslashes($arrLP[$i]['title']);
  4215. $menu_page = api_get_self() . '?cidReq=' . Security :: remove_XSS($_GET['cidReq']) . '&amp;action=view_item&amp;id=' . $arrLP[$i]['id'] . '&amp;lp_id=' . $_SESSION['oLP']->lp_id;
  4216. $icon_name = str_replace(' ', '', $arrLP[$i]['item_type']);
  4217. if (file_exists("../img/lp_" . $icon_name . ".png")) {
  4218. $return .= "\tm.add(" . $arrLP[$i]['id'] . ", " . $arrLP[$i]['parent_item_id'] . ", '" . $title . "', '" . $menu_page . "', '', '', '../img/lp_" . $icon_name . ".png', '../img/lp_" . $icon_name . ".png');\n";
  4219. } else
  4220. if (file_exists("../img/lp_" . $icon_name . ".gif")) {
  4221. $return .= "\tm.add(" . $arrLP[$i]['id'] . ", " . $arrLP[$i]['parent_item_id'] . ", '" . $title . "', '" . $menu_page . "', '', '', '../img/lp_" . $icon_name . ".gif', '../img/lp_" . $icon_name . ".gif');\n";
  4222. } else {
  4223. $return .= "\tm.add(" . $arrLP[$i]['id'] . ", " . $arrLP[$i]['parent_item_id'] . ", '" . $title . "', '" . $menu_page . "', '', '', '../img/folder_document.gif', '../img/folder_document.gif');\n";
  4224. }
  4225. if ($menu < $arrLP[$i]['id'])
  4226. $menu = $arrLP[$i]['id'];
  4227. }
  4228. $return .= "\n\tdocument.write(m);\n";
  4229. $return .= "\t if(!m.selectedNode) m.s(1);";
  4230. $return .= "</script>\n";
  4231. return $return;
  4232. }
  4233. /**
  4234. * Create a new document //still needs some finetuning
  4235. *
  4236. * @param array $_course
  4237. * @return string
  4238. */
  4239. function create_document($_course) {
  4240. global $charset;
  4241. $dir = isset ($_GET['dir']) ? $_GET['dir'] : $_POST['dir']; // please do not modify this dirname formatting
  4242. if (strstr($dir, '..'))
  4243. $dir = '/';
  4244. if ($dir[0] == '.')
  4245. $dir = substr($dir, 1);
  4246. if ($dir[0] != '/')
  4247. $dir = '/' . $dir;
  4248. if ($dir[strlen($dir) - 1] != '/')
  4249. $dir .= '/';
  4250. $filepath = api_get_path('SYS_COURSE_PATH') . $_course['path'] . '/document' . $dir;
  4251. if (!is_dir($filepath)) {
  4252. $filepath = api_get_path('SYS_COURSE_PATH') . $_course['path'] . '/document/';
  4253. $dir = '/';
  4254. }
  4255. //stripslashes before calling replace_dangerous_char() because $_POST['title']
  4256. //is already escaped twice when it gets here
  4257. $title = replace_dangerous_char(stripslashes($_POST['title']));
  4258. $title = disable_dangerous_file($title);
  4259. $title = replace_accents($title);
  4260. $filename = $title;
  4261. $content = $_POST['content_lp'];
  4262. $tmp_filename = $filename;
  4263. $i = 0;
  4264. while (file_exists($filepath . $tmp_filename . '.html'))
  4265. $tmp_filename = $filename . '_' . ++ $i;
  4266. $filename = $tmp_filename . '.html';
  4267. $content = stripslashes(text_filter($content));
  4268. $content = str_replace(api_get_path('WEB_COURSE_PATH'), api_get_path(REL_PATH) . 'courses/', $content);
  4269. // change the path of mp3 to absolute
  4270. // first regexp deals with ../../../ urls
  4271. $content = preg_replace("|(flashvars=\"file=)(\.+/)+|", "$1" . api_get_path(REL_COURSE_PATH) . $_course['path'] . '/document/', $content);
  4272. //second regexp deals with audio/ urls
  4273. $content = preg_replace("|(flashvars=\"file=)([^/]+)/|", "$1" . api_get_path(REL_COURSE_PATH) . $_course['path'] . '/document/$2/', $content);
  4274. // for flv player : to prevent edition problem with firefox, we have to use a strange tip (don't blame me please)
  4275. $content = str_replace('</body>', '<style type="text/css">body{}</style></body>', $content);
  4276. if (!file_exists($filepath . $filename)) {
  4277. if ($fp = @ fopen($filepath . $filename, 'w')) {
  4278. fputs($fp, $content);
  4279. fclose($fp);
  4280. $file_size = filesize($filepath . $filename);
  4281. $save_file_path = $dir . $filename;
  4282. $document_id = add_document($_course, $save_file_path, 'file', $file_size, $filename . '.html');
  4283. if ($document_id) {
  4284. api_item_property_update($_course, TOOL_DOCUMENT, $document_id, 'DocumentAdded', api_get_user_id());
  4285. //update parent folders
  4286. //item_property_update_on_folder($_course, $_GET['dir'], $_user['user_id']);
  4287. $new_comment = (isset ($_POST['comment'])) ? trim($_POST['comment']) : '';
  4288. $new_title = (isset ($_POST['title'])) ? trim($_POST['title']) : '';
  4289. if ($new_comment || $new_title) {
  4290. $tbl_doc = Database :: get_course_table(TABLE_DOCUMENT);
  4291. $ct = '';
  4292. if ($new_comment)
  4293. $ct .= ", comment='" . $new_comment . "'";
  4294. if ($new_title)
  4295. $ct .= ", title='" . Database :: escape_string(htmlspecialchars($new_title, ENT_QUOTES, $charset)) . ".html '";
  4296. $sql_update = "
  4297. UPDATE " . $tbl_doc . "
  4298. SET " . substr($ct, 1) . "
  4299. WHERE id = " . $document_id;
  4300. api_sql_query($sql_update, __FILE__, __LINE__);
  4301. }
  4302. }
  4303. return $document_id;
  4304. }
  4305. }
  4306. }
  4307. /**
  4308. * Edit a document based on $_POST and $_GET parameters 'dir' and 'path'
  4309. *
  4310. * @param array $_course array
  4311. * @return void
  4312. */
  4313. function edit_document($_course) {
  4314. global $_configuration;
  4315. $dir = isset ($_GET['dir']) ? $_GET['dir'] : $_POST['dir']; // please do not modify this dirname formatting
  4316. if (strstr($dir, '..'))
  4317. $dir = '/';
  4318. if ($dir[0] == '.')
  4319. $dir = substr($dir, 1);
  4320. if ($dir[0] != '/')
  4321. $dir = '/' . $dir;
  4322. if ($dir[strlen($dir) - 1] != '/')
  4323. $dir .= '/';
  4324. $filepath = api_get_path('SYS_COURSE_PATH') . $_course['path'] . '/document' . $dir;
  4325. if (!is_dir($filepath)) {
  4326. $filepath = api_get_path('SYS_COURSE_PATH') . $_course['path'] . '/document/';
  4327. $dir = '/';
  4328. }
  4329. $table_doc = Database :: get_course_table(TABLE_DOCUMENT);
  4330. if (isset($_POST['path']) && !empty($_POST['path'])) {
  4331. $sql = "SELECT path
  4332. FROM " . $table_doc . "
  4333. WHERE id = " . Database::escape_string($_POST['path']);
  4334. $res = api_sql_query($sql, __FILE__, __LINE__);
  4335. $row = Database :: fetch_array($res);
  4336. $content = stripslashes($_POST['content_lp']);
  4337. $file = $filepath . $row['path'];
  4338. if ($fp = @ fopen($file, 'w')) {
  4339. $content = text_filter($content);
  4340. $content = str_replace(api_get_path('WEB_COURSE_PATH'), $_configuration['url_append'] . '/courses/', $content);
  4341. // change the path of mp3 to absolute
  4342. // first regexp deals with ../../../ urls
  4343. $content = preg_replace("|(flashvars=\"file=)(\.+/)+|", "$1" . api_get_path(REL_COURSE_PATH) . $_course['path'] . '/document/', $content);
  4344. //second regexp deals with audio/ urls
  4345. $content = preg_replace("|(flashvars=\"file=)([^/]+)/|", "$1" . api_get_path(REL_COURSE_PATH) . $_course['path'] . '/document/$2/', $content);
  4346. fputs($fp, $content);
  4347. fclose($fp);
  4348. }
  4349. }
  4350. }
  4351. /**
  4352. * Displays the selected item, with a panel for manipulating the item
  4353. *
  4354. * @param int $item_id
  4355. * @param string $msg
  4356. * @return string
  4357. */
  4358. function display_item($item_id, $iframe = true, $msg = '') {
  4359. global $_course; //will disappear
  4360. $return = '';
  4361. if (is_numeric($item_id)) {
  4362. $tbl_lp_item = Database :: get_course_table(TABLE_LP_ITEM);
  4363. $tbl_doc = Database :: get_course_table(TABLE_DOCUMENT);
  4364. $sql = "SELECT lp.*
  4365. FROM " . $tbl_lp_item . " as lp
  4366. WHERE lp.id = " . Database :: escape_string($item_id);
  4367. $result = api_sql_query($sql, __FILE__, __LINE__);
  4368. while ($row = Database :: fetch_array($result)) {
  4369. $_SESSION['parent_item_id'] = ($row['item_type'] == 'dokeos_chapter' || $row['item_type'] == 'dokeos_module' || $row['item_type'] == 'dir') ? $item_id : 0;
  4370. $return .= $this->display_manipulate($item_id, $row['item_type']);
  4371. $return .= '<div style="padding:10px;">';
  4372. if ($msg != '')
  4373. $return .= $msg;
  4374. $row['title'] = Security :: remove_XSS(api_convert_encoding($row['title'], api_get_system_encoding(), $this->encoding));
  4375. //$row['title'] = Security::remove_XSS(api_convert_encoding($row['title'], $this->encoding, api_get_system_encoding()));
  4376. $return .= '<p class="lp_title">' . $row['title'] . '</p>';
  4377. //$return .= '<p class="lp_text">' . ((trim($row['description']) == '') ? 'no description' : stripslashes($row['description'])) . '</p>';
  4378. //$return .= '<hr />';
  4379. if ($row['item_type'] == TOOL_DOCUMENT) {
  4380. $tbl_doc = Database :: get_course_table(TABLE_DOCUMENT);
  4381. $sql_doc = "SELECT path FROM " . $tbl_doc . " WHERE id = " . Database :: escape_string($row['path']);
  4382. $result = api_sql_query($sql_doc, __FILE__, __LINE__);
  4383. $path_file = Database :: result($result, 0, 0);
  4384. $path_parts = pathinfo($path_file);
  4385. if (in_array($path_parts['extension'], array (
  4386. 'html',
  4387. 'txt',
  4388. 'png',
  4389. 'jpg',
  4390. 'JPG',
  4391. 'jpeg',
  4392. 'JPEG',
  4393. 'gif',
  4394. 'swf'
  4395. ))) {
  4396. $return .= $this->display_document($row['path'], true, true);
  4397. }
  4398. }
  4399. $return .= '</div>';
  4400. }
  4401. }
  4402. return $return;
  4403. }
  4404. /**
  4405. * Shows the needed forms for editing a specific item
  4406. *
  4407. * @param int $item_id
  4408. * @return string
  4409. */
  4410. function display_edit_item($item_id) {
  4411. global $_course; //will disappear
  4412. $return = '';
  4413. if (is_numeric($item_id)) {
  4414. $tbl_lp_item = Database :: get_course_table(TABLE_LP_ITEM);
  4415. $sql = "SELECT *
  4416. FROM " . $tbl_lp_item . "
  4417. WHERE id = " . Database :: escape_string($item_id);
  4418. $res = api_sql_query($sql, __FILE__, __LINE__);
  4419. $row = Database :: fetch_array($res);
  4420. switch ($row['item_type']) {
  4421. case 'dokeos_chapter' :
  4422. case 'dir' :
  4423. case 'asset' :
  4424. case 'sco' :
  4425. if (isset ($_GET['view']) && $_GET['view'] == 'build') {
  4426. $return .= $this->display_manipulate($item_id, $row['item_type']);
  4427. $return .= $this->display_item_form($row['item_type'], get_lang("EditCurrentChapter") . ' :', 'edit', $item_id, $row);
  4428. } else {
  4429. $return .= $this->display_item_small_form($row['item_type'], get_lang("EditCurrentChapter") . ' :', $row);
  4430. }
  4431. break;
  4432. case TOOL_DOCUMENT :
  4433. $tbl_doc = Database :: get_course_table(TABLE_DOCUMENT);
  4434. $sql_step = " SELECT lp.*, doc.path as dir
  4435. FROM " . $tbl_lp_item . " as lp
  4436. LEFT JOIN " . $tbl_doc . " as doc ON doc.id = lp.path
  4437. WHERE lp.id = " . Database :: escape_string($item_id);
  4438. $res_step = api_sql_query($sql_step, __FILE__, __LINE__);
  4439. $row_step = Database :: fetch_array($res_step);
  4440. $return .= $this->display_manipulate($item_id, $row['item_type']);
  4441. $return .= $this->display_document_form('edit', $item_id, $row_step);
  4442. break;
  4443. case TOOL_LINK :
  4444. $link_id = (string) $row['path'];
  4445. if (ctype_digit($link_id)) {
  4446. $tbl_link = Database :: get_course_table(TABLE_LINK);
  4447. $sql_select = 'SELECT url FROM ' . $tbl_link . ' WHERE id = ' . Database :: escape_string($link_id);
  4448. $res_link = api_sql_query($sql_select, __FILE__, __LINE__);
  4449. $row_link = Database :: fetch_array($res_link);
  4450. if (is_array($row_link)) {
  4451. $row['url'] = $row_link['url'];
  4452. }
  4453. }
  4454. $return .= $this->display_manipulate($item_id, $row['item_type']);
  4455. $return .= $this->display_link_form('edit', $item_id, $row);
  4456. break;
  4457. case 'dokeos_module' :
  4458. if (isset ($_GET['view']) && $_GET['view'] == 'build') {
  4459. $return .= $this->display_manipulate($item_id, $row['item_type']);
  4460. $return .= $this->display_item_form($row['item_type'], get_lang("EditCurrentModule") . ' :', 'edit', $item_id, $row);
  4461. } else {
  4462. $return .= $this->display_item_small_form($row['item_type'], get_lang("EditCurrentModule") . ' :', $row);
  4463. }
  4464. break;
  4465. case TOOL_QUIZ :
  4466. $return .= $this->display_manipulate($item_id, $row['item_type']);
  4467. $return .= $this->display_quiz_form('edit', $item_id, $row);
  4468. break;
  4469. case TOOL_HOTPOTATOES :
  4470. $return .= $this->display_manipulate($item_id, $row['item_type']);
  4471. $return .= $this->display_hotpotatoes_form('edit', $item_id, $row);
  4472. break;
  4473. case TOOL_STUDENTPUBLICATION :
  4474. $return .= $this->display_manipulate($item_id, $row['item_type']);
  4475. $return .= $this->display_student_publication_form('edit', $item_id, $row);
  4476. break;
  4477. case TOOL_FORUM :
  4478. $return .= $this->display_manipulate($item_id, $row['item_type']);
  4479. $return .= $this->display_forum_form('edit', $item_id, $row);
  4480. break;
  4481. case TOOL_THREAD :
  4482. $return .= $this->display_manipulate($item_id, $row['item_type']);
  4483. $return .= $this->display_thread_form('edit', $item_id, $row);
  4484. break;
  4485. }
  4486. }
  4487. return $return;
  4488. }
  4489. /**
  4490. * Function that displays a list with al the resources that could be added to the learning path
  4491. *
  4492. * @return string
  4493. */
  4494. function display_resources() {
  4495. global $_course; //TODO: don't use globals
  4496. $return .= '<div class="sectiontitle">' . get_lang('CreateNewStep') . '</div>';
  4497. $return .= '<div class="sectioncomment"><a href="' . api_get_self() . '?cidReq=' . $_GET['cidReq'] . '&amp;action=add_item&amp;type=' . TOOL_DOCUMENT . '&amp;lp_id=' . $_SESSION['oLP']->lp_id . '">' . '<img title="Nuevo documento" src="../img/new_doc.gif" alt="Nuevo documento"/> ' . get_lang("NewDocument") . '</a></div>'; //TODO: A hardcoded Spanish text is here.
  4498. $return .= '<div class="sectiontitle">' . get_lang('UseAnExistingResource') . '</div>';
  4499. $return .= '<div class="sectioncomment">';
  4500. /* get all the docs */
  4501. $return .= $this->get_documents();
  4502. /* get all the exercises */
  4503. $return .= $this->get_exercises();
  4504. /* get all the links */
  4505. $return .= $this->get_links();
  4506. /* get al the student publications */
  4507. $return .= $this->get_student_publications();
  4508. /* get al the forums */
  4509. $return .= $this->get_forums();
  4510. $return .= '</div>';
  4511. return $return;
  4512. }
  4513. /**
  4514. * Returns the extension of a document
  4515. *
  4516. * @param unknown_type $filename
  4517. * @return unknown
  4518. */
  4519. function get_extension($filename) {
  4520. $explode = explode('.', $filename);
  4521. return $explode[count($explode) - 1];
  4522. }
  4523. /**
  4524. * Displays a document by id
  4525. *
  4526. * @param unknown_type $id
  4527. * @return unknown
  4528. */
  4529. function display_document($id, $show_title = false, $iframe = true, $edit_link = false) {
  4530. global $_course; //temporary
  4531. $return = '';
  4532. $tbl_doc = Database :: get_course_table(TABLE_DOCUMENT);
  4533. $sql_doc = "SELECT * FROM " . $tbl_doc . "
  4534. WHERE id = " . $id;
  4535. $res_doc = api_sql_query($sql_doc, __FILE__, __LINE__);
  4536. $row_doc = Database :: fetch_array($res_doc);
  4537. //if($show_title)
  4538. //$return .= '<p class="lp_title">' . $row_doc['title'] . ($edit_link ? ' [ <a href="' .api_get_self(). '?cidReq=' . $_GET['cidReq'] . '&amp;action=add_item&amp;type=' . TOOL_DOCUMENT . '&amp;file=' . $_GET['file'] . '&amp;edit=true&amp;lp_id=' . $_GET['lp_id'] . '">Edit this document</a> ]' : '') . '</p>';
  4539. //TODO: add a path filter
  4540. if ($iframe) {
  4541. $return .= '<iframe id="learnpath_preview_frame" frameborder="0" height="400" width="100%" scrolling="auto" src="' . api_get_path(WEB_COURSE_PATH) . $_course['path'] . '/document' . str_replace('%2F', '/', urlencode($row_doc['path'])) . '?' . api_get_cidreq() . '"></iframe>';
  4542. } else {
  4543. $return .= file_get_contents(api_get_path(SYS_COURSE_PATH) . $_course['path'] . '/document' . $row_doc['path']);
  4544. }
  4545. return $return;
  4546. }
  4547. /**
  4548. * Return HTML form to add/edit a quiz
  4549. *
  4550. * @param string Action (add/edit)
  4551. * @param integer Item ID if already exists
  4552. * @param mixed Extra information (quiz ID if integer)
  4553. * @return string HTML form
  4554. */
  4555. function display_quiz_form($action = 'add', $id = 0, $extra_info = '') {
  4556. global $charset;
  4557. $tbl_lp_item = Database :: get_course_table(TABLE_LP_ITEM);
  4558. $tbl_quiz = Database :: get_course_table(TABLE_QUIZ_TEST);
  4559. if ($id != 0 && is_array($extra_info)) {
  4560. $item_title = stripslashes($extra_info['title']);
  4561. $item_description = stripslashes($extra_info['description']);
  4562. }
  4563. elseif (is_numeric($extra_info)) {
  4564. $sql_quiz = "
  4565. SELECT
  4566. title,
  4567. description
  4568. FROM " . $tbl_quiz . "
  4569. WHERE id = " . $extra_info;
  4570. $result = api_sql_query($sql_quiz, __FILE__, __LINE__);
  4571. $row = Database :: fetch_array($result);
  4572. $item_title = $row['title'];
  4573. $item_description = $row['description'];
  4574. } else {
  4575. $item_title = '';
  4576. $item_description = '';
  4577. }
  4578. $return = ' <div class="row">
  4579. <div class="form_header">';
  4580. if ($id != 0 && is_array($extra_info))
  4581. $parent = $extra_info['parent_item_id'];
  4582. else
  4583. $parent = 0;
  4584. $sql = "
  4585. SELECT *
  4586. FROM " . $tbl_lp_item . "
  4587. WHERE
  4588. lp_id = " . $this->lp_id;
  4589. $result = api_sql_query($sql, __FILE__, __LINE__);
  4590. $arrLP = array ();
  4591. while ($row = Database :: fetch_array($result)) {
  4592. $arrLP[] = array (
  4593. 'id' => $row['id'],
  4594. 'item_type' => $row['item_type'],
  4595. 'title' => $row['title'],
  4596. 'path' => $row['path'],
  4597. 'description' => $row['description'],
  4598. 'parent_item_id' => $row['parent_item_id'],
  4599. 'previous_item_id' => $row['previous_item_id'],
  4600. 'next_item_id' => $row['next_item_id'],
  4601. 'display_order' => $row['display_order'],
  4602. 'max_score' => $row['max_score'],
  4603. 'min_score' => $row['min_score'],
  4604. 'mastery_score' => $row['mastery_score'],
  4605. 'prerequisite' => $row['prerequisite'],
  4606. 'max_time_allowed' => $row['max_time_allowed']
  4607. );
  4608. }
  4609. $this->tree_array($arrLP);
  4610. $arrLP = $this->arrMenu;
  4611. unset ($this->arrMenu);
  4612. if ($action == 'add')
  4613. $return .= get_lang("CreateTheExercise") . '&nbsp;:' . "\n";
  4614. elseif ($action == 'move') $return .= get_lang("MoveTheCurrentExercise") . '&nbsp;:' . "\n";
  4615. else
  4616. $return .= get_lang("EditCurrentExecice") . '&nbsp;:' . "\n";
  4617. if (isset ($_GET['edit']) && $_GET['edit'] == 'true') {
  4618. $return .= Display :: return_warning_message('<p class="lp_title">' . get_lang("Warning") . ' !</p>' . get_lang("WarningEditingDocument"));
  4619. }
  4620. $return .= ' </div>
  4621. </div>';
  4622. $return .= '<div class="sectioncomment">';
  4623. $return .= '<form method="POST">' . "\n";
  4624. $return .= "\t" . '<table class="lp_form">' . "\n";
  4625. if ($action != 'move') {
  4626. $return .= "\t\t" . '<tr>' . "\n";
  4627. $return .= "\t\t\t" . '<td class="label"><label for="idTitle">' . get_lang('Title') . '</label></td>' . "\n";
  4628. $return .= "\t\t\t" . '<td class="input"><input id="idTitle" name="title" size="44" type="text" class="learnpath_item_form" value="' . $item_title . '" /></td>' . "\n";
  4629. $return .= "\t\t" . '</tr>' . "\n";
  4630. }
  4631. $return .= "\t\t" . '<tr>' . "\n";
  4632. $return .= "\t\t\t" . '<td class="label"><label for="idParent">' . get_lang('Parent') . '</label></td>' . "\n";
  4633. $return .= "\t\t\t" . '<td class="input">' . "\n";
  4634. $return .= "\t\t\t\t" . '<select id="idParent" style="width:100%;" name="parent" onchange="load_cbo(this.value);" class="learnpath_item_form" size="1">';
  4635. $return .= "\t\t\t\t\t" . '<option class="top" value="0">' . $this->name . '</option>';
  4636. $arrHide = array (
  4637. $id
  4638. );
  4639. $parent_item_id = $_SESSION['parent_item_id'];
  4640. for ($i = 0; $i < count($arrLP); $i++) {
  4641. if ($action != 'add') {
  4642. if (($arrLP[$i]['item_type'] == 'dokeos_module' || $arrLP[$i]['item_type'] == 'dokeos_chapter' || $arrLP[$i]['item_type'] == 'dir') && !in_array($arrLP[$i]['id'], $arrHide) && !in_array($arrLP[$i]['parent_item_id'], $arrHide)) {
  4643. $return .= "\t\t\t\t\t" . '<option ' . (($parent == $arrLP[$i]['id']) ? 'selected="selected" ' : '') . 'style="padding-left:' . ($arrLP[$i]['depth'] * 10) . 'px;" value="' . $arrLP[$i]['id'] . '">' . api_convert_encoding($arrLP[$i]['title'], $charset, $this->encoding) . '</option>';
  4644. } else {
  4645. $arrHide[] = $arrLP[$i]['id'];
  4646. }
  4647. } else {
  4648. if ($arrLP[$i]['item_type'] == 'dokeos_module' || $arrLP[$i]['item_type'] == 'dokeos_chapter' || $arrLP[$i]['item_type'] == 'dir')
  4649. $return .= "\t\t\t\t\t" . '<option ' . (($parent_item_id == $arrLP[$i]['id']) ? 'selected="selected" ' : '') . 'style="padding-left:' . ($arrLP[$i]['depth'] * 10) . 'px;" value="' . $arrLP[$i]['id'] . '">' . api_convert_encoding($arrLP[$i]['title'], $charset, $this->encoding) . '</option>';
  4650. }
  4651. }
  4652. if (is_array($arrLP)) {
  4653. reset($arrLP);
  4654. }
  4655. $return .= "\t\t\t\t" . '</select>';
  4656. $return .= "\t\t\t" . '</td>' . "\n";
  4657. $return .= "\t\t" . '</tr>' . "\n";
  4658. $return .= "\t\t" . '<tr>' . "\n";
  4659. $return .= "\t\t\t" . '<td class="label"><label for="idPosition">' . get_lang('Position') . '</label></td>' . "\n";
  4660. $return .= "\t\t\t" . '<td class="input">' . "\n";
  4661. $return .= "\t\t\t\t" . '<select class="learnpath_item_form" style="width:100%;" id="idPosition" name="previous" size="1">';
  4662. $return .= "\t\t\t\t\t" . '<option class="top" value="0">' . get_lang('FirstPosition') . '</option>';
  4663. for ($i = 0; $i < count($arrLP); $i++) {
  4664. if ($arrLP[$i]['parent_item_id'] == $parent && $arrLP[$i]['id'] != $id) {
  4665. if ($extra_info['previous_item_id'] == $arrLP[$i]['id'])
  4666. $selected = 'selected="selected" ';
  4667. elseif ($action == 'add') $selected = 'selected="selected" ';
  4668. else
  4669. $selected = '';
  4670. $return .= "\t\t\t\t\t" . '<option ' . $selected . 'value="' . $arrLP[$i]['id'] . '">' . get_lang("After") . ' "' . api_convert_encoding($arrLP[$i]['title'], $charset, $this->encoding) . '"</option>';
  4671. }
  4672. }
  4673. $return .= "\t\t\t\t" . '</select>';
  4674. $return .= "\t\t\t" . '</td>' . "\n";
  4675. $return .= "\t\t" . '</tr>' . "\n";
  4676. if ($action != 'move') {
  4677. $id_prerequisite = 0;
  4678. if (is_array($arrLP)) {
  4679. foreach ($arrLP as $key => $value) {
  4680. if ($value['id'] == $id) {
  4681. $id_prerequisite = $value['prerequisite'];
  4682. break;
  4683. }
  4684. }
  4685. }
  4686. $arrHide = array ();
  4687. for ($i = 0; $i < count($arrLP); $i++) {
  4688. if ($arrLP[$i]['id'] != $id && $arrLP[$i]['item_type'] != 'dokeos_chapter') {
  4689. if ($extra_info['previous_item_id'] == $arrLP[$i]['id'])
  4690. $s_selected_position = $arrLP[$i]['id'];
  4691. elseif ($action == 'add') $s_selected_position = 0;
  4692. $arrHide[$arrLP[$i]['id']]['value'] = api_convert_encoding($arrLP[$i]['title'], $charset, $this->encoding);
  4693. }
  4694. }
  4695. /*//comented the prerequisites, only visible in edit (exercise)
  4696. $return .= "\t\t" . '<tr>' . "\n";
  4697. $return .= "\t\t\t" . '<td class="label"><label for="idPrerequisites">'.get_lang('Prerequisites').'</label></td>' . "\n";
  4698. $return .= "\t\t\t" . '<td class="input"><select name="prerequisites" id="prerequisites" class="learnpath_item_form"><option value="0">'.get_lang("NoPrerequisites").'</option>';
  4699. foreach($arrHide as $key => $value){
  4700. if($key==$s_selected_position && $action == 'add'){
  4701. $return .= '<option value="'.$key.'" selected="selected">'.$value['value'].'</option>';
  4702. }
  4703. elseif($key==$id_prerequisite && $action == 'edit'){
  4704. $return .= '<option value="'.$key.'" selected="selected">'.$value['value'].'</option>';
  4705. }
  4706. else{
  4707. $return .= '<option value="'.$key.'">'.$value['value'].'</option>';
  4708. }
  4709. }
  4710. $return .= "</select></td>";
  4711. */
  4712. $return .= "\t\t" . '</tr>' . "\n";
  4713. /*$return .= "\t\t" . '<tr>' . "\n";
  4714. $return .= "\t\t\t" . '<td class="label"><label for="maxTimeAllowed">' . get_lang('MaxTimeAllowed') . '</label></td>' . "\n";
  4715. $return .= "\t\t\t" . '<td class="input"><input name="maxTimeAllowed" style="width:98%;" id="maxTimeAllowed" value="' . $extra_info['max_time_allowed'] . '" /></td>';
  4716. //Remove temporaly the test description
  4717. //$return .= "\t\t\t" . '<td class="label"><label for="idDescription">'.get_lang("Description").' :</label></td>' . "\n";
  4718. //$return .= "\t\t\t" . '<td class="input"><textarea id="idDescription" name="description" rows="4">' . $item_description . '</textarea></td>' . "\n";
  4719. $return .= "\t\t" . '</tr>' . "\n"; */
  4720. }
  4721. $return .= "\t\t" . '<tr>' . "\n";
  4722. $return .= "\t\t\t" . '<td>&nbsp;</td><td><button class="save" name="submit_button" type="submit">' . get_lang('AddExercise') . '</button></td>' . "\n";
  4723. $return .= "\t\t" . '</tr>' . "\n";
  4724. $return .= "\t" . '</table>' . "\n";
  4725. if ($action == 'move') {
  4726. $return .= "\t" . '<input name="title" type="hidden" value="' . $item_title . '" />' . "\n";
  4727. $return .= "\t" . '<input name="description" type="hidden" value="' . $item_description . '" />' . "\n";
  4728. }
  4729. if (is_numeric($extra_info)) {
  4730. $return .= "\t" . '<input name="path" type="hidden" value="' . $extra_info . '" />' . "\n";
  4731. }
  4732. elseif (is_array($extra_info)) {
  4733. $return .= "\t" . '<input name="path" type="hidden" value="' . $extra_info['path'] . '" />' . "\n";
  4734. }
  4735. $return .= "\t" . '<input name="type" type="hidden" value="' . TOOL_QUIZ . '" />' . "\n";
  4736. $return .= "\t" . '<input name="post_time" type="hidden" value="' . time() . '" />' . "\n";
  4737. $return .= '</form>' . "\n";
  4738. $return .= '</div>' . "\n";
  4739. return $return;
  4740. }
  4741. /**
  4742. * Addition of Hotpotatoes tests
  4743. * @param string Action
  4744. * @param integer Internal ID of the item
  4745. * @param mixed Extra information - can be an array with title and description indexes
  4746. * @return string HTML structure to display the hotpotatoes addition formular
  4747. */
  4748. function display_hotpotatoes_form($action = 'add', $id = 0, $extra_info = '') {
  4749. global $charset;
  4750. $uploadPath = DIR_HOTPOTATOES; //defined in main_api
  4751. $tbl_lp_item = Database :: get_course_table(TABLE_LP_ITEM);
  4752. if ($id != 0 && is_array($extra_info)) {
  4753. $item_title = stripslashes($extra_info['title']);
  4754. $item_description = stripslashes($extra_info['description']);
  4755. }
  4756. elseif (is_numeric($extra_info)) {
  4757. $TBL_DOCUMENT = Database :: get_course_table(TABLE_DOCUMENT);
  4758. $sql_hot = "SELECT * FROM " . $TBL_DOCUMENT . "
  4759. WHERE path LIKE '" . $uploadPath . "/%/%htm%'
  4760. AND id = " . (int) $extra_info . " ORDER BY id ASC";
  4761. $res_hot = api_sql_query($sql_hot, __FILE__, __LINE__);
  4762. $row = Database :: fetch_array($res_hot);
  4763. $item_title = $row['title'];
  4764. $item_description = $row['description'];
  4765. if (!empty ($row['comment'])) {
  4766. $item_title = $row['comment'];
  4767. }
  4768. } else {
  4769. $item_title = '';
  4770. $item_description = '';
  4771. }
  4772. $item_title = api_convert_encoding($item_title, $charset, $this->encoding);
  4773. $item_description = api_convert_encoding($item_description, $charset, $this->encoding);
  4774. $return = '<div style="margin:3px 12px;">';
  4775. if ($id != 0 && is_array($extra_info))
  4776. $parent = $extra_info['parent_item_id'];
  4777. else
  4778. $parent = 0;
  4779. $sql = "
  4780. SELECT *
  4781. FROM " . $tbl_lp_item . "
  4782. WHERE
  4783. lp_id = " . $this->lp_id;
  4784. $result = api_sql_query($sql, __FILE__, __LINE__);
  4785. $arrLP = array ();
  4786. while ($row = Database :: fetch_array($result)) {
  4787. $arrLP[] = array (
  4788. 'id' => $row['id'],
  4789. 'item_type' => $row['item_type'],
  4790. 'title' => $row['title'],
  4791. 'path' => $row['path'],
  4792. 'description' => $row['description'],
  4793. 'parent_item_id' => $row['parent_item_id'],
  4794. 'previous_item_id' => $row['previous_item_id'],
  4795. 'next_item_id' => $row['next_item_id'],
  4796. 'display_order' => $row['display_order'],
  4797. 'max_score' => $row['max_score'],
  4798. 'min_score' => $row['min_score'],
  4799. 'mastery_score' => $row['mastery_score'],
  4800. 'prerequisite' => $row['prerequisite'],
  4801. 'max_time_allowed' => $row['max_time_allowed']
  4802. );
  4803. }
  4804. $this->tree_array($arrLP);
  4805. $arrLP = $this->arrMenu;
  4806. unset ($this->arrMenu);
  4807. if ($action == 'add')
  4808. $return .= '<p class="lp_title">' . get_lang("CreateTheExercise") . '&nbsp;:</p>' . "\n";
  4809. elseif ($action == 'move') $return .= '<p class="lp_title">' . get_lang("MoveTheCurrentExercise") . '&nbsp;:</p>' . "\n";
  4810. else
  4811. $return .= '<p class="lp_title">' . get_lang("EditCurrentExecice") . '&nbsp;:</p>' . "\n";
  4812. if (isset ($_GET['edit']) && $_GET['edit'] == 'true') {
  4813. $return .= Display :: return_warning_message('<p class="lp_title">' . get_lang("Warning") . ' !</p>' . get_lang("WarningEditingDocument"));
  4814. }
  4815. $return .= '<form method="POST">' . "\n";
  4816. $return .= "\t" . '<table cellpadding="0" cellspacing="0" class="lp_form">' . "\n";
  4817. $return .= "\t\t" . '<tr>' . "\n";
  4818. $return .= "\t\t\t" . '<td class="label"><label for="idParent">' . get_lang("Parent") . ' :</label></td>' . "\n";
  4819. $return .= "\t\t\t" . '<td class="input">' . "\n";
  4820. $return .= "\t\t\t\t" . '<select id="idParent" name="parent" onchange="load_cbo(this.value);" size="1">';
  4821. $return .= "\t\t\t\t\t" . '<option class="top" value="0">' . $this->name . '</option>';
  4822. $arrHide = array (
  4823. $id
  4824. );
  4825. if (count($arrLP) > 0) {
  4826. for ($i = 0; $i < count($arrLP); $i++) {
  4827. if ($action != 'add') {
  4828. if (($arrLP[$i]['item_type'] == 'dokeos_module' || $arrLP[$i]['item_type'] == 'dokeos_chapter' || $arrLP[$i]['item_type'] == 'dir') && !in_array($arrLP[$i]['id'], $arrHide) && !in_array($arrLP[$i]['parent_item_id'], $arrHide)) {
  4829. $return .= "\t\t\t\t\t" . '<option ' . (($parent == $arrLP[$i]['id']) ? 'selected="selected" ' : '') . 'style="padding-left:' . ($arrLP[$i]['depth'] * 10) . 'px;" value="' . $arrLP[$i]['id'] . '">' . api_convert_encoding($arrLP[$i]['title'], $charset, $this->encoding) . '</option>';
  4830. } else {
  4831. $arrHide[] = $arrLP[$i]['id'];
  4832. }
  4833. } else {
  4834. if ($arrLP[$i]['item_type'] == 'dokeos_module' || $arrLP[$i]['item_type'] == 'dokeos_chapter' || $arrLP[$i]['item_type'] == 'dir')
  4835. $return .= "\t\t\t\t\t" . '<option ' . (($parent == $arrLP[$i]['id']) ? 'selected="selected" ' : '') . 'style="padding-left:' . ($arrLP[$i]['depth'] * 10) . 'px;" value="' . $arrLP[$i]['id'] . '">' . api_convert_encoding($arrLP[$i]['title'], $charset, $this->encoding) . '</option>';
  4836. }
  4837. }
  4838. reset($arrLP);
  4839. }
  4840. $return .= "\t\t\t\t" . '</select>';
  4841. $return .= "\t\t\t" . '</td>' . "\n";
  4842. $return .= "\t\t" . '</tr>' . "\n";
  4843. $return .= "\t\t" . '<tr>' . "\n";
  4844. $return .= "\t\t\t" . '<td class="label"><label for="idPosition">' . get_lang("Position") . ' :</label></td>' . "\n";
  4845. $return .= "\t\t\t" . '<td class="input">' . "\n";
  4846. $return .= "\t\t\t\t" . '<select id="idPosition" name="previous" size="1">';
  4847. $return .= "\t\t\t\t\t" . '<option class="top" value="0">' . get_lang('FirstPosition') . '</option>';
  4848. for ($i = 0; $i < count($arrLP); $i++) {
  4849. if ($arrLP[$i]['parent_item_id'] == $parent && $arrLP[$i]['id'] != $id) {
  4850. if ($extra_info['previous_item_id'] == $arrLP[$i]['id'])
  4851. $selected = 'selected="selected" ';
  4852. elseif ($action == 'add') $selected = 'selected="selected" ';
  4853. else
  4854. $selected = '';
  4855. $return .= "\t\t\t\t\t" . '<option ' . $selected . 'value="' . $arrLP[$i]['id'] . '">' . get_lang("After") . ' "' . api_convert_encoding($arrLP[$i]['title'], $charset, $this->encoding) . '"</option>';
  4856. }
  4857. }
  4858. $return .= "\t\t\t\t" . '</select>';
  4859. $return .= "\t\t\t" . '</td>' . "\n";
  4860. $return .= "\t\t" . '</tr>' . "\n";
  4861. if ($action != 'move') {
  4862. $return .= "\t\t" . '<tr>' . "\n";
  4863. $return .= "\t\t\t" . '<td class="label"><label for="idTitle">' . get_lang("Title") . ' :</label></td>' . "\n";
  4864. $return .= "\t\t\t" . '<td class="input"><input id="idTitle" name="title" type="text" value="' . $item_title . '" /></td>' . "\n";
  4865. $return .= "\t\t" . '</tr>' . "\n";
  4866. $id_prerequisite = 0;
  4867. if (is_array($arrLP) && count($arrLP) > 0) {
  4868. foreach ($arrLP as $key => $value) {
  4869. if ($value['id'] == $id) {
  4870. $id_prerequisite = $value['prerequisite'];
  4871. break;
  4872. }
  4873. }
  4874. $arrHide = array ();
  4875. for ($i = 0; $i < count($arrLP); $i++) {
  4876. if ($arrLP[$i]['id'] != $id && $arrLP[$i]['item_type'] != 'dokeos_chapter') {
  4877. if ($extra_info['previous_item_id'] == $arrLP[$i]['id'])
  4878. $s_selected_position = $arrLP[$i]['id'];
  4879. elseif ($action == 'add') $s_selected_position = 0;
  4880. $arrHide[$arrLP[$i]['id']]['value'] = api_convert_encoding($arrLP[$i]['title'], $charset, $this->encoding);
  4881. }
  4882. }
  4883. }
  4884. $return .= "\t\t" . '<tr>' . "\n";
  4885. //$return .= "\t\t\t" . '<td class="label"><label for="idPrerequisites">'.get_lang("Prerequisites").' :</label></td>' . "\n";
  4886. //$return .= "\t\t\t" . '<td class="input"><select name="prerequisites" id="prerequisites"><option value="0">'.get_lang("NoPrerequisites").'</option>';
  4887. foreach ($arrHide as $key => $value) {
  4888. if ($key == $s_selected_position && $action == 'add') {
  4889. $return .= '<option value="' . $key . '" selected="selected">' . $value['value'] . '</option>';
  4890. }
  4891. elseif ($key == $id_prerequisite && $action == 'edit') {
  4892. $return .= '<option value="' . $key . '" selected="selected">' . $value['value'] . '</option>';
  4893. } else {
  4894. $return .= '<option value="' . $key . '">' . $value['value'] . '</option>';
  4895. }
  4896. }
  4897. $return .= "</select></td>";
  4898. $return .= "\t\t" . '</tr>' . "\n";
  4899. $return .= "\t\t" . '<tr>' . "\n";
  4900. //Remove temporaly the test description
  4901. //$return .= "\t\t\t" . '<td class="label"><label for="idDescription">'.get_lang("Description").' :</label></td>' . "\n";
  4902. //$return .= "\t\t\t" . '<td class="input"><textarea id="idDescription" name="description" rows="4">' . $item_description . '</textarea></td>' . "\n";
  4903. $return .= "\t\t" . '</tr>' . "\n";
  4904. }
  4905. $return .= "\t\t" . '<tr>' . "\n";
  4906. $return .= "\t\t\t" . '<td> &nbsp;</td><td><button class="save" name="submit_button" type="submit">' . get_lang('AddHotpotatoes') . '</button></td>' . "\n";
  4907. $return .= "\t\t" . '</tr>' . "\n";
  4908. $return .= "\t" . '</table>' . "\n";
  4909. if ($action == 'move') {
  4910. $return .= "\t" . '<input name="title" type="hidden" value="' . $item_title . '" />' . "\n";
  4911. $return .= "\t" . '<input name="description" type="hidden" value="' . $item_description . '" />' . "\n";
  4912. }
  4913. if (is_numeric($extra_info)) {
  4914. $return .= "\t" . '<input name="path" type="hidden" value="' . $extra_info . '" />' . "\n";
  4915. }
  4916. elseif (is_array($extra_info)) {
  4917. $return .= "\t" . '<input name="path" type="hidden" value="' . $extra_info['path'] . '" />' . "\n";
  4918. }
  4919. $return .= "\t" . '<input name="type" type="hidden" value="' . TOOL_HOTPOTATOES . '" />' . "\n";
  4920. $return .= "\t" . '<input name="post_time" type="hidden" value="' . time() . '" />' . "\n";
  4921. $return .= '</form>' . "\n";
  4922. $return .= '</div>' . "\n";
  4923. return $return;
  4924. }
  4925. //fin du hotpot form
  4926. /**
  4927. * Return the form to display the forum edit/add option
  4928. *
  4929. * @param string Action (add/edit)
  4930. * @param integer ID of the lp_item if already exists
  4931. * @param mixed Forum ID or title
  4932. * @return string HTML form
  4933. */
  4934. function display_forum_form($action = 'add', $id = 0, $extra_info = '') {
  4935. global $charset;
  4936. $tbl_lp_item = Database :: get_course_table(TABLE_LP_ITEM);
  4937. $tbl_forum = Database :: get_course_table(TABLE_FORUM);
  4938. if ($id != 0 && is_array($extra_info)) {
  4939. $item_title = stripslashes($extra_info['title']);
  4940. }
  4941. elseif (is_numeric($extra_info)) {
  4942. $sql_forum = "
  4943. SELECT
  4944. forum_title as title, forum_comment as comment
  4945. FROM " . $tbl_forum . "
  4946. WHERE forum_id = " . $extra_info;
  4947. $result = api_sql_query($sql_forum, __FILE__, __LINE__);
  4948. $row = Database :: fetch_array($result);
  4949. $item_title = $row['title'];
  4950. $item_description = $row['comment'];
  4951. } else {
  4952. $item_title = '';
  4953. $item_description = '';
  4954. }
  4955. $item_title = api_convert_encoding($item_title, $charset, $this->encoding);
  4956. $item_description = api_convert_encoding($item_description, $charset, $this->encoding);
  4957. $return = ' <div class="row">
  4958. <div class="form_header">';
  4959. if ($id != 0 && is_array($extra_info))
  4960. $parent = $extra_info['parent_item_id'];
  4961. else
  4962. $parent = 0;
  4963. $sql = "
  4964. SELECT *
  4965. FROM " . $tbl_lp_item . "
  4966. WHERE
  4967. lp_id = " . $this->lp_id;
  4968. $result = api_sql_query($sql, __FILE__, __LINE__);
  4969. $arrLP = array ();
  4970. while ($row = Database :: fetch_array($result)) {
  4971. $arrLP[] = array (
  4972. 'id' => $row['id'],
  4973. 'item_type' => $row['item_type'],
  4974. 'title' => $row['title'],
  4975. 'path' => $row['path'],
  4976. 'description' => $row['description'],
  4977. 'parent_item_id' => $row['parent_item_id'],
  4978. 'previous_item_id' => $row['previous_item_id'],
  4979. 'next_item_id' => $row['next_item_id'],
  4980. 'display_order' => $row['display_order'],
  4981. 'max_score' => $row['max_score'],
  4982. 'min_score' => $row['min_score'],
  4983. 'mastery_score' => $row['mastery_score'],
  4984. 'prerequisite' => $row['prerequisite']
  4985. );
  4986. }
  4987. $this->tree_array($arrLP);
  4988. $arrLP = $this->arrMenu;
  4989. unset ($this->arrMenu);
  4990. if ($action == 'add')
  4991. $return .= get_lang("CreateTheForum") . '&nbsp;:' . "\n";
  4992. elseif ($action == 'move') $return .= get_lang("MoveTheCurrentForum") . '&nbsp;:' . "\n";
  4993. else
  4994. $return .= get_lang("EditCurrentForum") . '&nbsp;:' . "\n";
  4995. $return .= ' </div>
  4996. </div>';
  4997. $return .= '<div class="sectioncomment">';
  4998. $return .= '<form method="POST">' . "\n";
  4999. $return .= "\t" . '<table class="lp_form">' . "\n";
  5000. if ($action != 'move') {
  5001. $return .= "\t\t" . '<tr>' . "\n";
  5002. $return .= "\t\t\t" . '<td class="label"><label for="idTitle">' . get_lang('Title') . '</label></td>' . "\n";
  5003. $return .= "\t\t\t" . '<td class="input"><input id="idTitle" size="44" name="title" type="text" value="' . $item_title . '" class="learnpath_item_form" /></td>' . "\n";
  5004. $return .= "\t\t" . '</tr>' . "\n";
  5005. }
  5006. $return .= "\t\t" . '<tr>' . "\n";
  5007. $return .= "\t\t\t" . '<td class="label"><label for="idParent">' . get_lang('Parent') . '</label></td>' . "\n";
  5008. $return .= "\t\t\t" . '<td class="input">' . "\n";
  5009. $return .= "\t\t\t\t" . '<select id="idParent" style="width:100%;" name="parent" onchange="load_cbo(this.value);" class="learnpath_item_form" size="1">';
  5010. $return .= "\t\t\t\t\t" . '<option class="top" value="0">' . $this->name . '</option>';
  5011. $arrHide = array (
  5012. $id
  5013. );
  5014. $parent_item_id = $_SESSION['parent_item_id'];
  5015. for ($i = 0; $i < count($arrLP); $i++) {
  5016. if ($action != 'add') {
  5017. if (($arrLP[$i]['item_type'] == 'dokeos_module' || $arrLP[$i]['item_type'] == 'dokeos_chapter' || $arrLP[$i]['item_type'] == 'dir') && !in_array($arrLP[$i]['id'], $arrHide) && !in_array($arrLP[$i]['parent_item_id'], $arrHide)) {
  5018. $return .= "\t\t\t\t\t" . '<option ' . (($parent == $arrLP[$i]['id']) ? 'selected="selected" ' : '') . 'style="padding-left:' . ($arrLP[$i]['depth'] * 10) . 'px;" value="' . $arrLP[$i]['id'] . '">' . api_convert_encoding($arrLP[$i]['title'], $charset, $this->encoding) . '</option>';
  5019. } else {
  5020. $arrHide[] = $arrLP[$i]['id'];
  5021. }
  5022. } else {
  5023. if ($arrLP[$i]['item_type'] == 'dokeos_module' || $arrLP[$i]['item_type'] == 'dokeos_chapter' || $arrLP[$i]['item_type'] == 'dir')
  5024. $return .= "\t\t\t\t\t" . '<option ' . (($parent_item_id == $arrLP[$i]['id']) ? 'selected="selected" ' : '') . 'style="padding-left:' . ($arrLP[$i]['depth'] * 10) . 'px;" value="' . $arrLP[$i]['id'] . '">' . api_convert_encoding($arrLP[$i]['title'], $charset, $this->encoding) . '</option>';
  5025. }
  5026. }
  5027. if (is_array($arrLP)) {
  5028. reset($arrLP);
  5029. }
  5030. $return .= "\t\t\t\t" . '</select>';
  5031. $return .= "\t\t\t" . '</td>' . "\n";
  5032. $return .= "\t\t" . '</tr>' . "\n";
  5033. $return .= "\t\t" . '<tr>' . "\n";
  5034. $return .= "\t\t\t" . '<td class="label"><label for="idPosition">' . get_lang('Position') . '</label></td>' . "\n";
  5035. $return .= "\t\t\t" . '<td class="input">' . "\n";
  5036. $return .= "\t\t\t\t" . '<select id="idPosition" name="previous" style="width:100%;" size="1" class="learnpath_item_form">';
  5037. $return .= "\t\t\t\t\t" . '<option class="top" value="0">' . get_lang('FirstPosition') . '</option>';
  5038. for ($i = 0; $i < count($arrLP); $i++) {
  5039. if ($arrLP[$i]['parent_item_id'] == $parent && $arrLP[$i]['id'] != $id) {
  5040. if ($extra_info['previous_item_id'] == $arrLP[$i]['id'])
  5041. $selected = 'selected="selected" ';
  5042. elseif ($action == 'add') $selected = 'selected="selected" ';
  5043. else
  5044. $selected = '';
  5045. $return .= "\t\t\t\t\t" . '<option ' . $selected . 'value="' . $arrLP[$i]['id'] . '">' . get_lang("After") . ' "' . api_convert_encoding($arrLP[$i]['title'], $charset, $this->encoding) . '"</option>';
  5046. }
  5047. }
  5048. $return .= "\t\t\t\t" . '</select>';
  5049. $return .= "\t\t\t" . '</td>' . "\n";
  5050. $return .= "\t\t" . '</tr>' . "\n";
  5051. if ($action != 'move') {
  5052. $return .= "\t\t" . '<tr>' . "\n";
  5053. //Remove temporaly the test description
  5054. //$return .= "\t\t\t" . '<td class="label"><label for="idDescription">'.get_lang("Description").' :</label></td>' . "\n";
  5055. //$return .= "\t\t\t" . '<td class="input"><textarea id="idDescription" name="description" rows="4">' . $item_description . '</textarea></td>' . "\n";
  5056. $return .= "\t\t" . '</tr>' . "\n";
  5057. $id_prerequisite = 0;
  5058. if (is_array($arrLP)) {
  5059. foreach ($arrLP as $key => $value) {
  5060. if ($value['id'] == $id) {
  5061. $id_prerequisite = $value['prerequisite'];
  5062. break;
  5063. }
  5064. }
  5065. }
  5066. $arrHide = array ();
  5067. for ($i = 0; $i < count($arrLP); $i++) {
  5068. if ($arrLP[$i]['id'] != $id && $arrLP[$i]['item_type'] != 'dokeos_chapter') {
  5069. if ($extra_info['previous_item_id'] == $arrLP[$i]['id'])
  5070. $s_selected_position = $arrLP[$i]['id'];
  5071. elseif ($action == 'add') $s_selected_position = 0;
  5072. $arrHide[$arrLP[$i]['id']]['value'] = api_convert_encoding($arrLP[$i]['title'], $charset, $this->encoding);
  5073. }
  5074. }
  5075. /* //comented the prerequisites, only visible in edit (forum)
  5076. $return .= "\t\t" . '<tr>' . "\n";
  5077. $return .= "\t\t\t" . '<td class="label"><label for="idPrerequisites">'.get_lang('Prerequisites').'</label></td>' . "\n";
  5078. $return .= "\t\t\t" . '<td class="input"><select name="prerequisites" id="prerequisites" class="learnpath_item_form"><option value="0">'.get_lang("NoPrerequisites").'</option>';
  5079. foreach($arrHide as $key => $value) {
  5080. if($key==$s_selected_position && $action == 'add') {
  5081. $return .= '<option value="'.$key.'" selected="selected">'.$value['value'].'</option>';
  5082. }elseif($key==$id_prerequisite && $action == 'edit') {
  5083. $return .= '<option value="'.$key.'" selected="selected">'.$value['value'].'</option>';
  5084. } else {
  5085. $return .= '<option value="'.$key.'">'.$value['value'].'</option>';
  5086. }
  5087. }
  5088. $return .= "</select></td>";
  5089. */
  5090. $return .= "\t\t" . '</tr>' . "\n";
  5091. }
  5092. $return .= "\t\t" . '<tr>' . "\n";
  5093. $return .= "\t\t\t" . '<td>&nbsp;</td><td><button class="save" name="submit_button" type="submit"> ' . get_lang('AddForumToCourse') . ' </button></td>' . "\n";
  5094. $return .= "\t\t" . '</tr>' . "\n";
  5095. $return .= "\t" . '</table>' . "\n";
  5096. if ($action == 'move') {
  5097. $return .= "\t" . '<input name="title" type="hidden" value="' . $item_title . '" />' . "\n";
  5098. $return .= "\t" . '<input name="description" type="hidden" value="' . $item_description . '" />' . "\n";
  5099. }
  5100. if (is_numeric($extra_info)) {
  5101. $return .= "\t" . '<input name="path" type="hidden" value="' . $extra_info . '" />' . "\n";
  5102. }
  5103. elseif (is_array($extra_info)) {
  5104. $return .= "\t" . '<input name="path" type="hidden" value="' . $extra_info['path'] . '" />' . "\n";
  5105. }
  5106. $return .= "\t" . '<input name="type" type="hidden" value="' . TOOL_FORUM . '" />' . "\n";
  5107. $return .= "\t" . '<input name="post_time" type="hidden" value="' . time() . '" />' . "\n";
  5108. $return .= '</form>' . "\n";
  5109. $return .= '</div>' . "\n";
  5110. return $return;
  5111. }
  5112. /**
  5113. * Return HTML form to add/edit forum threads
  5114. * @param string Action (add/edit)
  5115. * @param integer Item ID if already exists in learning path
  5116. * @param mixed Extra information (thread ID if integer)
  5117. * @return string HTML form
  5118. */
  5119. function display_thread_form($action = 'add', $id = 0, $extra_info = '') {
  5120. global $charset;
  5121. echo '
  5122. <style>
  5123. div.row div.label {
  5124. width:110px;
  5125. }
  5126. div.row div.formw {
  5127. width: 82%;
  5128. }
  5129. </style>';
  5130. $tbl_lp_item = Database :: get_course_table(TABLE_LP_ITEM);
  5131. $tbl_forum = Database :: get_course_table(TABLE_FORUM_THREAD);
  5132. if ($id != 0 && is_array($extra_info)) {
  5133. $item_title = stripslashes($extra_info['title']);
  5134. }
  5135. elseif (is_numeric($extra_info)) {
  5136. $sql_forum = "
  5137. SELECT
  5138. thread_title as title
  5139. FROM " . $tbl_forum . "
  5140. WHERE thread_id = " . $extra_info;
  5141. $result = api_sql_query($sql_forum, __FILE__, __LINE__);
  5142. $row = Database :: fetch_array($result);
  5143. $item_title = $row['title'];
  5144. $item_description = '';
  5145. } else {
  5146. $item_title = '';
  5147. $item_description = '';
  5148. }
  5149. $item_title = api_convert_encoding($item_title, $charset, $this->encoding);
  5150. $item_description = api_convert_encoding($item_description, $charset, $this->encoding);
  5151. $return = '<div style="margin:3px 12px;">';
  5152. if ($id != 0 && is_array($extra_info))
  5153. $parent = $extra_info['parent_item_id'];
  5154. else
  5155. $parent = 0;
  5156. $sql = "
  5157. SELECT *
  5158. FROM " . $tbl_lp_item . "
  5159. WHERE
  5160. lp_id = " . $this->lp_id;
  5161. $result = api_sql_query($sql, __FILE__, __LINE__);
  5162. $arrLP = array ();
  5163. while ($row = Database :: fetch_array($result)) {
  5164. $arrLP[] = array (
  5165. 'id' => $row['id'],
  5166. 'item_type' => $row['item_type'],
  5167. 'title' => $row['title'],
  5168. 'path' => $row['path'],
  5169. 'description' => $row['description'],
  5170. 'parent_item_id' => $row['parent_item_id'],
  5171. 'previous_item_id' => $row['previous_item_id'],
  5172. 'next_item_id' => $row['next_item_id'],
  5173. 'display_order' => $row['display_order'],
  5174. 'max_score' => $row['max_score'],
  5175. 'min_score' => $row['min_score'],
  5176. 'mastery_score' => $row['mastery_score'],
  5177. 'prerequisite' => $row['prerequisite']
  5178. );
  5179. }
  5180. $this->tree_array($arrLP);
  5181. $arrLP = $this->arrMenu;
  5182. unset ($this->arrMenu);
  5183. if ($action == 'add')
  5184. $return .= '<p class="lp_title">' . get_lang("CreateTheForum") . '&nbsp;:</p>' . "\n";
  5185. elseif ($action == 'move') $return .= '<p class="lp_title">' . get_lang("MoveTheCurrentForum") . '&nbsp;:</p>' . "\n";
  5186. else
  5187. $return .= '<p class="lp_title">' . get_lang("EditCurrentForum") . '&nbsp;:</p>' . "\n";
  5188. $return .= '<form method="POST">' . "\n";
  5189. $return .= "\t" . '<table cellpadding="0" cellspacing="0" class="lp_form">' . "\n";
  5190. $return .= "\t\t" . '<tr>' . "\n";
  5191. $return .= "\t\t\t" . '<td class="label"><label for="idParent">' . get_lang("Parent") . '&nbsp;:</label></td>' . "\n";
  5192. $return .= "\t\t\t" . '<td class="input">' . "\n";
  5193. $return .= "\t\t\t\t" . '<select id="idParent" name="parent" onchange="load_cbo(this.value);" size="1">';
  5194. $return .= "\t\t\t\t\t" . '<option class="top" value="0">' . $this->name . '</option>';
  5195. $arrHide = array (
  5196. $id
  5197. );
  5198. for ($i = 0; $i < count($arrLP); $i++) {
  5199. if ($action != 'add') {
  5200. if (($arrLP[$i]['item_type'] == 'dokeos_module' || $arrLP[$i]['item_type'] == 'dokeos_chapter' || $arrLP[$i]['item_type'] == 'dir') && !in_array($arrLP[$i]['id'], $arrHide) && !in_array($arrLP[$i]['parent_item_id'], $arrHide)) {
  5201. $return .= "\t\t\t\t\t" . '<option ' . (($parent == $arrLP[$i]['id']) ? 'selected="selected" ' : '') . 'style="padding-left:' . ($arrLP[$i]['depth'] * 10) . 'px;" value="' . $arrLP[$i]['id'] . '">' . api_convert_encoding($arrLP[$i]['title'], $charset, $this->encoding) . '</option>';
  5202. } else {
  5203. $arrHide[] = $arrLP[$i]['id'];
  5204. }
  5205. } else {
  5206. if ($arrLP[$i]['item_type'] == 'dokeos_module' || $arrLP[$i]['item_type'] == 'dokeos_chapter' || $arrLP[$i]['item_type'] == 'dir')
  5207. $return .= "\t\t\t\t\t" . '<option ' . (($parent == $arrLP[$i]['id']) ? 'selected="selected" ' : '') . 'style="padding-left:' . ($arrLP[$i]['depth'] * 10) . 'px;" value="' . $arrLP[$i]['id'] . '">' . api_convert_encoding($arrLP[$i]['title'], $charset, $this->encoding) . '</option>';
  5208. }
  5209. }
  5210. if ($arrLP != null) {
  5211. reset($arrLP);
  5212. }
  5213. $return .= "\t\t\t\t" . '</select>';
  5214. $return .= "\t\t\t" . '</td>' . "\n";
  5215. $return .= "\t\t" . '</tr>' . "\n";
  5216. $return .= "\t\t" . '<tr>' . "\n";
  5217. $return .= "\t\t\t" . '<td class="label"><label for="idPosition">' . get_lang("Position") . '&nbsp;:</label></td>' . "\n";
  5218. $return .= "\t\t\t" . '<td class="input">' . "\n";
  5219. $return .= "\t\t\t\t" . '<select id="idPosition" name="previous" size="1">';
  5220. $return .= "\t\t\t\t\t" . '<option class="top" value="0">' . get_lang('FirstPosition') . '</option>';
  5221. for ($i = 0; $i < count($arrLP); $i++) {
  5222. if ($arrLP[$i]['parent_item_id'] == $parent && $arrLP[$i]['id'] != $id) {
  5223. if ($extra_info['previous_item_id'] == $arrLP[$i]['id'])
  5224. $selected = 'selected="selected" ';
  5225. elseif ($action == 'add') $selected = 'selected="selected" ';
  5226. else
  5227. $selected = '';
  5228. $return .= "\t\t\t\t\t" . '<option ' . $selected . 'value="' . $arrLP[$i]['id'] . '">' . get_lang("After") . ' "' . api_convert_encoding($arrLP[$i]['title'], $charset, $this->encoding) . '"</option>';
  5229. }
  5230. }
  5231. $return .= "\t\t\t\t" . '</select>';
  5232. $return .= "\t\t\t" . '</td>' . "\n";
  5233. $return .= "\t\t" . '</tr>' . "\n";
  5234. if ($action != 'move') {
  5235. $return .= "\t\t" . '<tr>' . "\n";
  5236. $return .= "\t\t\t" . '<td class="label"><label for="idTitle">' . get_lang("Title") . '&nbsp;:</label></td>' . "\n";
  5237. $return .= "\t\t\t" . '<td class="input"><input id="idTitle" name="title" type="text" value="' . $item_title . '" /></td>' . "\n";
  5238. $return .= "\t\t" . '</tr>' . "\n";
  5239. $return .= "\t\t" . '<tr>' . "\n";
  5240. //Remove temporaly the test description
  5241. //$return .= "\t\t\t" . '<td class="label"><label for="idDescription">'.get_lang("Description").' :</label></td>' . "\n";
  5242. //$return .= "\t\t\t" . '<td class="input"><textarea id="idDescription" name="description" rows="4">' . $item_description . '</textarea></td>' . "\n";
  5243. $return .= "\t\t" . '</tr>' . "\n";
  5244. $id_prerequisite = 0;
  5245. if ($arrLP != null) {
  5246. foreach ($arrLP as $key => $value) {
  5247. if ($value['id'] == $id) {
  5248. $id_prerequisite = $value['prerequisite'];
  5249. break;
  5250. }
  5251. }
  5252. }
  5253. $arrHide = array ();
  5254. for ($i = 0; $i < count($arrLP); $i++) {
  5255. if ($arrLP[$i]['id'] != $id && $arrLP[$i]['item_type'] != 'dokeos_chapter') {
  5256. if ($extra_info['previous_item_id'] == $arrLP[$i]['id'])
  5257. $s_selected_position = $arrLP[$i]['id'];
  5258. elseif ($action == 'add') $s_selected_position = 0;
  5259. $arrHide[$arrLP[$i]['id']]['value'] = api_convert_encoding($arrLP[$i]['title'], $charset, $this->encoding);
  5260. }
  5261. }
  5262. $return .= "\t\t" . '<tr>' . "\n";
  5263. $return .= "\t\t\t" . '<td class="label"><label for="idPrerequisites">' . get_lang("Prerequisites") . '&nbsp;:</label></td>' . "\n";
  5264. $return .= "\t\t\t" . '<td class="input"><select name="prerequisites" id="prerequisites"><option value="0">' . get_lang("NoPrerequisites") . '</option>';
  5265. foreach ($arrHide as $key => $value) {
  5266. if ($key == $s_selected_position && $action == 'add') {
  5267. $return .= '<option value="' . $key . '" selected="selected">' . $value['value'] . '</option>';
  5268. }
  5269. elseif ($key == $id_prerequisite && $action == 'edit') {
  5270. $return .= '<option value="' . $key . '" selected="selected">' . $value['value'] . '</option>';
  5271. } else {
  5272. $return .= '<option value="' . $key . '">' . $value['value'] . '</option>';
  5273. }
  5274. }
  5275. $return .= "</select></td>";
  5276. $return .= "\t\t" . '</tr>' . "\n";
  5277. }
  5278. $return .= "\t\t" . '<tr>' . "\n";
  5279. $return .= "\t\t\t" . '<td colspan="2">
  5280. <button class="save" name="submit_button" type="submit" value="'.get_lang('Ok').'" />'.get_lang('Ok').'</button></td>' . "\n";
  5281. $return .= "\t\t" . '</tr>' . "\n";
  5282. $return .= "\t" . '</table>' . "\n";
  5283. if ($action == 'move') {
  5284. $return .= "\t" . '<input name="title" type="hidden" value="' . $item_title . '" />' . "\n";
  5285. $return .= "\t" . '<input name="description" type="hidden" value="' . $item_description . '" />' . "\n";
  5286. }
  5287. if (is_numeric($extra_info)) {
  5288. $return .= "\t" . '<input name="path" type="hidden" value="' . $extra_info . '" />' . "\n";
  5289. }
  5290. elseif (is_array($extra_info)) {
  5291. $return .= "\t" . '<input name="path" type="hidden" value="' . $extra_info['path'] . '" />' . "\n";
  5292. }
  5293. $return .= "\t" . '<input name="type" type="hidden" value="' . TOOL_THREAD . '" />' . "\n";
  5294. $return .= "\t" . '<input name="post_time" type="hidden" value="' . time() . '" />' . "\n";
  5295. $return .= '</form>' . "\n";
  5296. $return .= '</div>' . "\n";
  5297. return $return;
  5298. }
  5299. /**
  5300. * Return the HTML form to display an item (generally a section/module item)
  5301. *
  5302. * @param string Item type (module/dokeos_module)
  5303. * @param string Title (optional, only when creating)
  5304. * @param string Action ('add'/'edit')
  5305. * @param integer lp_item ID
  5306. * @param mixed Extra info
  5307. * @return string HTML form
  5308. */
  5309. function display_item_form($item_type, $title = '', $action = 'add', $id = 0, $extra_info = 'new') {
  5310. global $_course;
  5311. global $charset;
  5312. $tbl_lp_item = Database :: get_course_table(TABLE_LP_ITEM);
  5313. if ($id != 0 && is_array($extra_info)) {
  5314. $item_title = $extra_info['title'];
  5315. $item_description = $extra_info['description'];
  5316. $item_path = api_get_path(WEB_COURSE_PATH) . $_course['path'] . '/scorm/' . $this->path . '/' . stripslashes($extra_info['path']);
  5317. } else {
  5318. $item_title = '';
  5319. $item_description = '';
  5320. }
  5321. $return = ' <div class="row">
  5322. <div class="form_header">';
  5323. if ($id != 0 && is_array($extra_info))
  5324. $parent = $extra_info['parent_item_id'];
  5325. else
  5326. $parent = 0;
  5327. $sql = "
  5328. SELECT *
  5329. FROM " . $tbl_lp_item . "
  5330. WHERE lp_id = " . $this->lp_id . " AND id != " . $id . " ";
  5331. if ($item_type == 'module')
  5332. $sql .= " AND parent_item_id = 0";
  5333. $result = api_sql_query($sql, __FILE__, __LINE__);
  5334. $arrLP = array ();
  5335. while ($row = Database :: fetch_array($result)) {
  5336. $arrLP[] = array (
  5337. 'id' => $row['id'],
  5338. 'item_type' => $row['item_type'],
  5339. 'title' => $row['title'],
  5340. 'path' => $row['path'],
  5341. 'description' => $row['description'],
  5342. 'parent_item_id' => $row['parent_item_id'],
  5343. 'previous_item_id' => $row['previous_item_id'],
  5344. 'next_item_id' => $row['next_item_id'],
  5345. 'max_score' => $row['max_score'],
  5346. 'min_score' => $row['min_score'],
  5347. 'mastery_score' => $row['mastery_score'],
  5348. 'prerequisite' => $row['prerequisite'],
  5349. 'display_order' => $row['display_order']
  5350. );
  5351. }
  5352. $this->tree_array($arrLP);
  5353. $arrLP = $this->arrMenu;
  5354. unset ($this->arrMenu);
  5355. $return .= $title . "\n";
  5356. $return .= ' </div>
  5357. </div>';
  5358. require_once (api_get_path(LIBRARY_PATH) . 'formvalidator/FormValidator.class.php');
  5359. $form = new FormValidator('form', 'POST', api_get_self() . "?" . $_SERVER["QUERY_STRING"]);
  5360. //$defaults["title"] = api_convert_encoding($item_title, $charset, $this->encoding);
  5361. $defaults["title"] = api_html_entity_decode(api_convert_encoding($item_title, $charset, $this->encoding), ENT_QUOTES, $charset);
  5362. $defaults["description"] = api_convert_encoding($item_description, $charset, $this->encoding);
  5363. $form->addElement('html', $return);
  5364. //$arrHide = array($id);
  5365. $arrHide[0]['value'] = Security :: remove_XSS(api_convert_encoding($this->name, api_get_system_encoding(), $_SESSION['oLP']->encoding));
  5366. $arrHide[0]['padding'] = 3;
  5367. $charset = api_get_system_encoding();
  5368. if ($item_type != 'module' && $item_type != 'dokeos_module') {
  5369. for ($i = 0; $i < count($arrLP); $i++) {
  5370. if ($action != 'add') {
  5371. if (($arrLP[$i]['item_type'] == 'dokeos_module' || $arrLP[$i]['item_type'] == 'dokeos_chapter' || $arrLP[$i]['item_type'] == 'dir') && !in_array($arrLP[$i]['id'], $arrHide) && !in_array($arrLP[$i]['parent_item_id'], $arrHide)) {
  5372. $arrHide[$arrLP[$i]['id']]['value'] = api_convert_encoding($arrLP[$i]['title'], $charset, $this->encoding);
  5373. $arrHide[$arrLP[$i]['id']]['padding'] = 3 + $arrLP[$i]['depth'] * 10;
  5374. if ($parent == $arrLP[$i]['id']) {
  5375. $s_selected_parent = $arrHide[$arrLP[$i]['id']];
  5376. }
  5377. }
  5378. } else {
  5379. if ($arrLP[$i]['item_type'] == 'dokeos_module' || $arrLP[$i]['item_type'] == 'dokeos_chapter' || $arrLP[$i]['item_type'] == 'dir') {
  5380. $arrHide[$arrLP[$i]['id']]['value'] = api_convert_encoding($arrLP[$i]['title'], $charset, $this->encoding);
  5381. $arrHide[$arrLP[$i]['id']]['padding'] = 3 + $arrLP[$i]['depth'] * 10;
  5382. if ($parent == $arrLP[$i]['id']) {
  5383. $s_selected_parent = $arrHide[$arrLP[$i]['id']];
  5384. }
  5385. }
  5386. }
  5387. }
  5388. if ($action != 'move') {
  5389. $form->addElement('text', 'title', get_lang('Title'), 'id="idTitle" class="learnpath_chapter_form" size="40%"');
  5390. $form->applyFilter('title', 'html_filter');
  5391. $form->addRule('title', '<div class="required">' . get_lang('ThisFieldIsRequired'), 'required');
  5392. //$form->addElement('textarea','description',get_lang("Description").' :', 'id="idDescription"');
  5393. } else {
  5394. $form->addElement('hidden', 'title');
  5395. }
  5396. $parent_select = & $form->addElement('select', 'parent', get_lang("Parent"), '', 'class="learnpath_chapter_form" style="width:37%;" id="Parent" onchange="load_cbo(this.value);"');
  5397. foreach ($arrHide as $key => $value) {
  5398. $parent_select->addOption($value['value'], $key, 'style="padding-left:' . $value['padding'] . 'px;"');
  5399. }
  5400. $parent_select->setSelected($s_selected_parent);
  5401. }
  5402. if (is_array($arrLP)) {
  5403. reset($arrLP);
  5404. }
  5405. $arrHide = array ();
  5406. //POSITION
  5407. for ($i = 0; $i < count($arrLP); $i++) {
  5408. if ($arrLP[$i]['parent_item_id'] == $parent && $arrLP[$i]['id'] != $id) {
  5409. if ($extra_info['previous_item_id'] == $arrLP[$i]['id'])
  5410. $s_selected_position = $arrLP[$i]['id'];
  5411. elseif ($action == 'add') $s_selected_position = $arrLP[$i]['id'];
  5412. $arrHide[$arrLP[$i]['id']]['value'] = get_lang("After") . ' "' . api_convert_encoding($arrLP[$i]['title'], $charset, $this->encoding);
  5413. }
  5414. }
  5415. $position = & $form->addElement('select', 'previous', get_lang('Position'), '', 'id="idPosition" class="learnpath_chapter_form" style="width:37%;"');
  5416. $position->addOption(get_lang('FirstPosition'), 0, 'style="padding-left:' . $value['padding'] . 'px;"');
  5417. foreach ($arrHide as $key => $value) {
  5418. $position->addOption($value['value'] . '"', $key, 'style="padding-left:' . $value['padding'] . 'px;"');
  5419. }
  5420. if (!empty ($s_selected_position)) {
  5421. $position->setSelected($s_selected_position);
  5422. }
  5423. if (is_array($arrLP)) {
  5424. reset($arrLP);
  5425. }
  5426. $form->addElement('style_submit_button', 'submit_button', get_lang('SaveSection'), 'class="save"');
  5427. if ($item_type == 'module' || $item_type == 'dokeos_module') {
  5428. $form->addElement('hidden', 'parent', '0');
  5429. }
  5430. $extension = pathinfo($item_path, PATHINFO_EXTENSION);
  5431. if (($item_type == 'asset' || $item_type == 'sco') && ($extension == 'html' || $extension == 'htm')) {
  5432. if ($item_type == 'sco') {
  5433. $form->addElement('html', '<script type="text/javascript">alert("' . get_lang('WarningWhenEditingScorm') . '")</script>');
  5434. }
  5435. $renderer = $form->defaultRenderer();
  5436. $renderer->setElementTemplate('<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{label}<br />{element}', 'content_lp');
  5437. $form->addElement('html_editor', 'content_lp', '', null, array('ToolbarSet' => 'LearningPathDocuments', 'Width' => '100%', 'Height' => '400', 'FullPage' => true));
  5438. $defaults["content_lp"] = file_get_contents($item_path);
  5439. }
  5440. $form->addElement('hidden', 'type', 'dokeos_' . $item_type);
  5441. $form->addElement('hidden', 'post_time', time());
  5442. $form->setDefaults($defaults);
  5443. return $form->return_form();
  5444. }
  5445. /**
  5446. * Returns the form to update or create a document
  5447. *
  5448. * @param string Action (add/edit)
  5449. * @param integer ID of the lp_item (if already exists)
  5450. * @param mixed Integer if document ID, string if info ('new')
  5451. * @return string HTML form
  5452. */
  5453. function display_document_form($action = 'add', $id = 0, $extra_info = 'new') {
  5454. global $charset;
  5455. $tbl_lp_item = Database :: get_course_table(TABLE_LP_ITEM);
  5456. $tbl_doc = Database :: get_course_table(TABLE_DOCUMENT);
  5457. $path_parts = pathinfo($extra_info['dir']);
  5458. $no_display_edit_textarea = false;
  5459. //If action==edit document
  5460. //We don't display the document form if it's not an editable document (html or txt file)
  5461. if ($action == "edit") {
  5462. if (is_array($extra_info)) {
  5463. if ($path_parts['extension'] != "txt" && $path_parts['extension'] != "html") {
  5464. $no_display_edit_textarea = true;
  5465. }
  5466. }
  5467. }
  5468. $no_display_add = false;
  5469. //If action==add an existing document
  5470. //We don't display the document form if it's not an editable document (html or txt file)
  5471. if ($action == "add") {
  5472. if (is_numeric($extra_info)) {
  5473. $sql_doc = "SELECT path FROM " . $tbl_doc . "WHERE id = " . Database :: escape_string($extra_info);
  5474. $result = api_sql_query($sql_doc, __FILE__, __LINE__);
  5475. $path_file = Database :: result($result, 0, 0);
  5476. $path_parts = pathinfo($path_file);
  5477. if ($path_parts['extension'] != "txt" && $path_parts['extension'] != "html") {
  5478. $no_display_add = true;
  5479. }
  5480. }
  5481. }
  5482. if ($id != 0 && is_array($extra_info)) {
  5483. $item_title = stripslashes($extra_info['title']);
  5484. $item_description = stripslashes($extra_info['description']);
  5485. $item_terms = stripslashes($extra_info['terms']);
  5486. if (empty ($item_title)) {
  5487. $path_parts = pathinfo($extra_info['path']);
  5488. $item_title = stripslashes($path_parts['filename']);
  5489. }
  5490. }
  5491. elseif (is_numeric($extra_info)) {
  5492. $sql_doc = "SELECT path, title
  5493. FROM " . $tbl_doc . "
  5494. WHERE id = " . Database :: escape_string($extra_info);
  5495. $result = api_sql_query($sql_doc, __FILE__, __LINE__);
  5496. $row = Database :: fetch_array($result);
  5497. $explode = explode('.', $row['title']);
  5498. if (count($explode) > 1) {
  5499. for ($i = 0; $i < count($explode) - 1; $i++)
  5500. $item_title .= $explode[$i];
  5501. } else {
  5502. $item_title = $row['title'];
  5503. }
  5504. $item_title = str_replace('_', ' ', $item_title);
  5505. if (empty ($item_title)) {
  5506. $path_parts = pathinfo($row['path']);
  5507. $item_title = stripslashes($path_parts['filename']);
  5508. }
  5509. } else {
  5510. $item_title = '';
  5511. $item_description = '';
  5512. }
  5513. $return = ' <div class="row">
  5514. <div class="form_header">';
  5515. if ($id != 0 && is_array($extra_info))
  5516. $parent = $extra_info['parent_item_id'];
  5517. else
  5518. $parent = 0;
  5519. $sql = "
  5520. SELECT *
  5521. FROM " . $tbl_lp_item . "
  5522. WHERE
  5523. lp_id = " . $this->lp_id;
  5524. $result = api_sql_query($sql, __FILE__, __LINE__);
  5525. $arrLP = array ();
  5526. while ($row = Database :: fetch_array($result)) {
  5527. $arrLP[] = array (
  5528. 'id' => $row['id'],
  5529. 'item_type' => $row['item_type'],
  5530. 'title' => $row['title'],
  5531. 'path' => $row['path'],
  5532. 'description' => $row['description'],
  5533. 'parent_item_id' => $row['parent_item_id'],
  5534. 'previous_item_id' => $row['previous_item_id'],
  5535. 'next_item_id' => $row['next_item_id'],
  5536. 'display_order' => $row['display_order'],
  5537. 'max_score' => $row['max_score'],
  5538. 'min_score' => $row['min_score'],
  5539. 'mastery_score' => $row['mastery_score'],
  5540. 'prerequisite' => $row['prerequisite']
  5541. );
  5542. }
  5543. $this->tree_array($arrLP);
  5544. $arrLP = $this->arrMenu;
  5545. unset ($this->arrMenu);
  5546. if ($action == 'add') {
  5547. $return .= get_lang("CreateTheDocument") . "\n";
  5548. }
  5549. elseif ($action == 'move') {
  5550. $return .= get_lang("MoveTheCurrentDocument") . "\n";
  5551. } else {
  5552. $return .= get_lang("EditTheCurrentDocument") . "\n";
  5553. }
  5554. $return .= ' </div>
  5555. </div>';
  5556. if (isset ($_GET['edit']) && $_GET['edit'] == 'true') {
  5557. $return .= Display :: return_warning_message('<strong>' . get_lang("Warning") . ' !</strong><br />' . get_lang("WarningEditingDocument"), false);
  5558. }
  5559. require_once (api_get_path(LIBRARY_PATH) . 'formvalidator/FormValidator.class.php');
  5560. $form = new FormValidator('form', 'POST', api_get_self() . "?" . $_SERVER["QUERY_STRING"], '', 'enctype="multipart/form-data"');
  5561. $defaults["title"] = Security :: remove_XSS(api_convert_encoding($item_title, api_get_system_encoding() ,$this->encoding));
  5562. if (empty ($item_title)) {
  5563. $defaults["title"] = Security :: remove_XSS($item_title);
  5564. }
  5565. $defaults["description"] = api_convert_encoding($item_description, $charset, $this->encoding);
  5566. $form->addElement('html', $return);
  5567. if ($action != 'move') {
  5568. $form->addElement('text', 'title', get_lang('Title'), 'id="idTitle" class="learnpath_item_form" size=44%');
  5569. $form->applyFilter('title', 'html_filter');
  5570. }
  5571. //$arrHide = array($id);
  5572. $arrHide[0]['value'] = $this->name;
  5573. $arrHide[0]['padding'] = 3;
  5574. for ($i = 0; $i < count($arrLP); $i++) {
  5575. if ($action != 'add') {
  5576. if (($arrLP[$i]['item_type'] == 'dokeos_module' || $arrLP[$i]['item_type'] == 'dokeos_chapter' || $arrLP[$i]['item_type'] == 'dir') && !in_array($arrLP[$i]['id'], $arrHide) && !in_array($arrLP[$i]['parent_item_id'], $arrHide)) {
  5577. $arrHide[$arrLP[$i]['id']]['value'] = $arrLP[$i]['title'];
  5578. $arrHide[$arrLP[$i]['id']]['padding'] = 3 + $arrLP[$i]['depth'] * 10;
  5579. if ($parent == $arrLP[$i]['id']) {
  5580. $s_selected_parent = $arrHide[$arrLP[$i]['id']];
  5581. }
  5582. }
  5583. } else {
  5584. if ($arrLP[$i]['item_type'] == 'dokeos_module' || $arrLP[$i]['item_type'] == 'dokeos_chapter' || $arrLP[$i]['item_type'] == 'dir') {
  5585. $arrHide[$arrLP[$i]['id']]['value'] = $arrLP[$i]['title'];
  5586. $arrHide[$arrLP[$i]['id']]['padding'] = 3 + $arrLP[$i]['depth'] * 10;
  5587. if ($parent == $arrLP[$i]['id']) {
  5588. $s_selected_parent = $arrHide[$arrLP[$i]['id']];
  5589. }
  5590. }
  5591. }
  5592. }
  5593. $parent_select = & $form->addElement('select', 'parent', get_lang('Parent'), '', 'class="learnpath_item_form" style="width:40%;" onchange="load_cbo(this.value);"');
  5594. $my_count=0;
  5595. foreach ($arrHide as $key => $value) {
  5596. if ($my_count!=0) {
  5597. // the LP name is also the first section and is not in the same charset like the other sections
  5598. $value['value'] = Security :: remove_XSS(api_convert_encoding($value['value'], api_get_system_encoding(), $this->encoding));
  5599. $parent_select->addOption($value['value'], $key, 'style="padding-left:' . $value['padding'] . 'px;"');
  5600. } else {
  5601. $value['value'] = Security :: remove_XSS($value['value']);
  5602. $parent_select->addOption($value['value'], $key, 'style="padding-left:' . $value['padding'] . 'px;"');
  5603. }
  5604. $my_count++;
  5605. }
  5606. if (!empty ($id)) {
  5607. $parent_select->setSelected($parent);
  5608. } else {
  5609. $parent_item_id = $_SESSION['parent_item_id'];
  5610. $parent_select->setSelected($parent_item_id);
  5611. }
  5612. if (is_array($arrLP)) {
  5613. reset($arrLP);
  5614. }
  5615. $arrHide = array ();
  5616. //POSITION
  5617. for ($i = 0; $i < count($arrLP); $i++) {
  5618. if ($arrLP[$i]['parent_item_id'] == $parent && $arrLP[$i]['id'] != $id) {
  5619. if ($extra_info['previous_item_id'] == $arrLP[$i]['id'])
  5620. $s_selected_position = $arrLP[$i]['id'];
  5621. elseif ($action == 'add') $s_selected_position = $arrLP[$i]['id'];
  5622. $arrHide[$arrLP[$i]['id']]['value'] = get_lang("After") . ' "' . api_convert_encoding($arrLP[$i]['title'], $charset, $this->encoding) . '"';
  5623. }
  5624. }
  5625. $position = & $form->addElement('select', 'previous', get_lang('Position'), '', 'id="idPosition" class="learnpath_item_form" style="width:40%;"');
  5626. $position->addOption(get_lang("FirstPosition"), 0);
  5627. foreach ($arrHide as $key => $value) {
  5628. $position->addOption($value['value'], $key, 'style="padding-left:' . $value['padding'] . 'px;"');
  5629. }
  5630. $position->setSelected($s_selected_position);
  5631. if (is_array($arrLP)) {
  5632. reset($arrLP);
  5633. }
  5634. if ($action != 'move') {
  5635. $id_prerequisite = 0;
  5636. if (is_array($arrLP)) {
  5637. foreach ($arrLP as $key => $value) {
  5638. if ($value['id'] == $id) {
  5639. $id_prerequisite = $value['prerequisite'];
  5640. break;
  5641. }
  5642. }
  5643. }
  5644. //comented the prerequisites, only visible in edit (new document)
  5645. //$select_prerequisites=$form->addElement('select', 'prerequisites', get_lang('Prerequisites'),null,'id="prerequisites" class="learnpath_item_form" style="width:263px;"');
  5646. //$select_prerequisites->addOption(get_lang("NoPrerequisites"),0);
  5647. // form element for uploading an mp3 file
  5648. //$form->addElement('file','mp3',get_lang('UploadMp3audio'),'id="mp3" size="33"');
  5649. //$form->addRule('file', 'The extension of the Song file should be *.mp3', 'filename', '/^.*\.mp3$/');
  5650. /* Code deprecated - moved to lp level (not lp-item)
  5651. if ( api_get_setting('search_enabled') === 'true' )
  5652. {
  5653. //add terms field
  5654. $terms = $form->addElement('text','terms', get_lang('SearchFeatureTerms').'&nbsp;:','id="idTerms" class="learnpath_item_form"');
  5655. $terms->setValue($item_terms);
  5656. }
  5657. */
  5658. $arrHide = array ();
  5659. for ($i = 0; $i < count($arrLP); $i++) {
  5660. if ($arrLP[$i]['id'] != $id && $arrLP[$i]['item_type'] != 'dokeos_chapter') {
  5661. if ($extra_info['previous_item_id'] == $arrLP[$i]['id'])
  5662. $s_selected_position = $arrLP[$i]['id'];
  5663. elseif ($action == 'add') $s_selected_position = $arrLP[$i]['id'];
  5664. $arrHide[$arrLP[$i]['id']]['value'] = api_convert_encoding($arrLP[$i]['title'], $charset, $this->encoding);
  5665. }
  5666. }
  5667. /* foreach($arrHide as $key => $value){
  5668. $select_prerequisites->addOption($value['value'],$key,'style="padding-left:'.$value['padding'].'px;"');
  5669. if($key==$s_selected_position && $action == 'add'){
  5670. $select_prerequisites -> setSelected(0);
  5671. }
  5672. elseif($key==$id_prerequisite && $action == 'edit'){
  5673. $select_prerequisites -> setSelected($id_prerequisite);
  5674. }
  5675. }
  5676. */
  5677. if (!$no_display_add) {
  5678. if (($extra_info == 'new' || $extra_info['item_type'] == TOOL_DOCUMENT || $_GET['edit'] == 'true')) {
  5679. if (isset ($_POST['content']))
  5680. $content = stripslashes($_POST['content']);
  5681. elseif (is_array($extra_info)) {
  5682. //If it's an html document or a text file
  5683. if (!$no_display_edit_textarea) {
  5684. $content = $this->display_document($extra_info['path'], false, false);
  5685. }
  5686. }
  5687. elseif (is_numeric($extra_info)) $content = $this->display_document($extra_info, false, false);
  5688. else
  5689. $content = '';
  5690. if (!$no_display_edit_textarea) {
  5691. // We need to claculate here some specific settings for the online editor.
  5692. // The calculated settings work for documents in the Documents tool
  5693. // (on the root or in subfolders).
  5694. // For documents in native scorm packages it is unclear whether the
  5695. // online editor should be activated or not.
  5696. $relative_path = $extra_info['dir'];
  5697. if ($relative_path == 'n/') {
  5698. // A new document, it is in the root of the repository.
  5699. $relative_path = '';
  5700. $relative_prefix = '';
  5701. } else {
  5702. // The document already exists. Whe have to determine its relative path towards the repository root.
  5703. $relative_path = explode('/', $relative_path);
  5704. $cnt = count($relative_path) - 2;
  5705. if ($cnt < 0) {
  5706. $cnt = 0;
  5707. }
  5708. $relative_prefix = str_repeat('../', $cnt);
  5709. $relative_path = array_slice($relative_path, 1, $cnt);
  5710. $relative_path = implode('/', $relative_path);
  5711. if (strlen($relative_path) > 0) {
  5712. $relative_path = $relative_path . '/';
  5713. }
  5714. }
  5715. $editor_config = array('ToolbarSet' => 'LearningPathDocuments', 'Width' => '100%', 'Height' => '700', 'FullPage' => true,
  5716. 'CreateDocumentDir' => $relative_prefix,
  5717. 'CreateDocumentWebDir' => api_get_path('WEB_COURSE_PATH') . api_get_course_path() . '/document/',
  5718. 'BaseHref' => api_get_path('WEB_COURSE_PATH') . api_get_course_path() . '/document/' . $relative_path
  5719. );
  5720. if ($_GET['action'] == 'add_item') {
  5721. $class = 'add';
  5722. $text = get_lang('LPCreateDocument');
  5723. } else
  5724. if ($_GET['action'] == 'edit_item') {
  5725. $class = 'save';
  5726. $text = get_lang('SaveDocument');
  5727. }
  5728. $form->addElement('style_submit_button', 'submit_button', $text, 'class="' . $class . '"');
  5729. $renderer = $form->defaultRenderer();
  5730. $renderer->setElementTemplate('<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{label}<br />{element}', 'content_lp');
  5731. $form->addElement('html', '<div style="margin:3px 12px">');
  5732. $form->addElement('html_editor', 'content_lp', '', null, $editor_config);
  5733. $form->addElement('html', '</div>');
  5734. $defaults["content_lp"] = $content;
  5735. }
  5736. }
  5737. elseif (is_numeric($extra_info)) {
  5738. $form->addElement('style_submit_button', 'submit_button', get_lang('SaveDocument'), 'class="save"');
  5739. $return = $this->display_document($extra_info, true, true, true);
  5740. $form->addElement('html', $return);
  5741. }
  5742. }
  5743. }
  5744. if ($action == 'move') {
  5745. $form->addElement('hidden', 'title', $item_title);
  5746. $form->addElement('hidden', 'description', $item_description);
  5747. }
  5748. if (is_numeric($extra_info)) {
  5749. $form->addElement('style_submit_button', 'submit_button', get_lang('SaveDocument'), 'value="submit_button", class="save"');
  5750. $form->addElement('hidden', 'path', $extra_info);
  5751. }
  5752. elseif (is_array($extra_info)) {
  5753. $form->addElement('style_submit_button', 'submit_button', get_lang('SaveDocument'), 'class="save"');
  5754. $form->addElement('hidden', 'path', $extra_info['path']);
  5755. }
  5756. $form->addElement('hidden', 'type', TOOL_DOCUMENT);
  5757. $form->addElement('hidden', 'post_time', time());
  5758. $form->setDefaults($defaults);
  5759. return $form->return_form();
  5760. }
  5761. /**
  5762. * Return HTML form to add/edit a link item
  5763. *
  5764. * @param string Action (add/edit)
  5765. * @param integer Item ID if exists
  5766. * @param mixed Extra info
  5767. * @return string HTML form
  5768. */
  5769. function display_link_form($action = 'add', $id = 0, $extra_info = '') {
  5770. global $charset;
  5771. $tbl_lp_item = Database :: get_course_table(TABLE_LP_ITEM);
  5772. $tbl_link = Database :: get_course_table(TABLE_LINK);
  5773. if ($id != 0 && is_array($extra_info)) {
  5774. $item_title = stripslashes($extra_info['title']);
  5775. $item_description = stripslashes($extra_info['description']);
  5776. $item_url = stripslashes($extra_info['url']);
  5777. }
  5778. elseif (is_numeric($extra_info)) {
  5779. $sql_link = "
  5780. SELECT
  5781. title,
  5782. description,
  5783. url
  5784. FROM " . $tbl_link . "
  5785. WHERE id = " . $extra_info;
  5786. $result = api_sql_query($sql_link, __FILE__, __LINE__);
  5787. $row = Database :: fetch_array($result);
  5788. $item_title = $row['title'];
  5789. $item_description = $row['description'];
  5790. $item_url = $row['url'];
  5791. } else {
  5792. $item_title = '';
  5793. $item_description = '';
  5794. $item_url = '';
  5795. }
  5796. $item_title = api_convert_encoding($item_title, $charset, $this->encoding);
  5797. $item_description = api_convert_encoding($item_description, $charset, $this->encoding);
  5798. $return = ' <div class="row">
  5799. <div class="form_header">';
  5800. if ($id != 0 && is_array($extra_info))
  5801. $parent = $extra_info['parent_item_id'];
  5802. else
  5803. $parent = 0;
  5804. $sql = "
  5805. SELECT *
  5806. FROM " . $tbl_lp_item . "
  5807. WHERE
  5808. lp_id = " . $this->lp_id;
  5809. $result = api_sql_query($sql, __FILE__, __LINE__);
  5810. $arrLP = array ();
  5811. while ($row = Database :: fetch_array($result)) {
  5812. $arrLP[] = array (
  5813. 'id' => $row['id'],
  5814. 'item_type' => $row['item_type'],
  5815. 'title' => $row['title'],
  5816. 'path' => $row['path'],
  5817. 'description' => $row['description'],
  5818. 'parent_item_id' => $row['parent_item_id'],
  5819. 'previous_item_id' => $row['previous_item_id'],
  5820. 'next_item_id' => $row['next_item_id'],
  5821. 'display_order' => $row['display_order'],
  5822. 'max_score' => $row['max_score'],
  5823. 'min_score' => $row['min_score'],
  5824. 'mastery_score' => $row['mastery_score'],
  5825. 'prerequisite' => $row['prerequisite']
  5826. );
  5827. }
  5828. $this->tree_array($arrLP);
  5829. $arrLP = $this->arrMenu;
  5830. unset ($this->arrMenu);
  5831. if ($action == 'add')
  5832. $return .= get_lang("CreateTheLink") . '&nbsp;:' . "\n";
  5833. elseif ($action == 'move') $return .= get_lang("MoveCurrentLink") . '&nbsp;:' . "\n";
  5834. else
  5835. $return .= get_lang("EditCurrentLink") . '&nbsp;:' . "\n";
  5836. $return .= ' </div>
  5837. </div>';
  5838. $return .= '<div class="sectioncomment">';
  5839. $return .= '<form method="POST">' . "\n";
  5840. $return .= "\t" . '<table>' . "\n";
  5841. if ($action != 'move') {
  5842. $return .= "\t\t" . '<tr>' . "\n";
  5843. $return .= "\t\t\t" . '<td class="label"><label for="idTitle">' . get_lang('Title') . '</label></td>' . "\n";
  5844. $return .= "\t\t\t" . '<td class="input"><input id="idTitle" name="title" size="44" type="text" value="' . $item_title . '" class="learnpath_item_form"/></td>' . "\n";
  5845. $return .= "\t\t" . '</tr>' . "\n";
  5846. }
  5847. $return .= "\t\t" . '<tr>' . "\n";
  5848. $return .= "\t\t\t" . '<td class="label"><label for="idParent">' . get_lang('Parent') . '</label></td>' . "\n";
  5849. $return .= "\t\t\t" . '<td class="input">' . "\n";
  5850. $return .= "\t\t\t\t" . '<select id="idParent" style="width:100%;" name="parent" onchange="load_cbo(this.value);" class="learnpath_item_form" size="1">';
  5851. $return .= "\t\t\t\t\t" . '<option class="top" value="0">' . $this->name . '</option>';
  5852. $arrHide = array (
  5853. $id
  5854. );
  5855. $parent_item_id = $_SESSION['parent_item_id'];
  5856. for ($i = 0; $i < count($arrLP); $i++) {
  5857. if ($action != 'add') {
  5858. if (($arrLP[$i]['item_type'] == 'dokeos_module' || $arrLP[$i]['item_type'] == 'dokeos_chapter' || $arrLP[$i]['item_type'] == 'dir') && !in_array($arrLP[$i]['id'], $arrHide) && !in_array($arrLP[$i]['parent_item_id'], $arrHide)) {
  5859. $return .= "\t\t\t\t\t" . '<option ' . (($parent == $arrLP[$i]['id']) ? 'selected="selected" ' : '') . 'style="padding-left:' . ($arrLP[$i]['depth'] * 10) . 'px;" value="' . $arrLP[$i]['id'] . '">' . api_convert_encoding($arrLP[$i]['title'], $charset, $this->encoding) . '</option>';
  5860. } else {
  5861. $arrHide[] = $arrLP[$i]['id'];
  5862. }
  5863. } else {
  5864. if ($arrLP[$i]['item_type'] == 'dokeos_module' || $arrLP[$i]['item_type'] == 'dokeos_chapter' || $arrLP[$i]['item_type'] == 'dir')
  5865. $return .= "\t\t\t\t\t" . '<option ' . (($parent_item_id == $arrLP[$i]['id']) ? 'selected="selected" ' : '') . 'style="padding-left:' . ($arrLP[$i]['depth'] * 10) . 'px;" value="' . $arrLP[$i]['id'] . '">' . api_convert_encoding($arrLP[$i]['title'], $charset, $this->encoding) . '</option>';
  5866. }
  5867. }
  5868. if (is_array($arrLP)) {
  5869. reset($arrLP);
  5870. }
  5871. $return .= "\t\t\t\t" . '</select>';
  5872. $return .= "\t\t\t" . '</td>' . "\n";
  5873. $return .= "\t\t" . '</tr>' . "\n";
  5874. $return .= "\t\t" . '<tr>' . "\n";
  5875. $return .= "\t\t\t" . '<td class="label"><label for="idPosition">' . get_lang('Position') . '</label></td>' . "\n";
  5876. $return .= "\t\t\t" . '<td class="input">' . "\n";
  5877. $return .= "\t\t\t\t" . '<select id="idPosition" name="previous" style="width:100%;" size="1" class="learnpath_item_form">';
  5878. $return .= "\t\t\t\t\t" . '<option class="top" value="0">' . get_lang("FirstPosition") . '</option>';
  5879. for ($i = 0; $i < count($arrLP); $i++) {
  5880. if ($arrLP[$i]['parent_item_id'] == $parent && $arrLP[$i]['id'] != $id) {
  5881. if ($extra_info['previous_item_id'] == $arrLP[$i]['id'])
  5882. $selected = 'selected="selected" ';
  5883. elseif ($action == 'add') $selected = 'selected="selected" ';
  5884. else
  5885. $selected = '';
  5886. $return .= "\t\t\t\t\t" . '<option ' . $selected . 'value="' . $arrLP[$i]['id'] . '">' . get_lang("After") . ' "' . api_convert_encoding($arrLP[$i]['title'], $charset, $this->encoding) . '"</option>';
  5887. }
  5888. }
  5889. $return .= "\t\t\t\t" . '</select>';
  5890. $return .= "\t\t\t" . '</td>' . "\n";
  5891. $return .= "\t\t" . '</tr>' . "\n";
  5892. if ($action != 'move') {
  5893. //Description filed is commentd for link
  5894. /*$return .= "\t\t" . '<tr>' . "\n";
  5895. $return .= "\t\t\t" . '<td class="label"><label for="idDescription">'.get_lang('Description').'</label></td>' . "\n";
  5896. $return .= "\t\t\t" . '<td class="input"><textarea id="idDescription" style="width:100%;" name="description" class="learnpath_item_form" rows="4">' . $item_description . '</textarea></td>' . "\n";
  5897. $return .= "\t\t" . '</tr>' . "\n";*/
  5898. $return .= "\t\t" . '<tr>' . "\n";
  5899. $return .= "\t\t\t" . '<td class="label"><label for="idURL">' . get_lang('Url') . '</label></td>' . "\n";
  5900. $return .= "\t\t\t" . '<td class="input"><input' . (is_numeric($extra_info) ? ' disabled="disabled"' : '') . ' id="idURL" name="url" style="width:99%;" type="text" value="' . $item_url . '" class="learnpath_item_form" /></td>' . "\n";
  5901. $return .= "\t\t" . '</tr>' . "\n";
  5902. $id_prerequisite = 0;
  5903. if (is_array($arrLP)) {
  5904. foreach ($arrLP as $key => $value) {
  5905. if ($value['id'] == $id) {
  5906. $id_prerequisite = $value['prerequisite'];
  5907. break;
  5908. }
  5909. }
  5910. }
  5911. $arrHide = array ();
  5912. for ($i = 0; $i < count($arrLP); $i++) {
  5913. if ($arrLP[$i]['id'] != $id && $arrLP[$i]['item_type'] != 'dokeos_chapter') {
  5914. if ($extra_info['previous_item_id'] == $arrLP[$i]['id'])
  5915. $s_selected_position = $arrLP[$i]['id'];
  5916. elseif ($action == 'add') $s_selected_position = 0;
  5917. $arrHide[$arrLP[$i]['id']]['value'] = api_convert_encoding($arrLP[$i]['title'], $charset, $this->encoding);
  5918. }
  5919. }
  5920. /*//comented the prerequisites, only visible in edit (link)
  5921. $return .= "\t\t" . '<tr>' . "\n";
  5922. $return .= "\t\t\t" . '<td class="label"><label for="idPrerequisites">'.get_lang('Prerequisites').'</label></td>' . "\n";
  5923. $return .= "\t\t\t" . '<td class="input"><select name="prerequisites" id="prerequisites" class="learnpath_item_form"><option value="0">'.get_lang("NoPrerequisites").'</option>';
  5924. foreach($arrHide as $key => $value)
  5925. {
  5926. if($key==$s_selected_position && $action == 'add')
  5927. {
  5928. $return .= '<option value="'.$key.'" selected="selected">'.$value['value'].'</option>';
  5929. }
  5930. elseif($key==$id_prerequisite && $action == 'edit'){
  5931. $return .= '<option value="'.$key.'" selected="selected">'.$value['value'].'</option>';
  5932. }
  5933. else{
  5934. $return .= '<option value="'.$key.'">'.$value['value'].'</option>';
  5935. }
  5936. }
  5937. $return .= "</select></td>";
  5938. */
  5939. $return .= "\t\t" . '</tr>' . "\n";
  5940. }
  5941. $return .= "\t\t" . '<tr>' . "\n";
  5942. $return .= "\t\t\t" . '<td>&nbsp;</td><td><button class="save" name="submit_button" type="submit">' . get_lang("AddLinkToCourse") . '</button></td>' . "\n";
  5943. $return .= "\t\t" . '</tr>' . "\n";
  5944. $return .= "\t" . '</table>' . "\n";
  5945. if ($action == 'move') {
  5946. $return .= "\t" . '<input name="title" type="hidden" value="' . $item_title . '" />' . "\n";
  5947. $return .= "\t" . '<input name="description" type="hidden" value="' . $item_description . '" />' . "\n";
  5948. }
  5949. if (is_numeric($extra_info)) {
  5950. $return .= "\t" . '<input name="path" type="hidden" value="' . $extra_info . '" />' . "\n";
  5951. }
  5952. elseif (is_array($extra_info)) {
  5953. $return .= "\t" . '<input name="path" type="hidden" value="' . $extra_info['path'] . '" />' . "\n";
  5954. }
  5955. $return .= "\t" . '<input name="type" type="hidden" value="' . TOOL_LINK . '" />' . "\n";
  5956. $return .= "\t" . '<input name="post_time" type="hidden" value="' . time() . '" />' . "\n";
  5957. $return .= '</form>' . "\n";
  5958. $return .= '</div>' . "\n";
  5959. return $return;
  5960. }
  5961. /**
  5962. * Return HTML form to add/edit a student publication (work)
  5963. *
  5964. * @param string Action (add/edit)
  5965. * @param integer Item ID if already exists
  5966. * @param mixed Extra info (work ID if integer)
  5967. * @return string HTML form
  5968. */
  5969. function display_student_publication_form($action = 'add', $id = 0, $extra_info = '') {
  5970. global $charset;
  5971. $tbl_lp_item = Database :: get_course_table(TABLE_LP_ITEM);
  5972. $tbl_publication = Database :: get_course_table(TABLE_STUDENT_PUBLICATION);
  5973. if ($id != 0 && is_array($extra_info)) {
  5974. $item_title = stripslashes($extra_info['title']);
  5975. $item_description = stripslashes($extra_info['description']);
  5976. }
  5977. elseif (is_numeric($extra_info)) {
  5978. $sql_publication = "
  5979. SELECT
  5980. title,
  5981. description
  5982. FROM " . $tbl_publication . "
  5983. WHERE id = " . $extra_info;
  5984. $result = api_sql_query($sql_publication, __FILE__, __LINE__);
  5985. $row = Database :: fetch_array($result);
  5986. $item_title = $row['title'];
  5987. } else {
  5988. $item_title = get_lang('Student_publication');
  5989. }
  5990. $item_title = api_convert_encoding($item_title, $charset, $this->encoding);
  5991. $return = ' <div class="row">
  5992. <div class="form_header">';
  5993. if ($id != 0 && is_array($extra_info))
  5994. $parent = $extra_info['parent_item_id'];
  5995. else
  5996. $parent = 0;
  5997. $sql = "
  5998. SELECT *
  5999. FROM " . $tbl_lp_item . "
  6000. WHERE
  6001. lp_id = " . $this->lp_id;
  6002. $result = api_sql_query($sql, __FILE__, __LINE__);
  6003. $arrLP = array ();
  6004. while ($row = Database :: fetch_array($result)) {
  6005. $arrLP[] = array (
  6006. 'id' => $row['id'],
  6007. 'item_type' => $row['item_type'],
  6008. 'title' => $row['title'],
  6009. 'path' => $row['path'],
  6010. 'description' => $row['description'],
  6011. 'parent_item_id' => $row['parent_item_id'],
  6012. 'previous_item_id' => $row['previous_item_id'],
  6013. 'next_item_id' => $row['next_item_id'],
  6014. 'display_order' => $row['display_order'],
  6015. 'max_score' => $row['max_score'],
  6016. 'min_score' => $row['min_score'],
  6017. 'mastery_score' => $row['mastery_score'],
  6018. 'prerequisite' => $row['prerequisite']
  6019. );
  6020. }
  6021. $this->tree_array($arrLP);
  6022. $arrLP = $this->arrMenu;
  6023. unset ($this->arrMenu);
  6024. if ($action == 'add')
  6025. $return .= get_lang("Student_publication") . '&nbsp;:' . "\n";
  6026. elseif ($action == 'move') $return .= get_lang("MoveCurrentStudentPublication") . '&nbsp;:' . "\n";
  6027. else
  6028. $return .= get_lang("EditCurrentStudentPublication") . '&nbsp;:' . "\n";
  6029. $return .= ' </div>
  6030. </div>';
  6031. $return .= '<div class="sectioncomment">';
  6032. $return .= '<form method="POST">' . "\n";
  6033. $return .= "\t" . '<table class="lp_form">' . "\n";
  6034. if ($action != 'move') {
  6035. $return .= "\t\t" . '<tr>' . "\n";
  6036. $return .= "\t\t\t" . '<td class="label"><label for="idTitle">' . get_lang('Title') . '</label></td>' . "\n";
  6037. $return .= "\t\t\t" . '<td class="input"><input id="idTitle" name="title" size="44" type="text" value="' . $item_title . '" class="learnpath_item_form" /></td>' . "\n";
  6038. $return .= "\t\t" . '</tr>' . "\n";
  6039. }
  6040. $return .= "\t\t" . '<tr>' . "\n";
  6041. $return .= "\t\t\t" . '<td class="label"><label for="idParent">' . get_lang('Parent') . '</label></td>' . "\n";
  6042. $return .= "\t\t\t" . '<td class="input">' . "\n";
  6043. $return .= "\t\t\t\t" . '<select id="idParent" name="parent" style="width:100%;" onchange="load_cbo(this.value);" class="learnpath_item_form" size="1">';
  6044. $parent_item_id = $_SESSION['parent_item_id'];
  6045. $return .= "\t\t\t\t\t" . '<option class="top" value="0">' . $this->name . '</option>';
  6046. $arrHide = array (
  6047. $id
  6048. );
  6049. for ($i = 0; $i < count($arrLP); $i++) {
  6050. if ($action != 'add') {
  6051. if (($arrLP[$i]['item_type'] == 'dokeos_module' || $arrLP[$i]['item_type'] == 'dokeos_chapter' || $arrLP[$i]['item_type'] == 'dir') && !in_array($arrLP[$i]['id'], $arrHide) && !in_array($arrLP[$i]['parent_item_id'], $arrHide)) {
  6052. $return .= "\t\t\t\t\t" . '<option ' . (($parent == $arrLP[$i]['id']) ? 'selected="selected" ' : '') . 'style="padding-left:' . ($arrLP[$i]['depth'] * 10) . 'px;" value="' . $arrLP[$i]['id'] . '">' . api_convert_encoding($arrLP[$i]['title'], $charset, $this->encoding) . '</option>';
  6053. } else {
  6054. $arrHide[] = $arrLP[$i]['id'];
  6055. }
  6056. } else {
  6057. if ($arrLP[$i]['item_type'] == 'dokeos_module' || $arrLP[$i]['item_type'] == 'dokeos_chapter' || $arrLP[$i]['item_type'] == 'dir')
  6058. $return .= "\t\t\t\t\t" . '<option ' . (($parent_item_id == $arrLP[$i]['id']) ? 'selected="selected" ' : '') . 'style="padding-left:' . ($arrLP[$i]['depth'] * 10) . 'px;" value="' . $arrLP[$i]['id'] . '">' . api_convert_encoding($arrLP[$i]['title'], $charset, $this->encoding) . '</option>';
  6059. }
  6060. }
  6061. if (is_array($arrLP)) {
  6062. reset($arrLP);
  6063. }
  6064. $return .= "\t\t\t\t" . '</select>';
  6065. $return .= "\t\t\t" . '</td>' . "\n";
  6066. $return .= "\t\t" . '</tr>' . "\n";
  6067. $return .= "\t\t" . '<tr>' . "\n";
  6068. $return .= "\t\t\t" . '<td class="label"><label for="idPosition">' . get_lang('Position') . '</label></td>' . "\n";
  6069. $return .= "\t\t\t" . '<td class="input">' . "\n";
  6070. $return .= "\t\t\t\t" . '<select id="idPosition" name="previous" style="width:100%;" size="1" class="learnpath_item_form">';
  6071. $return .= "\t\t\t\t\t" . '<option class="top" value="0">' . get_lang("FirstPosition") . '</option>';
  6072. for ($i = 0; $i < count($arrLP); $i++) {
  6073. if ($arrLP[$i]['parent_item_id'] == $parent && $arrLP[$i]['id'] != $id) {
  6074. if ($extra_info['previous_item_id'] == $arrLP[$i]['id'])
  6075. $selected = 'selected="selected" ';
  6076. elseif ($action == 'add') $selected = 'selected="selected" ';
  6077. else
  6078. $selected = '';
  6079. $return .= "\t\t\t\t\t" . '<option ' . $selected . 'value="' . $arrLP[$i]['id'] . '">' . get_lang("After") . ' "' . api_convert_encoding($arrLP[$i]['title'], $charset, $this->encoding) . '"</option>';
  6080. }
  6081. }
  6082. $return .= "\t\t\t\t" . '</select>';
  6083. $return .= "\t\t\t" . '</td>' . "\n";
  6084. $return .= "\t\t" . '</tr>' . "\n";
  6085. if ($action != 'move') {
  6086. $id_prerequisite = 0;
  6087. if (is_array($arrLP)) {
  6088. foreach ($arrLP as $key => $value) {
  6089. if ($value['id'] == $id) {
  6090. $id_prerequisite = $value['prerequisite'];
  6091. break;
  6092. }
  6093. }
  6094. }
  6095. $arrHide = array ();
  6096. for ($i = 0; $i < count($arrLP); $i++) {
  6097. if ($arrLP[$i]['id'] != $id && $arrLP[$i]['item_type'] != 'dokeos_chapter') {
  6098. if ($extra_info['previous_item_id'] == $arrLP[$i]['id'])
  6099. $s_selected_position = $arrLP[$i]['id'];
  6100. elseif ($action == 'add') $s_selected_position = 0;
  6101. $arrHide[$arrLP[$i]['id']]['value'] = api_convert_encoding($arrLP[$i]['title'], $charset, $this->encoding);
  6102. }
  6103. }
  6104. //comented the prerequisites, only visible in edit (work)
  6105. /*
  6106. $return .= "\t\t" . '<tr>' . "\n";
  6107. $return .= "\t\t\t" . '<td class="label"><label for="idPrerequisites">'.get_lang('Prerequisites').'</label></td>' . "\n";
  6108. $return .= "\t\t\t" . '<td class="input"><select name="prerequisites" id="prerequisites" class="learnpath_item_form"><option value="0">'.get_lang("NoPrerequisites").'</option>';
  6109. foreach($arrHide as $key => $value){
  6110. if($key==$s_selected_position && $action == 'add'){
  6111. $return .= '<option value="'.$key.'" selected="selected">'.$value['value'].'</option>';
  6112. }
  6113. elseif($key==$id_prerequisite && $action == 'edit'){
  6114. $return .= '<option value="'.$key.'" selected="selected">'.$value['value'].'</option>';
  6115. }
  6116. else{
  6117. $return .= '<option value="'.$key.'">'.$value['value'].'</option>';
  6118. }
  6119. }
  6120. $return .= "</select></td>";
  6121. */
  6122. $return .= "\t\t" . '</tr>' . "\n";
  6123. }
  6124. $return .= "\t\t" . '<tr>' . "\n";
  6125. $return .= "\t\t\t" . '<td>&nbsp</td><td><button class="save" name="submit_button" type="submit">' . get_lang("AddAssignmentToCourse") . '</button></td>' . "\n";
  6126. $return .= "\t\t" . '</tr>' . "\n";
  6127. $return .= "\t" . '</table>' . "\n";
  6128. if ($action == 'move') {
  6129. $return .= "\t" . '<input name="title" type="hidden" value="' . $item_title . '" />' . "\n";
  6130. $return .= "\t" . '<input name="description" type="hidden" value="' . $item_description . '" />' . "\n";
  6131. }
  6132. if (is_numeric($extra_info)) {
  6133. $return .= "\t" . '<input name="path" type="hidden" value="' . $extra_info . '" />' . "\n";
  6134. }
  6135. elseif (is_array($extra_info)) {
  6136. $return .= "\t" . '<input name="path" type="hidden" value="' . $extra_info['path'] . '" />' . "\n";
  6137. }
  6138. $return .= "\t" . '<input name="type" type="hidden" value="' . TOOL_STUDENTPUBLICATION . '" />' . "\n";
  6139. $return .= "\t" . '<input name="post_time" type="hidden" value="' . time() . '" />' . "\n";
  6140. $return .= '</form>' . "\n";
  6141. $return .= '</div>' . "\n";
  6142. return $return;
  6143. }
  6144. /**
  6145. * Displays the menu for manipulating a step
  6146. *
  6147. * @return unknown
  6148. */
  6149. function display_manipulate($item_id, $item_type = TOOL_DOCUMENT) {
  6150. global $charset, $_course;
  6151. $return = '<div class="actions">';
  6152. switch ($item_type) {
  6153. case 'dokeos_chapter' :
  6154. case 'chapter' :
  6155. //comented the message cause should not show it
  6156. //$lang = get_lang('TitleManipulateChapter');
  6157. break;
  6158. case 'dokeos_module' :
  6159. case 'module' :
  6160. //comented the message cause should not show it
  6161. //$lang = get_lang('TitleManipulateModule');
  6162. break;
  6163. case TOOL_DOCUMENT :
  6164. //comented the message cause should not show it
  6165. //$lang = get_lang('TitleManipulateDocument');
  6166. break;
  6167. case TOOL_LINK :
  6168. case 'link' :
  6169. //comented the message cause should not show it
  6170. //$lang = get_lang('TitleManipulateLink');
  6171. break;
  6172. case TOOL_QUIZ :
  6173. //comented the message cause should not show it
  6174. //$lang = get_lang('TitleManipulateQuiz');
  6175. break;
  6176. case TOOL_STUDENTPUBLICATION :
  6177. //comented the message cause should not show it
  6178. //$lang = get_lang('TitleManipulateStudentPublication');
  6179. break;
  6180. }
  6181. $tbl_lp_item = Database :: get_course_table(TABLE_LP_ITEM);
  6182. $sql = "
  6183. SELECT
  6184. *
  6185. FROM " . $tbl_lp_item . " as lp
  6186. WHERE
  6187. lp.id = " . $item_id;
  6188. $result = api_sql_query($sql, __FILE__, __LINE__);
  6189. $row = mysql_fetch_assoc($result);
  6190. $s_title = $row['title'];
  6191. $s_title = api_convert_encoding($s_title, $charset, $this->encoding);
  6192. // we display an audio player if needed
  6193. if (!empty ($row['audio'])) {
  6194. $return .= '<div class="lp_mediaplayer" id="container"><a href="http://www.macromedia.com/go/getflashplayer">Get the Flash Player</a> to see this player.</div>';
  6195. $return .= '<script type="text/javascript" src="../inc/lib/mediaplayer/swfobject.js"></script>';
  6196. $return .= '<script type="text/javascript">
  6197. var s1 = new SWFObject("../inc/lib/mediaplayer/player.swf","ply","250","20","9","#FFFFFF");
  6198. s1.addParam("allowscriptaccess","always");
  6199. s1.addParam("flashvars","file=../../courses/' . $_course['path'] . '/document/audio/' . $row['audio'] . '&autostart=true");
  6200. s1.write("container");
  6201. </script>';
  6202. }
  6203. //commented ":" for message in step
  6204. //$return .= $lang.': ';
  6205. $return .= '<a href="' . api_get_self() . '?cidReq=' . Security :: remove_XSS($_GET['cidReq']) . '&amp;action=edit_item&amp;view=build&amp;id=' . $item_id . '&amp;lp_id=' . $this->lp_id . '&path_item=' . $row['path'] . '" title="' . get_lang('Edit') . '"><img align="absbottom" alt="Edit the current item" src="../img/edit.gif" title="' . get_lang("Edit") . '" /> ' . get_lang("Edit") . '</a>';
  6206. $return .= '<a href="' . api_get_self() . '?cidReq=' . Security :: remove_XSS($_GET['cidReq']) . '&amp;action=move_item&amp;view=build&amp;id=' . $item_id . '&amp;lp_id=' . $this->lp_id . '" title="Move the current item"><img align="absbottom" alt="Move the current item" src="../img/deplacer_fichier.gif" title="' . get_lang("Move") . '" /> ' . get_lang("Move") . '</a>';
  6207. // commented for now as prerequisites cannot be added to chapters
  6208. if ($item_type != 'dokeos_chapter' && $item_type != 'chapter') {
  6209. $return .= '<a href="' . api_get_self() . '?cidReq=' . Security :: remove_XSS($_GET['cidReq']) . '&amp;action=edit_item_prereq&amp;view=build&amp;id=' . $item_id . '&amp;lp_id=' . $this->lp_id . '" title="' . get_lang('Prerequisites') . '"><img align="absbottom" alt="' . get_lang('Prerequisites') . '" src="../img/right.gif" title="' . get_lang('Prerequisites') . '" /> ' . get_lang('Prerequisites') . '</a>';
  6210. }
  6211. $return .= '<a href="' . api_get_self() . '?cidReq=' . Security :: remove_XSS($_GET['cidReq']) . '&amp;action=delete_item&amp;view=build&amp;id=' . $item_id . '&amp;lp_id=' . $this->lp_id . '" onclick="return confirmation(\'' . addslashes($s_title) . '\');" title="Delete the current item"><img alt="Delete the current item" align="absbottom" src="../img/delete.gif" title="' . get_lang("Delete") . '" /> ' . get_lang("Delete") . '</a>';
  6212. //$return .= '<br><br><p class="lp_text">' . ((trim($s_description) == '') ? ''.get_lang("NoDescription").'' : stripslashes(nl2br($s_description))) . '</p>';
  6213. //$return.="</td><td valign='top'>";
  6214. // get the audiorecorder. Use of ob_* functions since there are echos in the file
  6215. ob_start();
  6216. $audio_recorder_studentview = 'false';
  6217. $audio_recorder_item_id = $item_id;
  6218. if (api_get_setting('service_visio', 'active') == 'true') {
  6219. include ('audiorecorder.inc.php');
  6220. }
  6221. $return .= ob_get_contents();
  6222. ob_end_clean();
  6223. // end of audiorecorder include
  6224. $return .= '</div>';
  6225. return $return;
  6226. }
  6227. /**
  6228. * Creates the javascript needed for filling up the checkboxes without page reload
  6229. *
  6230. * @return string
  6231. */
  6232. function create_js() {
  6233. $return = '<script language="javascript" type="text/javascript">' . "\n";
  6234. $return .= 'function load_cbo(id){' . "\n";
  6235. $return .= "var cbo = document.getElementById('idPosition');\n";
  6236. $return .= 'for(var i = cbo.length - 1; i > 0; i--)';
  6237. $return .= 'cbo.options[i] = null;' . "\n";
  6238. $return .= 'var k=0;' . "\n";
  6239. $return .= 'for(var i = 1; i <= child_name[id].length; i++){' . "\n";
  6240. $return .= ' cbo.options[i] = new Option(child_name[id][i-1], child_value[id][i-1]);' . "\n";
  6241. $return .= 'k=i;';
  6242. $return .= '}' . "\n\n";
  6243. $return .= 'if( typeof cbo != "undefined" ) {cbo.options[k].selected = true;}';
  6244. $return .= '}';
  6245. $return .= 'var child_name = new Array();' . "\n";
  6246. $return .= 'var child_value = new Array();' . "\n\n";
  6247. $return .= 'child_name[0] = new Array();' . "\n";
  6248. $return .= 'child_value[0] = new Array();' . "\n\n";
  6249. $tbl_lp_item = Database :: get_course_table(TABLE_LP_ITEM);
  6250. $sql_zero = "
  6251. SELECT *
  6252. FROM " . $tbl_lp_item . "
  6253. WHERE
  6254. lp_id = " . $this->lp_id . " AND
  6255. parent_item_id = 0
  6256. ORDER BY display_order ASC";
  6257. $res_zero = api_sql_query($sql_zero, __FILE__, __LINE__);
  6258. global $charset;
  6259. $i = 0;
  6260. while ($row_zero = Database :: fetch_array($res_zero)) {
  6261. $return .= 'child_name[0][' . $i . '] = "' . get_lang("After") . ' \"' . api_convert_encoding($row_zero['title'], $charset, $this->encoding) . '\"";' . "\n";
  6262. $return .= 'child_value[0][' . $i++ . '] = "' . $row_zero['id'] . '";' . "\n";
  6263. }
  6264. $return .= "\n";
  6265. $sql = "
  6266. SELECT *
  6267. FROM " . $tbl_lp_item . "
  6268. WHERE
  6269. lp_id = " . $this->lp_id;
  6270. $res = api_sql_query($sql, __FILE__, __LINE__);
  6271. while ($row = Database :: fetch_array($res)) {
  6272. $sql_parent = "
  6273. SELECT *
  6274. FROM " . $tbl_lp_item . "
  6275. WHERE parent_item_id = " . $row['id'] . "
  6276. ORDER BY display_order ASC";
  6277. $res_parent = api_sql_query($sql_parent, __FILE__, __LINE__);
  6278. $i = 0;
  6279. $return .= 'child_name[' . $row['id'] . '] = new Array();' . "\n";
  6280. $return .= 'child_value[' . $row['id'] . '] = new Array();' . "\n\n";
  6281. while ($row_parent = Database :: fetch_array($res_parent)) {
  6282. $return .= 'child_name[' . $row['id'] . '][' . $i . '] = "' . get_lang("After") . ' \"' . api_convert_encoding($row_parent['title'], $charset, $this->encoding) . '\"";' . "\n";
  6283. $return .= 'child_value[' . $row['id'] . '][' . $i++ . '] = "' . $row_parent['id'] . '";' . "\n";
  6284. }
  6285. $return .= "\n";
  6286. }
  6287. $return .= '</script>' . "\n";
  6288. return $return;
  6289. }
  6290. /**
  6291. * Display the form to allow moving an item
  6292. *
  6293. * @param integer Item ID
  6294. * @return string HTML form
  6295. */
  6296. function display_move_item($item_id) {
  6297. global $_course; //will disappear
  6298. global $charset;
  6299. $return = '';
  6300. if (is_numeric($item_id)) {
  6301. $tbl_lp_item = Database :: get_course_table(TABLE_LP_ITEM);
  6302. $sql = "SELECT *
  6303. FROM " . $tbl_lp_item . "
  6304. WHERE id = " . $item_id;
  6305. $res = api_sql_query($sql, __FILE__, __LINE__);
  6306. $row = Database :: fetch_array($res);
  6307. switch ($row['item_type']) {
  6308. case 'dokeos_chapter' :
  6309. case 'dir' :
  6310. case 'asset' :
  6311. $return .= $this->display_manipulate($item_id, $row['item_type']);
  6312. $return .= $this->display_item_form($row['item_type'], get_lang('MoveCurrentChapter'), 'move', $item_id, $row);
  6313. break;
  6314. case 'dokeos_module' :
  6315. $return .= $this->display_manipulate($item_id, $row['item_type']);
  6316. $return .= $this->display_item_form($row['item_type'], 'Move th current module:', 'move', $item_id, $row);
  6317. break;
  6318. case TOOL_DOCUMENT :
  6319. $return .= $this->display_manipulate($item_id, $row['item_type']);
  6320. $return .= $this->display_document_form('move', $item_id, $row);
  6321. break;
  6322. case TOOL_LINK :
  6323. $return .= $this->display_manipulate($item_id, $row['item_type']);
  6324. $return .= $this->display_link_form('move', $item_id, $row);
  6325. break;
  6326. case TOOL_HOTPOTATOES :
  6327. $return .= $this->display_manipulate($item_id, $row['item_type']);
  6328. $return .= $this->display_link_form('move', $item_id, $row);
  6329. break;
  6330. case TOOL_QUIZ :
  6331. $return .= $this->display_manipulate($item_id, $row['item_type']);
  6332. $return .= $this->display_quiz_form('move', $item_id, $row);
  6333. break;
  6334. case TOOL_STUDENTPUBLICATION :
  6335. $return .= $this->display_manipulate($item_id, $row['item_type']);
  6336. $return .= $this->display_student_publication_form('move', $item_id, $row);
  6337. break;
  6338. case TOOL_FORUM :
  6339. $return .= $this->display_manipulate($item_id, $row['item_type']);
  6340. $return .= $this->display_forum_form('move', $item_id, $row);
  6341. }
  6342. }
  6343. return $return;
  6344. }
  6345. /**
  6346. * Displays a basic form on the overview page for changing the item title and the item description.
  6347. *
  6348. * @param string $item_type
  6349. * @param string $title
  6350. * @param array $data
  6351. * @return string
  6352. */
  6353. function display_item_small_form($item_type, $title = '', $data) {
  6354. global $charset;
  6355. $return .= '<div class="lp_small_form">' . "\n";
  6356. $return .= '<p class="lp_title">' . $title . '</p>';
  6357. $return .= '<form method="post">' . "\n";
  6358. $return .= '<table cellpadding="0" cellspacing="0" class="lp_form">';
  6359. $return .= "\t\t" . '<tr>' . "\n";
  6360. $return .= "\t\t\t" . '<td class="label"><label for="idTitle">Title&nbsp;:</label></td>' . "\n";
  6361. $return .= "\t\t\t" . '<td class="input"><input class="small_form" id="idTitle" name="title" type="text" value="' . api_html_entity_decode(api_convert_encoding(htmlspecialchars($data['title']), $charset, $this->encoding), ENT_QUOTES, $charset) . '" /></td>' . "\n";
  6362. $return .= "\t\t" . '</tr>' . "\n";
  6363. //it said these lines of code - see SVN#11724 and SVN#10770
  6364. //$return .= "\t\t" . '<tr>' . "\n";
  6365. //$return .= "\t\t\t" . '<td class="label"><label for="idDescription">Description&nbsp;:</label></td>' . "\n";
  6366. //$return .= "\t\t\t" . '<td class="input"><textarea class="small_form" id="idDescription" name="description" rows="4">' . $data['description'] . '</textarea></td>' . "\n";
  6367. //$return .= "\t\t" . '</tr>' . "\n";
  6368. $return .= "\t\t" . '<tr>' . "\n";
  6369. $return .= "\t\t\t" . '<td colspan="2"><button class="save" name="submit_button" type="submit">' . get_lang('Save') . '</button></td>' . "\n";
  6370. $return .= "\t\t" . '</tr>' . "\n";
  6371. $return .= "\t\t" . '</table>' . "\n";
  6372. $return .= "\t" . '<input name="parent" type="hidden" value="' . $data['parent_item_id'] . '"/>' . "\n";
  6373. $return .= "\t" . '<input name="previous" type="hidden" value="' . $data['previous_item_id'] . '"/>' . "\n";
  6374. $return .= '</form>';
  6375. $return .= '</div>';
  6376. return $return;
  6377. }
  6378. /**
  6379. * Return HTML form to allow prerequisites selection
  6380. *
  6381. * @param integer Item ID
  6382. * @return string HTML form
  6383. */
  6384. function display_item_prerequisites_form($item_id) {
  6385. global $charset;
  6386. $tbl_lp_item = Database :: get_course_table(TABLE_LP_ITEM);
  6387. /* current prerequisite */
  6388. $sql = "
  6389. SELECT *
  6390. FROM " . $tbl_lp_item . "
  6391. WHERE id = " . $item_id;
  6392. $result = api_sql_query($sql, __FILE__, __LINE__);
  6393. $row = Database :: fetch_array($result);
  6394. $preq_id = $row['prerequisite'];
  6395. //$preq_mastery = $row['mastery_score'];
  6396. //$preq_max = $row['max_score'];
  6397. $return = $this->display_manipulate($item_id, TOOL_DOCUMENT);
  6398. $return = ' <div class="row">
  6399. <div class="form_header">';
  6400. $return .= get_lang('AddEditPrerequisites');
  6401. $return .= '</div> </div>';
  6402. $return .= '<div class="sectioncomment">';
  6403. $return .= '<form method="POST">';
  6404. $return .= '<table class="lp_prerequisites" style="border-collapse:collapse;">';
  6405. $return .= '<tr style="border-collapse:collapse;">';
  6406. $return .= '<th height="24" style="border:1px solid gray; background-color:#F0F0F0;">' . get_lang('Prerequisites') . '</th>';
  6407. $return .= '<th width="70" height="24" style="border:1px solid gray; background-color:#F0F0F0;">' . get_lang('Minimum') . '</th>';
  6408. $return .= '<th width="70" height="24" style="border:1px solid gray; background-color:#F0F0F0;">' . get_lang('Maximum') . '</th>';
  6409. $return .= '</tr>';
  6410. //$return .= '<tr >';
  6411. //$return .= '<td class="radio" style="border:1px solid gray;border-collapse:collapse;">';
  6412. //$return .= '<input checked="checked" id="idNone" name="prerequisites" style="margin-left:0; margin-right:10px;" type="radio" />';
  6413. //$return .= '<label for="idNone">' . get_lang('None') . '</label>';
  6414. //$return .= '</td><td colspan="2" />';
  6415. //$return .= '</tr>';
  6416. $sql = "
  6417. SELECT *
  6418. FROM " . $tbl_lp_item . "
  6419. WHERE
  6420. lp_id = " . $this->lp_id;
  6421. $result = api_sql_query($sql, __FILE__, __LINE__);
  6422. $arrLP = array ();
  6423. while ($row = Database :: fetch_array($result)) {
  6424. $arrLP[] = array (
  6425. 'id' => $row['id'],
  6426. 'item_type' => $row['item_type'],
  6427. 'title' => api_convert_encoding($row['title'], $charset, $this->encoding),
  6428. 'ref' => $row['ref'],
  6429. 'description' => $row['description'],
  6430. 'parent_item_id' => $row['parent_item_id'],
  6431. 'previous_item_id' => $row['previous_item_id'],
  6432. 'next_item_id' => $row['next_item_id'],
  6433. 'max_score' => $row['max_score'],
  6434. 'min_score' => $row['min_score'],
  6435. 'mastery_score' => $row['mastery_score'],
  6436. 'prerequisite' => $row['prerequisite'],
  6437. 'next_item_id' => $row['next_item_id'],
  6438. 'display_order' => $row['display_order']
  6439. );
  6440. if ($row['ref'] == $preq_id) {
  6441. $preq_mastery = $row['mastery_score'];
  6442. $preq_max = $row['max_score'];
  6443. }
  6444. }
  6445. $this->tree_array($arrLP);
  6446. $arrLP = $this->arrMenu;
  6447. unset ($this->arrMenu);
  6448. for ($i = 0; $i < count($arrLP); $i++) {
  6449. if ($arrLP[$i]['id'] == $item_id)
  6450. break;
  6451. $return .= '<tr >';
  6452. $return .= '<td style="border:1px solid #B0B0B0; border-collapse:collapse; height:25px; padding: 3px 5px 3px 3px;" class="radio"' . (($arrLP[$i]['item_type'] != TOOL_QUIZ && $arrLP[$i]['item_type'] != TOOL_HOTPOTATOES) ? ' colspan="3"' : '') . '>';
  6453. $return .= '<input' . (($arrLP[$i]['id'] == $preq_id) ? ' checked="checked" ' : '') . (($arrLP[$i]['item_type'] == 'dokeos_module' || $arrLP[$i]['item_type'] == 'dokeos_chapter') ? ' disabled="disabled" ' : ' ') . 'id="id' . $arrLP[$i]['id'] . '" name="prerequisites" style="margin-left:' . $arrLP[$i]['depth'] * 10 . 'px; margin-right:10px;" type="radio" value="' . $arrLP[$i]['id'] . '" />';
  6454. $icon_name = str_replace(' ', '', $arrLP[$i]['item_type']);
  6455. if (file_exists("../img/lp_" . $icon_name . ".png")) {
  6456. $return .= '<img alt="" src="../img/lp_' . $icon_name . '.png" style="margin-right:5px;" title="" />';
  6457. } else
  6458. if (file_exists("../img/lp_" . $icon_name . ".gif")) {
  6459. $return .= '<img alt="" src="../img/lp_' . $icon_name . '.gif" style="margin-right:5px;" title="" />';
  6460. } else {
  6461. $return .= Display::return_icon('folder_document.gif','',array('style'=>'margin-right:5px;'));
  6462. }
  6463. $return .= '<label for="id' . $arrLP[$i]['id'] . '">' . $arrLP[$i]['title'] . '</label>';
  6464. $return .= '</td>';
  6465. //$return .= '<td class="radio"' . (($arrLP[$i]['item_type'] != TOOL_HOTPOTATOES) ? ' colspan="3"' : '') . ' />';
  6466. if ($arrLP[$i]['item_type'] == TOOL_QUIZ) {
  6467. $return .= '<td class="exercise" style="border:1px solid #B0B0B0;">';
  6468. $return .= '<center><input size="4" maxlength="3" name="min_' . $arrLP[$i]['id'] . '" type="text" value="' . (($arrLP[$i]['id'] == $preq_id) ? $preq_mastery : 0) . '" /></center>';
  6469. $return .= '</td>';
  6470. $return .= '<td class="exercise" style="border:1px solid #B0B0B0;">';
  6471. $return .= '<center><input size="4" maxlength="3" name="max_' . $arrLP[$i]['id'] . '" type="text" value="' . $arrLP[$i]['max_score'] . '" disabled="true" /></center>';
  6472. $return .= '</td>';
  6473. }
  6474. if ($arrLP[$i]['item_type'] == TOOL_HOTPOTATOES) {
  6475. $return .= '<td class="exercise" style="border:1px solid #B0B0B0;">';
  6476. $return .= '<center><input size="4" maxlength="3" name="min_' . $arrLP[$i]['id'] . '" type="text" value="' . (($arrLP[$i]['id'] == $preq_id) ? $preq_mastery : 0) . '" /></center>';
  6477. $return .= '</td>';
  6478. $return .= '<td class="exercise" style="border:1px solid #B0B0B0;">';
  6479. $return .= '<center><input size="4" maxlength="3" name="max_' . $arrLP[$i]['id'] . '" type="text" value="' . $arrLP[$i]['max_score'] . '" disabled="true" /></center>';
  6480. $return .= '</td>';
  6481. }
  6482. $return .= '</tr>';
  6483. }
  6484. $return .= '<tr>';
  6485. $return .= '<td colspan="3">';
  6486. $return .= '</td>';
  6487. $return .= '</tr>';
  6488. $return .= '</table>';
  6489. $return .= '<div style="padding-top:3px;">';
  6490. $return .= '<button class="save" name="submit_button" type="submit">' . get_lang("ModifyPrerequisites") . ' </button></td>' . "\n";
  6491. $return .= '</div>';
  6492. $return .= '</form>';
  6493. $return .= '</div>';
  6494. return $return;
  6495. }
  6496. /**
  6497. * Creates a list with all the documents in it
  6498. *
  6499. * @return string
  6500. */
  6501. function get_documents() {
  6502. global $_course;
  6503. $tbl_doc = Database :: get_course_table(TABLE_DOCUMENT);
  6504. $sql_doc = "
  6505. SELECT *
  6506. FROM " . $tbl_doc . "
  6507. WHERE
  6508. path NOT LIKE '%_DELETED_%'
  6509. ORDER BY path ASC";
  6510. $res_doc = api_sql_query($sql_doc, __FILE__, __LINE__);
  6511. $return = '<div class="lp_resource_header"' . " onclick=\"if(document.getElementById('resDoc').style.display == 'block') {document.getElementById('resDoc').style.display = 'none';} else {document.getElementById('resDoc').style.display = 'block';}\"" . '>'.Display::return_icon('folder_document.gif',get_lang('Documents'),array('style'=>'margin-right:5px;', 'height' => '16px')).' '. get_lang('Documents') . '</div>';
  6512. $return .= '<div class="lp_resource_elements" id="resDoc">';
  6513. $resources = api_store_result($res_doc);
  6514. $resources_sorted = array ();
  6515. // if you want to debug it, I advise you to do "echo" on the eval statements
  6516. foreach ($resources as $resource) {
  6517. $resource_paths = explode('/', $resource['path']);
  6518. array_shift($resource_paths);
  6519. $path_to_eval = $last_path = '';
  6520. $is_file = false;
  6521. foreach ($resource_paths as $key => $resource_path) {
  6522. if (strpos($resource_path, '.') === false && $key != count($resource_paths) - 1) { // it's a folder
  6523. $path_to_eval .= '["' . $resource_path . '"]["files"]';
  6524. } else
  6525. if (strpos($resource_path, '.') !== false)
  6526. $is_file = true;
  6527. $last_path = $resource_path;
  6528. }
  6529. if ($is_file) {
  6530. eval ('$resources_sorted' . $path_to_eval . '[' . $resource['id'] . '] = "' . $last_path . '";');
  6531. } else {
  6532. eval ('$resources_sorted' . $path_to_eval . '["' . $last_path . '"]["id"]=' . $resource['id'] . ';');
  6533. }
  6534. }
  6535. $return .= $this->write_resources_tree($resources_sorted);
  6536. $return .= '</div>';
  6537. if (Database :: num_rows($res_doc) == 0)
  6538. $return .= '<div class="lp_resource_element">' . get_lang("NoDocuments") . '</div>';
  6539. return $return;
  6540. }
  6541. /**
  6542. * Generate and return an HTML list of resources based on a given array.
  6543. *
  6544. * This list is used to show the course creator a list of available resources to choose from
  6545. * when creating a learning path.
  6546. * @param array Array of elements to add to the list
  6547. * @param integer Enables the tree display by shifting the new elements a certain distance to the right
  6548. * @return string The HTML list
  6549. */
  6550. function write_resources_tree($resources_sorted, $num = 0) {
  6551. include_once (api_get_path(LIBRARY_PATH) . 'fileDisplay.lib.php');
  6552. if (count($resources_sorted) > 0) {
  6553. foreach ($resources_sorted as $key => $resource) {
  6554. if (is_int($resource['id'])) { // it's a folder
  6555. $return .= '<div><div style="margin-left:' . ($num * 15) . 'px;margin-right:5px;"><img style="cursor: pointer;" src="../img/nolines_plus.gif" align="absmiddle" id="img_' . $resource["id"] . '" onclick="testResources(\'' . $resource["id"] . '\',\'img_' . $resource["id"] . '\')"><img alt="" src="../img/lp_folder.gif" title="" align="absmiddle" />&nbsp;<span onclick="testResources(\'' . $resource["id"] . '\',\'img_' . $resource["id"] . '\')" style="cursor: pointer;" >' . $key . '</span></div><div style="display: none;" id="' . $resource['id'] . '">';
  6556. $return .= $this->write_resources_tree($resource['files'], $num +1);
  6557. $return .= "</div></div>\r\n";
  6558. } else {
  6559. // it's a file
  6560. $icon = choose_image($resource);
  6561. $position = strrpos($icon, '.');
  6562. $icon = substr($icon, 0, $position) . '_small.gif';
  6563. $return .= '<div><div style="margin-left:' . (($num +1) * 15) . 'px;margin-right:5px;"><a href="' . api_get_self() . '?cidReq=' . $_GET['cidReq'] . '&amp;action=add_item&amp;type=' . TOOL_DOCUMENT . '&amp;file=' . $key . '&amp;lp_id=' . $this->lp_id . '"><img alt="" src="../img/' . $icon . '" title="" />&nbsp;' . $resource . "</a></div></div>\r\n";
  6564. }
  6565. }
  6566. }
  6567. return $return;
  6568. }
  6569. /**
  6570. * Creates a list with all the exercises (quiz) in it
  6571. *
  6572. * @return string
  6573. */
  6574. function get_exercises() {
  6575. // new for hotpotatoes
  6576. $uploadPath = DIR_HOTPOTATOES; //defined in main_api
  6577. $tbl_doc = Database :: get_course_table(TABLE_DOCUMENT);
  6578. $tbl_quiz = Database :: get_course_table(TABLE_QUIZ_TEST);
  6579. $sql_quiz = "
  6580. SELECT *
  6581. FROM " . $tbl_quiz . "
  6582. WHERE active<>'-1'
  6583. ORDER BY title ASC";
  6584. $sql_hot = "SELECT * FROM " . $tbl_doc . " " .
  6585. " WHERE path LIKE '" . $uploadPath . "/%/%htm%'" .
  6586. " ORDER BY id ASC";
  6587. $res_quiz = api_sql_query($sql_quiz, __FILE__, __LINE__);
  6588. $res_hot = api_sql_query($sql_hot, __FILE__, __LINE__);
  6589. $return .= '<div class="lp_resource_header"' . " onclick=\"if(document.getElementById('resExercise').style.display == 'block') {document.getElementById('resExercise').style.display = 'none';} else {document.getElementById('resExercise').style.display = 'block';}\"" . ' ><img align="left" alt="" src="../img/lp_' . TOOL_QUIZ . '.gif" style="margin-right:5px;" title="" />' . get_lang('Quiz') . '</div>';
  6590. $return .= '<div class="lp_resource_elements" id="resExercise">';
  6591. while ($row_hot = Database :: fetch_array($res_hot)) {
  6592. $return .= '<div class="lp_resource_element">';
  6593. //display quizhotpotatoes
  6594. $return .= '<img alt="" src="../img/jqz.gif" style="margin-right:5px;" title="" />';
  6595. $return .= '<a href="' . api_get_self() . '?cidReq=' . Security :: remove_XSS($_GET['cidReq']) . '&amp;action=add_item&amp;type=' . TOOL_HOTPOTATOES . '&amp;file=' . $row_hot['id'] . '&amp;lp_id=' . $this->lp_id . '">' . ((!empty ($row_hot['comment'])) ? $row_hot['comment'] : $row_hot['title']) . '</a>';
  6596. //$return .= $row_quiz['title'];
  6597. $return .= '</div>';
  6598. }
  6599. while ($row_quiz = Database :: fetch_array($res_quiz)) {
  6600. $return .= '<div class="lp_resource_element">';
  6601. $return .= '<img alt="" src="../img/quizz_small.gif" style="margin-right:5px;" title="" />';
  6602. $return .= '<a href="' . api_get_self() . '?cidReq=' . Security :: remove_XSS($_GET['cidReq']) . '&amp;action=add_item&amp;type=' . TOOL_QUIZ . '&amp;file=' . $row_quiz['id'] . '&amp;lp_id=' . $this->lp_id . '">' . $row_quiz['title'] . '</a>';
  6603. //$return .= $row_quiz['title'];
  6604. $return .= '</div>';
  6605. }
  6606. if (Database :: num_rows($res_quiz) == 0)
  6607. $return .= '<div class="lp_resource_element">' . get_lang("NoExercisesAvailable") . '</div>';
  6608. $return .= '<div class="lp_resource_element">';
  6609. $return .= '<img alt="" src="../img/new_test_small.gif" style="margin-right:5px;" title="" />';
  6610. $return .= '<a href="' . api_get_path(REL_CODE_PATH) . 'exercice/exercise_admin.php">' . get_lang('NewExercise') . '</a>';
  6611. $return .= '</div>';
  6612. $return .= '</div>';
  6613. return $return;
  6614. }
  6615. /**
  6616. * Creates a list with all the links in it
  6617. *
  6618. * @return string
  6619. */
  6620. function get_links() {
  6621. $tbl_link = Database :: get_course_table(TABLE_LINK);
  6622. $sql_link = "
  6623. SELECT *
  6624. FROM " . $tbl_link . "
  6625. ORDER BY title ASC";
  6626. $res_link = api_sql_query($sql_link, __FILE__, __LINE__);
  6627. $return .= '<div class="lp_resource_header"' . " onclick=\"if(document.getElementById('resLink').style.display == 'block') {document.getElementById('resLink').style.display = 'none';} else {document.getElementById('resLink').style.display = 'block';}\"" . '><img alt="" src="../img/lp_' . TOOL_LINK . '.gif" style="margin-right:5px;" title="" />' . get_lang("Links") . '</div>';
  6628. $return .= '<div class="lp_resource_elements" id="resLink">';
  6629. while ($row_link = Database :: fetch_array($res_link)) {
  6630. $return .= '<div class="lp_resource_element">';
  6631. $return .= '<img alt="" src="../img/file_html_small.gif" style="margin-right:5px;" title="" />';
  6632. $return .= '<a href="' . api_get_self() . '?cidReq=' . Security :: remove_XSS($_GET['cidReq']) . '&amp;action=add_item&amp;type=' . TOOL_LINK . '&amp;file=' . $row_link['id'] . '&amp;lp_id=' . $this->lp_id . '">' . $row_link['title'] . '</a>';
  6633. $return .= '</div>';
  6634. }
  6635. $return .= '<div class="lp_resource_element">';
  6636. $return .= '<img alt="" src="../img/file_html_new_small.gif" style="margin-right:5px;" title="" />';
  6637. $return .= '<a href="' . api_get_path(REL_CODE_PATH) . 'link/link.php?' . api_get_cidreq() . '&action=addlink" title="' . get_lang('LinkAdd') . '">' . get_lang('LinkAdd') . '</a>';
  6638. $return .= '</div>';
  6639. if (Database :: num_rows($res_link) == 0)
  6640. $return .= '<div class="lp_resource_element">' . get_lang("NoLinksAvailable") . '</div>';
  6641. $return .= '</div>';
  6642. return $return;
  6643. }
  6644. /**
  6645. * Creates a list with all the student publications in it
  6646. *
  6647. * @return unknown
  6648. */
  6649. function get_student_publications() {
  6650. $tbl_student = Database :: get_course_table(TABLE_STUDENT_PUBLICATION);
  6651. $sql_student = "
  6652. SELECT *
  6653. FROM " . $tbl_student . "
  6654. ORDER BY title ASC";
  6655. $res_student = api_sql_query($sql_student, __FILE__, __LINE__);
  6656. $return .= '<div class="lp_resource_header"' . " onclick=\"if(document.getElementById('resStudent').style.display == 'block') {document.getElementById('resStudent').style.display = 'none';} else {document.getElementById('resStudent').style.display = 'block';}\"" . '><img alt="" src="../img/lp_' . TOOL_STUDENTPUBLICATION . '.gif" style="margin-right:5px;" title="" />' . get_lang('Student_publication') . '</div>';
  6657. $return .= '<div class="lp_resource_elements" id="resStudent">';
  6658. $return .= '<div class="lp_resource_element">';
  6659. $return .= '<img align="left" alt="" src="../img/works_small.gif" style="margin-right:5px;" title="" />';
  6660. $return .= '<a href="' . api_get_self() . '?cidReq=' . Security :: remove_XSS($_GET['cidReq']) . '&amp;action=add_item&amp;type=' . TOOL_STUDENTPUBLICATION . '&amp;lp_id=' . $this->lp_id . '">' . get_lang('AddAssignmentPage') . '</a>';
  6661. $return .= '</div>';
  6662. $return .= '</div>';
  6663. return $return;
  6664. }
  6665. /**
  6666. * Creates a list with all the forums in it
  6667. *
  6668. * @return string
  6669. */
  6670. function get_forums() {
  6671. include ('../forum/forumfunction.inc.php');
  6672. include ('../forum/forumconfig.inc.php');
  6673. global $table_forums, $table_threads, $table_posts, $table_item_property, $table_users;
  6674. $table_forums = Database :: get_course_table(TABLE_FORUM);
  6675. $table_threads = Database :: get_course_table(TABLE_FORUM_THREAD);
  6676. $table_posts = Database :: get_course_table(TABLE_FORUM_POST);
  6677. $table_item_property = Database :: get_course_table(TABLE_ITEM_PROPERTY);
  6678. $table_users = Database :: get_main_table(TABLE_MAIN_USER);
  6679. $a_forums = get_forums();
  6680. $return .= '<div class="lp_resource_header"' . " onclick=\"if(document.getElementById('forums').style.display == 'block') {document.getElementById('forums').style.display = 'none';} else {document.getElementById('forums').style.display = 'block';}\"" . '><img alt="" src="../img/lp_forum.gif" style="margin-right:5px;" title="" />' . get_lang('Forums') . '</div>';
  6681. $return .= '<div class="lp_resource_elements" style="border:1px solid #999999;" id="forums">';
  6682. foreach ($a_forums as $forum) {
  6683. $return .= '<div class="lp_resource_element">';
  6684. $return .= '<script type="text/javascript">
  6685. function toggle_forum(forum_id){
  6686. if(document.getElementById("forum_"+forum_id+"_content").style.display == "none"){
  6687. document.getElementById("forum_"+forum_id+"_content").style.display = "block";
  6688. document.getElementById("forum_"+forum_id+"_opener").src = "' . api_get_path(WEB_IMG_PATH) . 'remove.gif";
  6689. }
  6690. else {
  6691. document.getElementById("forum_"+forum_id+"_content").style.display = "none";
  6692. document.getElementById("forum_"+forum_id+"_opener").src = "' . api_get_path(WEB_IMG_PATH) . 'add.gif";
  6693. }
  6694. }
  6695. </script>
  6696. ';
  6697. $return .= '<img alt="" src="../img/lp_forum.gif" style="margin-right:5px;" title="" />';
  6698. $return .= '<a style="cursor:hand" onclick="toggle_forum(' . $forum['forum_id'] . ')" style="vertical-align:middle"><img src="' . api_get_path(WEB_IMG_PATH) . 'add.gif" id="forum_' . $forum['forum_id'] . '_opener" align="absbottom" /></a>
  6699. <a href="' . api_get_self() . '?cidReq=' . Security :: remove_XSS($_GET['cidReq']) . '&amp;action=add_item&amp;type=' . TOOL_FORUM . '&amp;forum_id=' . $forum['forum_id'] . '&amp;lp_id=' . $this->lp_id . '" style="vertical-align:middle">' . $forum['forum_title'] . '</a><ul style="display:none" id="forum_' . $forum['forum_id'] . '_content">';
  6700. $a_threads = get_threads($forum['forum_id']);
  6701. if (is_array($a_threads)) {
  6702. foreach ($a_threads as $thread) {
  6703. $return .= '<li><a href="' . api_get_self() . '?cidReq=' . Security :: remove_XSS($_GET['cidReq']) . '&amp;action=add_item&amp;type=' . TOOL_THREAD . '&amp;thread_id=' . $thread['thread_id'] . '&amp;lp_id=' . $this->lp_id . '">' . $thread['thread_title'] . '</a></li>';
  6704. }
  6705. }
  6706. $return .= '</ul></div>';
  6707. }
  6708. $return .= '<div class="lp_resource_element">';
  6709. $return .= '<img alt="" src="../img/forum_new_small.gif" style="margin-right:5px;" title="" />';
  6710. $return .= '<a href="' . api_get_path(REL_CODE_PATH) . 'forum/index.php?' . api_get_cidreq() . '&action=add&amp;content=forum" title="' . get_lang('CreateANewForum') . '">' . get_lang('CreateANewForum') . '</a>';
  6711. $return .= '</div>';
  6712. return $return;
  6713. }
  6714. /**
  6715. * Exports the learning path as a SCORM package. This is the main function that
  6716. * gathers the content, transforms it, writes the imsmanifest.xml file, zips the
  6717. * whole thing and returns the zip.
  6718. *
  6719. * This method needs to be called in PHP5, as it will fail with non-adequate
  6720. * XML package (like the ones for PHP4), and it is *not* a static method, so
  6721. * you need to call it on a learnpath object.
  6722. * @TODO The method might be redefined later on in the scorm class itself to avoid
  6723. * creating a SCORM structure if there is one already. However, if the initial SCORM
  6724. * path has been modified, it should use the generic method here below.
  6725. * @TODO link this function with the export_lp() function in the same class
  6726. * @param string Optional name of zip file. If none, title of learnpath is
  6727. * domesticated and trailed with ".zip"
  6728. * @return string Returns the zip package string, or null if error
  6729. */
  6730. function scorm_export()
  6731. {
  6732. global $_course;
  6733. global $charset;
  6734. if (!class_exists('DomDocument'))
  6735. {
  6736. error_log('DOM functions not supported for PHP version below 5.0',0);
  6737. $this->error = 'PHP DOM functions not supported for PHP versions below 5.0';
  6738. return null;
  6739. }
  6740. //remove memory and time limits as much as possible as this might be a long process...
  6741. if(function_exists('ini_set'))
  6742. {
  6743. $mem = ini_get('memory_limit');
  6744. if(substr($mem,-1,1)=='M')
  6745. {
  6746. $mem_num = substr($mem,0,-1);
  6747. if($mem_num<128)
  6748. {
  6749. ini_set('memory_limit','128M');
  6750. }
  6751. }
  6752. else
  6753. {
  6754. ini_set('memory_limit','128M');
  6755. }
  6756. ini_set('max_execution_time',600);
  6757. //}else{
  6758. //error_log('Scorm export: could not change memory and time limits',0);
  6759. }
  6760. //Create the zip handler (this will remain available throughout the method)
  6761. $archive_path = api_get_path(SYS_ARCHIVE_PATH);
  6762. $sys_course_path = api_get_path(SYS_COURSE_PATH);
  6763. $temp_dir_short = uniqid();
  6764. $temp_zip_dir = $archive_path."/".$temp_dir_short;
  6765. $temp_zip_file = $temp_zip_dir."/".md5(time()).".zip";
  6766. $zip_folder=new PclZip($temp_zip_file);
  6767. $current_course_path = api_get_path(SYS_COURSE_PATH).api_get_course_path();
  6768. $root_path = $main_path = api_get_path(SYS_PATH);
  6769. $files_cleanup = array();
  6770. //place to temporarily stash the zipfiles
  6771. //create the temp dir if it doesn't exist
  6772. //or do a cleanup befor creating the zipfile
  6773. if(!is_dir($temp_zip_dir))
  6774. {
  6775. mkdir($temp_zip_dir);
  6776. }
  6777. else
  6778. {//cleanup: check the temp dir for old files and delete them
  6779. $handle=opendir($temp_zip_dir);
  6780. while (false!==($file = readdir($handle)))
  6781. {
  6782. if ($file != "." && $file != "..")
  6783. {
  6784. unlink("$temp_zip_dir/$file");
  6785. }
  6786. }
  6787. closedir($handle);
  6788. }
  6789. $zip_files = $zip_files_abs = $zip_files_dist = array();
  6790. if(is_dir($current_course_path.'/scorm/'.$this->path) && is_file($current_course_path.'/scorm/'.$this->path.'/imsmanifest.xml'))
  6791. {
  6792. // remove the possible . at the end of the path
  6793. $dest_path_to_lp = substr($this->path, -1) == '.' ? substr($this->path, 0, -1) : $this->path;
  6794. $dest_path_to_scorm_folder = str_replace('//','/',$temp_zip_dir.'/scorm/'.$dest_path_to_lp);
  6795. $perm = api_get_setting('permissions_for_new_directories');
  6796. $perm = octdec(!empty($perm)?$perm:'0770');
  6797. mkdir ($dest_path_to_scorm_folder, $perm, true);
  6798. $zip_files_dist = copyr($current_course_path.'/scorm/'.$this->path, $dest_path_to_scorm_folder, array('imsmanifest'), $zip_files);
  6799. }
  6800. //Build a dummy imsmanifest structure. Do not add to the zip yet (we still need it)
  6801. //This structure is developed following regulations for SCORM 1.2 packaging in the SCORM 1.2 Content
  6802. //Aggregation Model official document, secion "2.3 Content Packaging"
  6803. $xmldoc = new DOMDocument('1.0',$this->encoding);
  6804. $root = $xmldoc->createElement('manifest');
  6805. $root->setAttribute('identifier','SingleCourseManifest');
  6806. $root->setAttribute('version','1.1');
  6807. $root->setAttribute('xmlns','http://www.imsproject.org/xsd/imscp_rootv1p1p2');
  6808. $root->setAttribute('xmlns:adlcp','http://www.adlnet.org/xsd/adlcp_rootv1p2');
  6809. $root->setAttribute('xmlns:xsi','http://www.w3.org/2001/XMLSchema-instance');
  6810. $root->setAttribute('xsi:schemaLocation','http://www.imsproject.org/xsd/imscp_rootv1p1p2 imscp_rootv1p1p2.xsd http://www.imsglobal.org/xsd/imsmd_rootv1p2p1 imsmd_rootv1p2p1.xsd http://www.adlnet.org/xsd/adlcp_rootv1p2 adlcp_rootv1p2.xsd');
  6811. //Build mandatory sub-root container elements
  6812. $metadata = $xmldoc->createElement('metadata');
  6813. $md_schema = $xmldoc->createElement('schema','ADL SCORM');
  6814. $metadata->appendChild($md_schema);
  6815. $md_schemaversion = $xmldoc->createElement('schemaversion','1.2');
  6816. $metadata->appendChild($md_schemaversion);
  6817. $root->appendChild($metadata);
  6818. $organizations = $xmldoc->createElement('organizations');
  6819. $resources = $xmldoc->createElement('resources');
  6820. //Build the only organization we will use in building our learnpaths
  6821. $organizations->setAttribute('default','dokeos_scorm_export');
  6822. $organization = $xmldoc->createElement('organization');
  6823. $organization->setAttribute('identifier','dokeos_scorm_export');
  6824. //to set the title of the SCORM entity (=organization), we take the name given
  6825. //in Dokeos and convert it to HTML entities using the Dokeos charset (not the
  6826. //learning path charset) as it is the encoding that defines how it is stored
  6827. //in the database. Then we convert it to HTML entities again as the "&" character
  6828. //alone is not authorized in XML (must be &amp;)
  6829. //The title is then decoded twice when extracting (see scorm::parse_manifest)
  6830. $org_title = $xmldoc->createElement('title',htmlentities(api_htmlentities($this->get_name(),ENT_QUOTES,$charset)));
  6831. $organization->appendChild($org_title);
  6832. //For each element, add it to the imsmanifest structure, then add it to the zip.
  6833. //Always call the learnpathItem->scorm_export() method to change it to the SCORM
  6834. //format
  6835. $link_updates = array();
  6836. foreach($this->items as $index => $item){
  6837. if(!in_array($item->type , array(TOOL_QUIZ, TOOL_FORUM, TOOL_THREAD, TOOL_LINK, TOOL_STUDENTPUBLICATION)))
  6838. {
  6839. //get included documents from this item
  6840. if($item->type=='sco')
  6841. $inc_docs = $item->get_resources_from_source(null,api_get_path(SYS_COURSE_PATH).api_get_course_path().'/'.'scorm/'.$this->path.'/'.$item->get_path());
  6842. else
  6843. $inc_docs = $item->get_resources_from_source();
  6844. //give a child element <item> to the <organization> element
  6845. $my_item_id = $item->get_id();
  6846. $my_item = $xmldoc->createElement('item');
  6847. $my_item->setAttribute('identifier','ITEM_'.$my_item_id);
  6848. $my_item->setAttribute('identifierref','RESOURCE_'.$my_item_id);
  6849. $my_item->setAttribute('isvisible','true');
  6850. //give a child element <title> to the <item> element
  6851. $my_title = $xmldoc->createElement('title',htmlspecialchars($item->get_title(), ENT_QUOTES, $this->encoding));
  6852. $my_item->appendChild($my_title);
  6853. //give a child element <adlcp:prerequisites> to the <item> element
  6854. $my_prereqs = $xmldoc->createElement('adlcp:prerequisites',$this->get_scorm_prereq_string($my_item_id));
  6855. $my_prereqs->setAttribute('type','aicc_script');
  6856. $my_item->appendChild($my_prereqs);
  6857. //give a child element <adlcp:maxtimeallowed> to the <item> element - not yet supported
  6858. //$xmldoc->createElement('adlcp:maxtimeallowed','');
  6859. //give a child element <adlcp:timelimitaction> to the <item> element - not yet supported
  6860. //$xmldoc->createElement('adlcp:timelimitaction','');
  6861. //give a child element <adlcp:datafromlms> to the <item> element - not yet supported
  6862. //$xmldoc->createElement('adlcp:datafromlms','');
  6863. //give a child element <adlcp:masteryscore> to the <item> element
  6864. $my_masteryscore = $xmldoc->createElement('adlcp:masteryscore',$item->get_mastery_score());
  6865. $my_item->appendChild($my_masteryscore);
  6866. //attach this item to the organization element or hits parent if there is one
  6867. if(!empty($item->parent) && $item->parent!=0)
  6868. {
  6869. $children = $organization->childNodes;
  6870. $possible_parent = &$this->get_scorm_xml_node($children,'ITEM_'.$item->parent);
  6871. if(is_object($possible_parent))
  6872. {
  6873. $possible_parent->appendChild($my_item);
  6874. }
  6875. else
  6876. {
  6877. if($this->debug>0){error_log('Parent ITEM_'.$item->parent.' of item ITEM_'.$my_item_id.' not found');}
  6878. }
  6879. }
  6880. else
  6881. {
  6882. if($this->debug>0){error_log('No parent');}
  6883. $organization->appendChild($my_item);
  6884. }
  6885. //get the path of the file(s) from the course directory root
  6886. $my_file_path = $item->get_file_path('scorm/'.$this->path.'/');
  6887. $my_xml_file_path = api_htmlentities($my_file_path, ENT_QUOTES, $this->encoding);
  6888. $my_sub_dir = dirname($my_file_path);
  6889. $my_xml_sub_dir = api_htmlentities($my_sub_dir, ENT_QUOTES, $this->encoding);
  6890. //give a <resource> child to the <resources> element
  6891. $my_resource = $xmldoc->createElement('resource');
  6892. $my_resource->setAttribute('identifier','RESOURCE_'.$item->get_id());
  6893. $my_resource->setAttribute('type','webcontent');
  6894. $my_resource->setAttribute('href',$my_xml_file_path);
  6895. //adlcp:scormtype can be either 'sco' or 'asset'
  6896. if($item->type=='sco')
  6897. {
  6898. $my_resource->setAttribute('adlcp:scormtype','sco');
  6899. }
  6900. else
  6901. {
  6902. $my_resource->setAttribute('adlcp:scormtype','asset');
  6903. }
  6904. //xml:base is the base directory to find the files declared in this resource
  6905. $my_resource->setAttribute('xml:base','');
  6906. //give a <file> child to the <resource> element
  6907. $my_file = $xmldoc->createElement('file');
  6908. $my_file->setAttribute('href',$my_xml_file_path);
  6909. $my_resource->appendChild($my_file);
  6910. //dependency to other files - not yet supported
  6911. $i = 1;
  6912. foreach($inc_docs as $doc_info)
  6913. {
  6914. if(count($doc_info)<1 or empty($doc_info[0])){continue;}
  6915. $my_dep = $xmldoc->createElement('resource');
  6916. $res_id = 'RESOURCE_'.$item->get_id().'_'.$i;
  6917. $my_dep->setAttribute('identifier',$res_id);
  6918. $my_dep->setAttribute('type','webcontent');
  6919. $my_dep->setAttribute('adlcp:scormtype','asset');
  6920. $my_dep_file = $xmldoc->createElement('file');
  6921. //check type of URL
  6922. //error_log(__LINE__.'Now dealing with '.$doc_info[0].' of type '.$doc_info[1].'-'.$doc_info[2],0);
  6923. if($doc_info[1] == 'remote')
  6924. { //remote file. Save url as is
  6925. $my_dep_file->setAttribute('href',$doc_info[0]);
  6926. $my_dep->setAttribute('xml:base','');
  6927. }elseif($doc_info[1] == 'local'){
  6928. switch($doc_info[2])
  6929. {
  6930. case 'url': //local URL - save path as url for now, don't zip file
  6931. $abs_path = api_get_path(SYS_PATH).str_replace(api_get_path(WEB_PATH),'',$doc_info[0]);
  6932. $current_dir = dirname($abs_path);
  6933. $file_path = realpath($abs_path);
  6934. $my_dep_file->setAttribute('href',$file_path);
  6935. $my_dep->setAttribute('xml:base','');
  6936. if(strstr($file_path,$main_path) !== false)
  6937. {//the calculated real path is really inside the dokeos root path
  6938. //reduce file path to what's under the DocumentRoot
  6939. $file_path = substr($file_path,strlen($root_path)-1);
  6940. //echo $file_path;echo '<br><br>';
  6941. //error_log(__LINE__.'Reduced url path: '.$file_path,0);
  6942. $zip_files_abs[] = $file_path;
  6943. $link_updates[$my_file_path][] = array('orig'=>$doc_info[0],'dest'=>$file_path);
  6944. $my_dep_file->setAttribute('href',$file_path);
  6945. $my_dep->setAttribute('xml:base','');
  6946. }
  6947. else if (empty($file_path))
  6948. {
  6949. /*$document_root = substr(api_get_path(SYS_PATH), 0, strpos(api_get_path(SYS_PATH),api_get_path(REL_PATH)));
  6950. if(strpos($document_root,-1)=='/')
  6951. {
  6952. $document_root = substr(0, -1, $document_root);
  6953. }*/
  6954. $file_path = $_SERVER['DOCUMENT_ROOT'].$abs_path;
  6955. $file_path = str_replace('//','/',$file_path);
  6956. if(file_exists($file_path))
  6957. {
  6958. $file_path = substr($file_path,strlen($current_dir)); // we get the relative path
  6959. $zip_files[] = $my_sub_dir.'/'.$file_path;
  6960. $link_updates[$my_file_path][] = array('orig'=>$doc_info[0],'dest'=>$file_path);
  6961. $my_dep_file->setAttribute('href',$file_path);
  6962. $my_dep->setAttribute('xml:base','');
  6963. }
  6964. }
  6965. break;
  6966. case 'abs': //absolute path from DocumentRoot. Save file and leave path as is in the zip
  6967. $my_dep_file->setAttribute('href',$doc_info[0]);
  6968. $my_dep->setAttribute('xml:base','');
  6969. //$current_dir = dirname($current_course_path.'/'.$item->get_file_path()).'/';
  6970. //the next lines fix a bug when using the "subdir" mode of Dokeos, whereas
  6971. //an image path would be constructed as /var/www/subdir/subdir/img/foo.bar
  6972. $abs_img_path_without_subdir = $doc_info[0];
  6973. $relp = api_get_path(REL_PATH); //the url-append config param
  6974. $pos = strpos($abs_img_path_without_subdir,$relp);
  6975. if($pos===0)
  6976. {
  6977. $abs_img_path_without_subdir = '/'.substr($abs_img_path_without_subdir,strlen($relp));
  6978. }
  6979. //$file_path = realpath(api_get_path(SYS_PATH).$doc_info[0]);
  6980. $file_path = realpath(api_get_path(SYS_PATH).$abs_img_path_without_subdir);
  6981. $file_path = str_replace('\\','/',$file_path);
  6982. $file_path = str_replace('//','/',$file_path);
  6983. //error_log(__LINE__.'Abs path: '.$file_path,0);
  6984. //prepare the current directory path (until just under 'document') with a trailing slash
  6985. $cur_path = substr($current_course_path,-1)=='/'?$current_course_path:$current_course_path.'/';
  6986. //check if the current document is in that path
  6987. if(strstr($file_path,$cur_path) !== false)
  6988. {
  6989. //the document is in that path, now get the relative path
  6990. //to the containing document
  6991. $orig_file_path = dirname($cur_path.$my_file_path).'/';
  6992. $relative_path ='';
  6993. if(strstr($file_path,$cur_path)!==false)
  6994. {
  6995. $relative_path = substr($file_path,strlen($orig_file_path));
  6996. $file_path = substr($file_path,strlen($cur_path));
  6997. }
  6998. else
  6999. {
  7000. //this case is still a problem as it's difficult to calculate a relative path easily
  7001. //might still generate wrong links
  7002. //$file_path = substr($file_path,strlen($cur_path));
  7003. //calculate the directory path to the current file (without trailing slash)
  7004. $my_relative_path = dirname($file_path);
  7005. $my_relative_file = basename($file_path);
  7006. //calculate the directory path to the containing file (without trailing slash)
  7007. $my_orig_file_path = substr($orig_file_path,0,-1);
  7008. $dotdot = '';
  7009. $subdir = '';
  7010. while(strstr($my_relative_path,$my_orig_file_path)===false && (strlen($my_orig_file_path)>1) && (strlen($my_relative_path)>1))
  7011. {
  7012. $my_relative_path2 = dirname($my_relative_path);
  7013. $my_orig_file_path = dirname($my_orig_file_path);
  7014. $subdir = substr($my_relative_path,strlen($my_relative_path2)+1)."/".$subdir;
  7015. $dotdot += '../';
  7016. $my_relative_path = $my_relative_path2;
  7017. }
  7018. $relative_path = $dotdot.$subdir.$my_relative_file;
  7019. }
  7020. //put the current document in the zip (this array is the array
  7021. //that will manage documents already in the course folder - relative)
  7022. $zip_files[] = $file_path;
  7023. //update the links to the current document in the containing document (make them relative)
  7024. $link_updates[$my_file_path][] = array('orig'=>$doc_info[0],'dest'=>$relative_path);
  7025. $my_dep_file->setAttribute('href',$file_path);
  7026. $my_dep->setAttribute('xml:base','');
  7027. }
  7028. elseif(strstr($file_path,$main_path) !== false)
  7029. {//the calculated real path is really inside the dokeos root path
  7030. //reduce file path to what's under the DocumentRoot
  7031. $file_path = substr($file_path,strlen($root_path));
  7032. //echo $file_path;echo '<br><br>';
  7033. //error_log('Reduced path: '.$file_path,0);
  7034. $zip_files_abs[] = $file_path;
  7035. $link_updates[$my_file_path][] = array('orig'=>$doc_info[0],'dest'=>$file_path);
  7036. $my_dep_file->setAttribute('href','document/'.$file_path);
  7037. $my_dep->setAttribute('xml:base','');
  7038. }
  7039. else if (empty($file_path))
  7040. {
  7041. /*$document_root = substr(api_get_path(SYS_PATH), 0, strpos(api_get_path(SYS_PATH),api_get_path(REL_PATH)));
  7042. if(strpos($document_root,-1)=='/')
  7043. {
  7044. $document_root = substr(0, -1, $document_root);
  7045. }*/
  7046. $file_path = $_SERVER['DOCUMENT_ROOT'].$doc_info[0];
  7047. $file_path = str_replace('//','/',$file_path);
  7048. if(file_exists($file_path))
  7049. {
  7050. $file_path = substr($file_path,strlen($current_dir)); // we get the relative path
  7051. $zip_files[] = $my_sub_dir.'/'.$file_path;
  7052. $link_updates[$my_file_path][] = array('orig'=>$doc_info[0],'dest'=>$file_path);
  7053. $my_dep_file->setAttribute('href','document/'.$file_path);
  7054. $my_dep->setAttribute('xml:base','');
  7055. }
  7056. }
  7057. break;
  7058. case 'rel': //path relative to the current document. Save xml:base as current document's directory and save file in zip as subdir.file_path
  7059. if(substr($doc_info[0],0,2)=='..')
  7060. { //relative path going up
  7061. $current_dir = dirname($current_course_path.'/'.$item->get_file_path()).'/';
  7062. $file_path = realpath($current_dir.$doc_info[0]);
  7063. //error_log($file_path.' <-> '.$main_path,0);
  7064. if(strstr($file_path,$main_path) !== false)
  7065. {//the calculated real path is really inside the dokeos root path
  7066. //reduce file path to what's under the DocumentRoot
  7067. $file_path = substr($file_path,strlen($root_path));
  7068. //error_log('Reduced path: '.$file_path,0);
  7069. $zip_files_abs[] = $file_path;
  7070. $link_updates[$my_file_path][] = array('orig'=>$doc_info[0],'dest'=>$file_path);
  7071. $my_dep_file->setAttribute('href','document/'.$file_path);
  7072. $my_dep->setAttribute('xml:base','');
  7073. }
  7074. }else{
  7075. $zip_files[] = $my_sub_dir.'/'.$doc_info[0];
  7076. $my_dep_file->setAttribute('href',$doc_info[0]);
  7077. $my_dep->setAttribute('xml:base',$my_xml_sub_dir);
  7078. }
  7079. break;
  7080. default:
  7081. $my_dep_file->setAttribute('href',$doc_info[0]);
  7082. $my_dep->setAttribute('xml:base','');
  7083. break;
  7084. }
  7085. }
  7086. $my_dep->appendChild($my_dep_file);
  7087. $resources->appendChild($my_dep);
  7088. $dependency = $xmldoc->createElement('dependency');
  7089. $dependency->setAttribute('identifierref',$res_id);
  7090. $my_resource->appendChild($dependency);
  7091. $i++;
  7092. }
  7093. //$my_dependency = $xmldoc->createElement('dependency');
  7094. //$my_dependency->setAttribute('identifierref','');
  7095. $resources->appendChild($my_resource);
  7096. $zip_files[] = $my_file_path;
  7097. //error_log('File '.$my_file_path. ' added to $zip_files',0);
  7098. }
  7099. else
  7100. { // if the item is a quiz or a link or whatever non-exportable, we include a step indicating it
  7101. if($item->type == TOOL_LINK)
  7102. {
  7103. $my_item = $xmldoc->createElement('item');
  7104. $my_item->setAttribute('identifier','ITEM_'.$item->get_id());
  7105. $my_item->setAttribute('identifierref','RESOURCE_'.$item->get_id());
  7106. $my_item->setAttribute('isvisible','true');
  7107. //give a child element <title> to the <item> element
  7108. $my_title = $xmldoc->createElement('title',htmlspecialchars($item->get_title(),ENT_QUOTES));
  7109. $my_item->appendChild($my_title);
  7110. //give a child element <adlcp:prerequisites> to the <item> element
  7111. $my_prereqs = $xmldoc->createElement('adlcp:prerequisites',$item->get_prereq_string());
  7112. $my_prereqs->setAttribute('type','aicc_script');
  7113. $my_item->appendChild($my_prereqs);
  7114. //give a child element <adlcp:maxtimeallowed> to the <item> element - not yet supported
  7115. //$xmldoc->createElement('adlcp:maxtimeallowed','');
  7116. //give a child element <adlcp:timelimitaction> to the <item> element - not yet supported
  7117. //$xmldoc->createElement('adlcp:timelimitaction','');
  7118. //give a child element <adlcp:datafromlms> to the <item> element - not yet supported
  7119. //$xmldoc->createElement('adlcp:datafromlms','');
  7120. //give a child element <adlcp:masteryscore> to the <item> element
  7121. $my_masteryscore = $xmldoc->createElement('adlcp:masteryscore',$item->get_mastery_score());
  7122. $my_item->appendChild($my_masteryscore);
  7123. //attach this item to the organization element or its parent if there is one
  7124. if(!empty($item->parent) && $item->parent!=0)
  7125. {
  7126. $children = $organization->childNodes;
  7127. for($i=0;$i<$children->length;$i++){
  7128. $item_temp = $children->item($i);
  7129. if ($item_temp -> nodeName == 'item')
  7130. {
  7131. if($item_temp->getAttribute('identifier') == 'ITEM_'.$item->parent)
  7132. {
  7133. $item_temp -> appendChild($my_item);
  7134. }
  7135. }
  7136. }
  7137. }
  7138. else
  7139. {
  7140. $organization->appendChild($my_item);
  7141. }
  7142. $my_file_path = 'link_'.$item->get_id().'.html';
  7143. $sql = 'SELECT url, title FROM '.Database :: get_course_table(TABLE_LINK).' WHERE id='.$item->path;
  7144. $rs = api_sql_query($sql, __FILE__, __LINE__);
  7145. if($link = Database :: fetch_array($rs))
  7146. {
  7147. $url = $link['url'];
  7148. $title = stripslashes($link['title']);
  7149. $links_to_create[$my_file_path] = array('title'=>$title,'url'=>$url);
  7150. $my_xml_file_path = api_htmlentities($my_file_path, ENT_QUOTES, $this->encoding);
  7151. $my_sub_dir = dirname($my_file_path);
  7152. $my_xml_sub_dir = api_htmlentities($my_sub_dir, ENT_QUOTES, $this->encoding);
  7153. //give a <resource> child to the <resources> element
  7154. $my_resource = $xmldoc->createElement('resource');
  7155. $my_resource->setAttribute('identifier','RESOURCE_'.$item->get_id());
  7156. $my_resource->setAttribute('type','webcontent');
  7157. $my_resource->setAttribute('href',$my_xml_file_path);
  7158. //adlcp:scormtype can be either 'sco' or 'asset'
  7159. $my_resource->setAttribute('adlcp:scormtype','asset');
  7160. //xml:base is the base directory to find the files declared in this resource
  7161. $my_resource->setAttribute('xml:base','');
  7162. //give a <file> child to the <resource> element
  7163. $my_file = $xmldoc->createElement('file');
  7164. $my_file->setAttribute('href',$my_xml_file_path);
  7165. $my_resource->appendChild($my_file);
  7166. $resources->appendChild($my_resource);
  7167. }
  7168. }
  7169. elseif($item->type == TOOL_QUIZ)
  7170. {
  7171. require_once(api_get_path(SYS_CODE_PATH).'exercice/exercise.class.php');
  7172. $exe_id = $item->path; //should be using ref when everything will be cleaned up in this regard
  7173. $exe = new Exercise();
  7174. $exe->read($exe_id);
  7175. $my_item = $xmldoc->createElement('item');
  7176. $my_item->setAttribute('identifier','ITEM_'.$item->get_id());
  7177. $my_item->setAttribute('identifierref','RESOURCE_'.$item->get_id());
  7178. $my_item->setAttribute('isvisible','true');
  7179. //give a child element <title> to the <item> element
  7180. $my_title = $xmldoc->createElement('title',htmlspecialchars($item->get_title(), ENT_QUOTES, $this->encoding));
  7181. $my_item->appendChild($my_title);
  7182. $my_max_score = $xmldoc->createElement('max_score',$item->get_max());
  7183. $my_item->appendChild($my_max_score);
  7184. //give a child element <adlcp:prerequisites> to the <item> element
  7185. $my_prereqs = $xmldoc->createElement('adlcp:prerequisites',$item->get_prereq_string());
  7186. $my_prereqs->setAttribute('type','aicc_script');
  7187. $my_item->appendChild($my_prereqs);
  7188. //give a child element <adlcp:masteryscore> to the <item> element
  7189. $my_masteryscore = $xmldoc->createElement('adlcp:masteryscore',$item->get_mastery_score());
  7190. $my_item->appendChild($my_masteryscore);
  7191. //attach this item to the organization element or hits parent if there is one
  7192. if(!empty($item->parent) && $item->parent!=0)
  7193. {
  7194. $children = $organization->childNodes;
  7195. for($i=0;$i<$children->length;$i++){
  7196. $item_temp = $children->item($i);
  7197. if ($item_temp -> nodeName == 'item')
  7198. {
  7199. if($item_temp->getAttribute('identifier') == 'ITEM_'.$item->parent)
  7200. {
  7201. $item_temp -> appendChild($my_item);
  7202. }
  7203. }
  7204. }
  7205. }
  7206. else
  7207. {
  7208. $organization->appendChild($my_item);
  7209. }
  7210. //include export scripts
  7211. require_once(api_get_path(SYS_CODE_PATH).'exercice/export/scorm/scorm_export.php');
  7212. //get the path of the file(s) from the course directory root
  7213. //$my_file_path = $item->get_file_path('scorm/'.$this->path.'/');
  7214. $my_file_path = 'quiz_'.$item->get_id().'.html';
  7215. //write the contents of the exported exercise into a (big) html file
  7216. //to later pack it into the exported SCORM. The file will be removed afterwards
  7217. $contents = export_exercise($exe_id,true);
  7218. $tmp_file_path = $archive_path.$temp_dir_short.'/'.$my_file_path;
  7219. $res = file_put_contents($tmp_file_path,$contents);
  7220. if($res === false){error_log('Could not write into file '.$tmp_file_path.' '.__FILE__.' '.__LINE__,0);}
  7221. $files_cleanup[] = $tmp_file_path;
  7222. //error_log($tmp_path);die();
  7223. $my_xml_file_path = api_htmlentities($my_file_path, ENT_QUOTES, $this->encoding);
  7224. $my_sub_dir = dirname($my_file_path);
  7225. $my_xml_sub_dir = api_htmlentities($my_sub_dir, ENT_QUOTES, $this->encoding);
  7226. //give a <resource> child to the <resources> element
  7227. $my_resource = $xmldoc->createElement('resource');
  7228. $my_resource->setAttribute('identifier','RESOURCE_'.$item->get_id());
  7229. $my_resource->setAttribute('type','webcontent');
  7230. $my_resource->setAttribute('href',$my_xml_file_path);
  7231. //adlcp:scormtype can be either 'sco' or 'asset'
  7232. $my_resource->setAttribute('adlcp:scormtype','sco');
  7233. //xml:base is the base directory to find the files declared in this resource
  7234. $my_resource->setAttribute('xml:base','');
  7235. //give a <file> child to the <resource> element
  7236. $my_file = $xmldoc->createElement('file');
  7237. $my_file->setAttribute('href',$my_xml_file_path);
  7238. $my_resource->appendChild($my_file);
  7239. //get included docs
  7240. $inc_docs = $item->get_resources_from_source(null,$tmp_file_path);
  7241. //dependency to other files - not yet supported
  7242. $i = 1;
  7243. foreach($inc_docs as $doc_info)
  7244. {
  7245. if(count($doc_info)<1 or empty($doc_info[0])){continue;}
  7246. $my_dep = $xmldoc->createElement('resource');
  7247. $res_id = 'RESOURCE_'.$item->get_id().'_'.$i;
  7248. $my_dep->setAttribute('identifier',$res_id);
  7249. $my_dep->setAttribute('type','webcontent');
  7250. $my_dep->setAttribute('adlcp:scormtype','asset');
  7251. $my_dep_file = $xmldoc->createElement('file');
  7252. //check type of URL
  7253. //error_log(__LINE__.'Now dealing with '.$doc_info[0].' of type '.$doc_info[1].'-'.$doc_info[2],0);
  7254. if($doc_info[1] == 'remote')
  7255. { //remote file. Save url as is
  7256. $my_dep_file->setAttribute('href',$doc_info[0]);
  7257. $my_dep->setAttribute('xml:base','');
  7258. }elseif($doc_info[1] == 'local'){
  7259. switch($doc_info[2])
  7260. {
  7261. case 'url': //local URL - save path as url for now, don't zip file
  7262. //save file but as local file (retrieve from URL)
  7263. $abs_path = api_get_path(SYS_PATH).str_replace(api_get_path(WEB_PATH),'',$doc_info[0]);
  7264. $current_dir = dirname($abs_path);
  7265. $file_path = realpath($abs_path);
  7266. $my_dep_file->setAttribute('href','document/'.$file_path);
  7267. $my_dep->setAttribute('xml:base','');
  7268. if(strstr($file_path,$main_path) !== false)
  7269. {//the calculated real path is really inside the dokeos root path
  7270. //reduce file path to what's under the DocumentRoot
  7271. $file_path = substr($file_path,strlen($root_path));
  7272. //echo $file_path;echo '<br><br>';
  7273. //error_log('Reduced path: '.$file_path,0);
  7274. $zip_files_abs[] = $file_path;
  7275. $link_updates[$my_file_path][] = array('orig'=>$doc_info[0],'dest'=>'document/'.$file_path);
  7276. $my_dep_file->setAttribute('href','document/'.$file_path);
  7277. $my_dep->setAttribute('xml:base','');
  7278. }
  7279. else if (empty($file_path))
  7280. {
  7281. /*$document_root = substr(api_get_path(SYS_PATH), 0, strpos(api_get_path(SYS_PATH),api_get_path(REL_PATH)));
  7282. if(strpos($document_root,-1)=='/')
  7283. {
  7284. $document_root = substr(0, -1, $document_root);
  7285. }*/
  7286. $file_path = $_SERVER['DOCUMENT_ROOT'].$abs_path;
  7287. $file_path = str_replace('//','/',$file_path);
  7288. if(file_exists($file_path))
  7289. {
  7290. $file_path = substr($file_path,strlen($current_dir)); // we get the relative path
  7291. $zip_files[] = $my_sub_dir.'/'.$file_path;
  7292. $link_updates[$my_file_path][] = array('orig'=>$doc_info[0],'dest'=>'document/'.$file_path);
  7293. $my_dep_file->setAttribute('href','document/'.$file_path);
  7294. $my_dep->setAttribute('xml:base','');
  7295. }
  7296. }
  7297. break;
  7298. case 'abs': //absolute path from DocumentRoot. Save file and leave path as is in the zip
  7299. $current_dir = dirname($current_course_path.'/'.$item->get_file_path()).'/';
  7300. $file_path = realpath($doc_info[0]);
  7301. $my_dep_file->setAttribute('href',$file_path);
  7302. $my_dep->setAttribute('xml:base','');
  7303. if(strstr($file_path,$main_path) !== false)
  7304. {//the calculated real path is really inside the dokeos root path
  7305. //reduce file path to what's under the DocumentRoot
  7306. $file_path = substr($file_path,strlen($root_path));
  7307. //echo $file_path;echo '<br><br>';
  7308. //error_log('Reduced path: '.$file_path,0);
  7309. $zip_files_abs[] = $file_path;
  7310. $link_updates[$my_file_path][] = array('orig'=>$doc_info[0],'dest'=>$file_path);
  7311. $my_dep_file->setAttribute('href','document/'.$file_path);
  7312. $my_dep->setAttribute('xml:base','');
  7313. }
  7314. else if (empty($file_path))
  7315. {
  7316. /*$document_root = substr(api_get_path(SYS_PATH), 0, strpos(api_get_path(SYS_PATH),api_get_path(REL_PATH)));
  7317. if(strpos($document_root,-1)=='/')
  7318. {
  7319. $document_root = substr(0, -1, $document_root);
  7320. }*/
  7321. $file_path = $_SERVER['DOCUMENT_ROOT'].$doc_info[0];
  7322. $file_path = str_replace('//','/',$file_path);
  7323. if(file_exists($file_path))
  7324. {
  7325. $file_path = substr($file_path,strlen($current_dir)); // we get the relative path
  7326. $zip_files[] = $my_sub_dir.'/'.$file_path;
  7327. $link_updates[$my_file_path][] = array('orig'=>$doc_info[0],'dest'=>$file_path);
  7328. $my_dep_file->setAttribute('href','document/'.$file_path);
  7329. $my_dep->setAttribute('xml:base','');
  7330. }
  7331. }
  7332. break;
  7333. case 'rel': //path relative to the current document. Save xml:base as current document's directory and save file in zip as subdir.file_path
  7334. if(substr($doc_info[0],0,2)=='..')
  7335. { //relative path going up
  7336. $current_dir = dirname($current_course_path.'/'.$item->get_file_path()).'/';
  7337. $file_path = realpath($current_dir.$doc_info[0]);
  7338. //error_log($file_path.' <-> '.$main_path,0);
  7339. if(strstr($file_path,$main_path) !== false)
  7340. {//the calculated real path is really inside the dokeos root path
  7341. //reduce file path to what's under the DocumentRoot
  7342. $file_path = substr($file_path,strlen($root_path));
  7343. $file_path_dest=$file_path;
  7344. //file path is courses/DOKEOS/document/....
  7345. $info_file_path=explode('/',$file_path);
  7346. if ($info_file_path[0]=='courses') {//add character "/" in file path
  7347. $file_path_dest='/'.$file_path;
  7348. }
  7349. //error_log('Reduced path: '.$file_path,0);
  7350. $zip_files_abs[] = $file_path;
  7351. $link_updates[$my_file_path][] = array('orig'=>$doc_info[0],'dest'=>$file_path_dest);
  7352. $my_dep_file->setAttribute('href','document/'.$file_path);
  7353. $my_dep->setAttribute('xml:base','');
  7354. }
  7355. }else{
  7356. $zip_files[] = $my_sub_dir.'/'.$doc_info[0];
  7357. $my_dep_file->setAttribute('href',$doc_info[0]);
  7358. $my_dep->setAttribute('xml:base',$my_xml_sub_dir);
  7359. }
  7360. break;
  7361. default:
  7362. $my_dep_file->setAttribute('href',$doc_info[0]);// ../../courses/
  7363. $my_dep->setAttribute('xml:base','');
  7364. break;
  7365. }
  7366. }
  7367. $my_dep->appendChild($my_dep_file);
  7368. $resources->appendChild($my_dep);
  7369. $dependency = $xmldoc->createElement('dependency');
  7370. $dependency->setAttribute('identifierref',$res_id);
  7371. $my_resource->appendChild($dependency);
  7372. $i++;
  7373. }
  7374. $resources->appendChild($my_resource);
  7375. $zip_files[] = $my_file_path;
  7376. }
  7377. else
  7378. {
  7379. //get the path of the file(s) from the course directory root
  7380. $my_file_path = 'non_exportable.html';
  7381. $my_xml_file_path = api_htmlentities($my_file_path, ENT_COMPAT, $this->encoding);
  7382. $my_sub_dir = dirname($my_file_path);
  7383. $my_xml_sub_dir = api_htmlentities($my_sub_dir, ENT_COMPAT, $this->encoding);
  7384. //give a <resource> child to the <resources> element
  7385. $my_resource = $xmldoc->createElement('resource');
  7386. $my_resource->setAttribute('identifier','RESOURCE_'.$item->get_id());
  7387. $my_resource->setAttribute('type','webcontent');
  7388. $my_resource->setAttribute('href','document/'.$my_xml_file_path);
  7389. //adlcp:scormtype can be either 'sco' or 'asset'
  7390. $my_resource->setAttribute('adlcp:scormtype','asset');
  7391. //xml:base is the base directory to find the files declared in this resource
  7392. $my_resource->setAttribute('xml:base','');
  7393. //give a <file> child to the <resource> element
  7394. $my_file = $xmldoc->createElement('file');
  7395. $my_file->setAttribute('href','document/'.$my_xml_file_path);
  7396. $my_resource->appendChild($my_file);
  7397. $resources->appendChild($my_resource);
  7398. }
  7399. }
  7400. }
  7401. $organizations->appendChild($organization);
  7402. $root->appendChild($organizations);
  7403. $root->appendChild($resources);
  7404. $xmldoc->appendChild($root);
  7405. //todo: add a readme file here, with a short description and a link to the Reload player
  7406. //then add the file to the zip, then destroy the file (this is done automatically)
  7407. // http://www.reload.ac.uk/scormplayer.html - once done, don't forget to close FS#138
  7408. //error_log(print_r($zip_files,true),0);
  7409. foreach($zip_files as $file_path)
  7410. {
  7411. if(empty($file_path)){continue;}
  7412. //error_log(__LINE__.'getting document from '.$sys_course_path.$_course['path'].'/'.$file_path.' removing '.$sys_course_path.$_course['path'].'/',0);
  7413. $dest_file = $archive_path.$temp_dir_short.'/'.$file_path;
  7414. $this->create_path($dest_file);
  7415. //error_log('copy '.api_get_path('SYS_COURSE_PATH').$_course['path'].'/'.$file_path.' to '.api_get_path('SYS_ARCHIVE_PATH').$temp_dir_short.'/'.$file_path,0);
  7416. //echo $main_path.$file_path.'<br>';
  7417. @copy($sys_course_path.$_course['path'].'/'.$file_path,$dest_file);
  7418. //check if the file needs a link update
  7419. if(in_array($file_path,array_keys($link_updates))){
  7420. $string = file_get_contents($dest_file);
  7421. unlink($dest_file);
  7422. foreach($link_updates[$file_path] as $old_new)
  7423. {
  7424. //error_log('Replacing '.$old_new['orig'].' by '.$old_new['dest'].' in '.$file_path,0);
  7425. //this is an ugly hack that allows .flv files to be found by the flv player that
  7426. // will be added in document/main/inc/lib/flv_player/flv_player.swf and that needs
  7427. // to find the flv to play in document/main/, so we replace main/ in the flv path by
  7428. // ../../.. to return from inc/lib/flv_player to the document/main path
  7429. if(substr($old_new['dest'],-3)=='flv' && substr($old_new['dest'],0,5)=='main/')
  7430. {
  7431. $old_new['dest'] = str_replace('main/','../../../',$old_new['dest']);
  7432. }
  7433. elseif(substr($old_new['dest'],-3)=='flv' && substr($old_new['dest'],0,6)=='video/')
  7434. {
  7435. $old_new['dest'] = str_replace('video/','../../../../video/',$old_new['dest']);
  7436. }
  7437. $string = str_replace($old_new['orig'],$old_new['dest'],$string);
  7438. }
  7439. file_put_contents($dest_file,$string);
  7440. }
  7441. }
  7442. foreach($zip_files_abs as $file_path)
  7443. {
  7444. if(empty($file_path)){continue;}
  7445. //error_log(__LINE__.'checking existence of '.$main_path.$file_path.'',0);
  7446. if(!is_file($main_path.$file_path) || !is_readable($main_path.$file_path)){continue;}
  7447. //error_log(__LINE__.'getting document from '.$main_path.$file_path.' removing '.api_get_path('SYS_COURSE_PATH').$_course['path'].'/',0);
  7448. $dest_file = $archive_path.$temp_dir_short.'/document/'.$file_path;
  7449. $this->create_path($dest_file);
  7450. //error_log('Created path '.api_get_path(SYS_ARCHIVE_PATH).$temp_dir_short.'/document/'.$file_path,0);
  7451. //error_log('copy '.api_get_path(SYS_COURSE_PATH).$_course['path'].'/'.$file_path.' to '.api_get_path(SYS_ARCHIVE_PATH).$temp_dir_short.'/'.$file_path,0);
  7452. //echo $main_path.$file_path.' - '.$dest_file.'<br>';
  7453. copy($main_path.$file_path,$dest_file);
  7454. //check if the file needs a link update
  7455. if(in_array($file_path,array_keys($link_updates)))
  7456. {
  7457. $string = file_get_contents($dest_file);
  7458. unlink($dest_file);
  7459. foreach($link_updates[$file_path] as $old_new)
  7460. {
  7461. //error_log('Replacing '.$old_new['orig'].' by '.$old_new['dest'].' in '.$file_path,0);
  7462. //this is an ugly hack that allows .flv files to be found by the flv player that
  7463. // will be added in document/main/inc/lib/flv_player/flv_player.swf and that needs
  7464. // to find the flv to play in document/main/, so we replace main/ in the flv path by
  7465. // ../../.. to return from inc/lib/flv_player to the document/main path
  7466. if(substr($old_new['dest'],-3)=='flv' && substr($old_new['dest'],0,5)=='main/')
  7467. {
  7468. $old_new['dest'] = str_replace('main/','../../../',$old_new['dest']);
  7469. }
  7470. $string = str_replace($old_new['orig'],$old_new['dest'],$string);
  7471. }
  7472. file_put_contents($dest_file,$string);
  7473. }
  7474. }
  7475. if(is_array($links_to_create))
  7476. {
  7477. foreach($links_to_create as $file=>$link)
  7478. {
  7479. $file_content = '<html><body><div style="text-align:center"><a href="'.$link['url'].'">'.$link['title'].'</a></div></body></html>';
  7480. file_put_contents($archive_path.$temp_dir_short.'/'.$file, $file_content);
  7481. }
  7482. }
  7483. // add non exportable message explanation
  7484. $lang_not_exportable = get_lang('ThisItemIsNotExportable');
  7485. $file_content =
  7486. <<<EOD
  7487. <html>
  7488. <head>
  7489. <style>
  7490. .error-message {
  7491. font-family: arial, verdana, helvetica, sans-serif;
  7492. border-width: 1px;
  7493. border-style: solid;
  7494. left: 50%;
  7495. margin: 10px auto;
  7496. min-height: 30px;
  7497. padding: 5px;
  7498. right: 50%;
  7499. width: 500px;
  7500. background-color: #FFD1D1;
  7501. border-color: #FF0000;
  7502. color: #000;
  7503. }
  7504. </style>
  7505. <body>
  7506. <div class="error-message">
  7507. $lang_not_exportable
  7508. </div>
  7509. </body>
  7510. </html>
  7511. EOD;
  7512. if(!is_dir($archive_path.$temp_dir_short.'/document'))
  7513. {
  7514. @mkdir($archive_path.$temp_dir_short.'/document');
  7515. }
  7516. file_put_contents($archive_path.$temp_dir_short.'/document/non_exportable.html', $file_content);
  7517. //Add the extra files that go along with a SCORM package
  7518. $main_code_path = api_get_path(SYS_CODE_PATH).'newscorm/packaging/';
  7519. $extra_files = scandir($main_code_path);
  7520. foreach ($extra_files as $extra_file) {
  7521. if (strpos($extra_file, '.') === 0)
  7522. continue;
  7523. else {
  7524. $dest_file = $archive_path . $temp_dir_short . '/' . $extra_file;
  7525. $this->create_path($dest_file);
  7526. copy($main_code_path.$extra_file,$dest_file);
  7527. }
  7528. }
  7529. //Finalize the imsmanifest structure, add to the zip, then return the zip
  7530. $xmldoc->save($archive_path.'/'.$temp_dir_short.'/imsmanifest.xml');
  7531. $zip_folder->add($archive_path.'/'.$temp_dir_short, PCLZIP_OPT_REMOVE_PATH, $archive_path.'/'.$temp_dir_short.'/');
  7532. //clean possible temporary files
  7533. foreach($files_cleanup as $file)
  7534. {
  7535. $res = unlink($file);
  7536. if($res === false){error_log('Could not delete temp file '.$file.' '.__FILE__.' '.__LINE__,0);}
  7537. }
  7538. //Send file to client
  7539. //$name = 'scorm_export_'.$this->lp_id.'.zip';
  7540. require_once(api_get_path(LIBRARY_PATH).'fileUpload.lib.php');
  7541. $name = preg_replace('([^a-zA-Z0-9_\.])','-',html_entity_decode($this->get_name(),ENT_QUOTES)).'.zip';
  7542. DocumentManager::file_send_for_download($temp_zip_file,true,$name);
  7543. }
  7544. /**
  7545. * Temp function to be moved in main_api or the best place around for this. Creates a file path
  7546. * if it doesn't exist
  7547. */
  7548. function create_path($path) {
  7549. $path_bits = split('/', dirname($path));
  7550. // IS_WINDOWS_OS has been defined in main_api.lib.php
  7551. $path_built = IS_WINDOWS_OS ? '' : '/';
  7552. foreach ($path_bits as $bit) {
  7553. if (!empty ($bit)) {
  7554. $new_path = $path_built . $bit;
  7555. if (is_dir($new_path)) {
  7556. $path_built = $new_path . '/';
  7557. } else {
  7558. mkdir($new_path);
  7559. $path_built = $new_path . '/';
  7560. }
  7561. }
  7562. }
  7563. }
  7564. /**
  7565. * Delete the image relative to this learning path. No parameter. Only works on instanciated object.
  7566. * @return boolean The results of the unlink function, or false if there was no image to start with
  7567. */
  7568. function delete_lp_image() {
  7569. $img = $this->get_preview_image();
  7570. if ($img != '') {
  7571. $del_file = api_get_path(SYS_COURSE_PATH) . api_get_course_path() . '/upload/learning_path/images/' . $img;
  7572. $this->set_preview_image('');
  7573. return @ unlink($del_file);
  7574. } else {
  7575. return false;
  7576. }
  7577. }
  7578. /**
  7579. * Uploads an author image to the upload/learning_path/images directory
  7580. * @param array The image array, coming from the $_FILES superglobal
  7581. * @return boolean True on success, false on error
  7582. */
  7583. function upload_image($image_array) {
  7584. $image_moved = false;
  7585. if (!empty ($image_array['name'])) {
  7586. $upload_ok = process_uploaded_file($image_array);
  7587. $has_attachment = true;
  7588. } else {
  7589. $image_moved = true;
  7590. }
  7591. if ($upload_ok) {
  7592. if ($has_attachment) {
  7593. $courseDir = api_get_course_path() . '/upload/learning_path/images';
  7594. $sys_course_path = api_get_path(SYS_COURSE_PATH);
  7595. $updir = $sys_course_path . $courseDir;
  7596. // Try to add an extension to the file if it hasn't one
  7597. $new_file_name = add_ext_on_mime(stripslashes($image_array['name']), $image_array['type']);
  7598. if (!filter_extension($new_file_name)) {
  7599. //Display :: display_error_message(get_lang('UplUnableToSaveFileFilteredExtension'));
  7600. $image_moved = false;
  7601. } else {
  7602. $file_extension = explode('.', $image_array['name']);
  7603. $file_extension = strtolower($file_extension[sizeof($file_extension) - 1]);
  7604. $new_file_name = uniqid('') . '.' . $file_extension;
  7605. $new_path = $updir . '/' . $new_file_name;
  7606. //$result= @move_uploaded_file($image_array['tmp_name'], $new_path);
  7607. // resize the image
  7608. include_once (api_get_path(LIBRARY_PATH) . 'image.lib.php');
  7609. $temp = new image($image_array['tmp_name']);
  7610. $picture_infos=@getimagesize($image_array['tmp_name']); // $picture_infos[0]-> width
  7611. if ($picture_infos[0] > 104) {
  7612. $thumbwidth = 104;
  7613. } else {
  7614. $thumbwidth = $picture_infos[0];
  7615. }
  7616. if ($picture_infos[1] > 96) {
  7617. $new_height = 96;
  7618. } else {
  7619. $new_height = $picture_infos[1];
  7620. }
  7621. //$new_height = round(($thumbwidth/$picture_infos[0])*$picture_infos[1]);
  7622. $temp->resize($thumbwidth,$new_height,0);
  7623. $type=$picture_infos[2];
  7624. $result=false;
  7625. switch ($type)
  7626. {
  7627. case 2 :
  7628. $result=$temp->send_image('JPG',$new_path);
  7629. break;
  7630. case 3 :
  7631. $result=$temp->send_image('PNG',$new_path);
  7632. break;
  7633. case 1 :
  7634. $result=$temp->send_image('GIF',$new_path);
  7635. break;
  7636. }
  7637. $temp->resize($thumbwidth, $new_height, 0);
  7638. $type = $picture_infos[2];
  7639. $result = false;
  7640. switch ($type) {
  7641. case 2 :
  7642. $result = $temp->send_image('JPG', $new_path);
  7643. break;
  7644. case 3 :
  7645. $result = $temp->send_image('PNG', $new_path);
  7646. break;
  7647. case 1 :
  7648. $result = $temp->send_image('GIF', $new_path);
  7649. break;
  7650. }
  7651. // Storing the image filename
  7652. if ($result) {
  7653. $image_moved = true;
  7654. $this->set_preview_image($new_file_name);
  7655. return true;
  7656. }
  7657. }
  7658. }
  7659. }
  7660. return false;
  7661. }
  7662. }
  7663. if (!function_exists('trim_value')) {
  7664. function trim_value(& $value) {
  7665. $value = trim($value);
  7666. }
  7667. }