functions.php 189 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585458645874588458945904591459245934594459545964597459845994600460146024603460446054606460746084609461046114612461346144615461646174618461946204621462246234624462546264627462846294630463146324633463446354636463746384639464046414642464346444645464646474648464946504651465246534654465546564657465846594660466146624663466446654666466746684669467046714672467346744675467646774678467946804681468246834684468546864687468846894690469146924693469446954696469746984699470047014702470347044705470647074708470947104711471247134714471547164717471847194720472147224723472447254726472747284729473047314732473347344735473647374738473947404741474247434744474547464747474847494750475147524753475447554756475747584759476047614762476347644765476647674768476947704771477247734774477547764777477847794780478147824783478447854786478747884789479047914792479347944795479647974798479948004801480248034804480548064807480848094810481148124813481448154816481748184819482048214822482348244825482648274828482948304831483248334834483548364837483848394840484148424843484448454846484748484849485048514852485348544855485648574858485948604861486248634864486548664867486848694870487148724873487448754876487748784879488048814882488348844885488648874888488948904891489248934894489548964897489848994900490149024903490449054906490749084909491049114912491349144915491649174918491949204921492249234924492549264927492849294930493149324933493449354936493749384939494049414942494349444945494649474948494949504951495249534954495549564957495849594960496149624963496449654966496749684969497049714972497349744975497649774978497949804981498249834984498549864987498849894990499149924993499449954996499749984999500050015002500350045005500650075008500950105011501250135014501550165017501850195020502150225023502450255026502750285029503050315032503350345035503650375038503950405041504250435044504550465047504850495050505150525053505450555056505750585059506050615062506350645065506650675068506950705071507250735074507550765077507850795080508150825083508450855086508750885089509050915092509350945095509650975098509951005101510251035104510551065107510851095110511151125113511451155116511751185119512051215122512351245125512651275128512951305131513251335134513551365137513851395140514151425143514451455146514751485149515051515152515351545155515651575158515951605161516251635164516551665167516851695170517151725173517451755176517751785179518051815182518351845185518651875188518951905191519251935194519551965197519851995200520152025203520452055206520752085209521052115212521352145215521652175218521952205221522252235224522552265227522852295230523152325233523452355236523752385239524052415242524352445245524652475248524952505251525252535254525552565257525852595260526152625263526452655266526752685269527052715272527352745275527652775278527952805281528252835284528552865287528852895290529152925293529452955296529752985299530053015302530353045305530653075308530953105311531253135314531553165317531853195320532153225323532453255326532753285329533053315332533353345335533653375338533953405341534253435344534553465347534853495350535153525353535453555356535753585359536053615362536353645365536653675368536953705371537253735374537553765377537853795380538153825383538453855386538753885389539053915392539353945395539653975398539954005401540254035404540554065407540854095410541154125413541454155416541754185419542054215422542354245425542654275428542954305431543254335434543554365437543854395440544154425443544454455446544754485449545054515452545354545455545654575458545954605461546254635464546554665467546854695470547154725473547454755476547754785479548054815482548354845485548654875488548954905491549254935494549554965497549854995500550155025503550455055506550755085509551055115512551355145515551655175518551955205521552255235524552555265527552855295530553155325533553455355536553755385539554055415542554355445545554655475548554955505551555255535554555555565557555855595560556155625563556455655566556755685569557055715572557355745575557655775578557955805581558255835584558555865587558855895590559155925593559455955596559755985599560056015602560356045605560656075608560956105611561256135614561556165617561856195620562156225623562456255626562756285629563056315632563356345635563656375638563956405641564256435644564556465647564856495650565156525653565456555656565756585659566056615662566356645665566656675668566956705671567256735674567556765677567856795680568156825683568456855686568756885689569056915692569356945695569656975698569957005701570257035704570557065707570857095710571157125713571457155716571757185719572057215722572357245725572657275728572957305731573257335734573557365737573857395740574157425743574457455746574757485749575057515752575357545755575657575758575957605761576257635764576557665767576857695770577157725773577457755776577757785779578057815782578357845785578657875788578957905791579257935794579557965797579857995800580158025803580458055806580758085809581058115812581358145815581658175818581958205821582258235824582558265827582858295830583158325833583458355836583758385839584058415842584358445845584658475848584958505851585258535854585558565857585858595860586158625863586458655866586758685869587058715872587358745875587658775878587958805881588258835884588558865887588858895890589158925893589458955896589758985899590059015902590359045905590659075908590959105911591259135914591559165917591859195920592159225923592459255926592759285929593059315932593359345935593659375938593959405941594259435944594559465947594859495950595159525953595459555956595759585959596059615962596359645965596659675968596959705971597259735974597559765977597859795980598159825983598459855986598759885989599059915992599359945995599659975998599960006001600260036004600560066007600860096010601160126013601460156016601760186019602060216022602360246025602660276028602960306031603260336034603560366037603860396040604160426043604460456046604760486049605060516052605360546055605660576058605960606061606260636064606560666067606860696070607160726073607460756076607760786079608060816082608360846085608660876088608960906091609260936094609560966097609860996100610161026103610461056106610761086109611061116112611361146115611661176118611961206121612261236124612561266127612861296130613161326133613461356136613761386139614061416142614361446145614661476148614961506151615261536154615561566157615861596160616161626163616461656166616761686169617061716172617361746175617661776178617961806181618261836184618561866187618861896190619161926193619461956196619761986199620062016202620362046205620662076208620962106211621262136214621562166217621862196220622162226223622462256226622762286229623062316232623362346235623662376238623962406241624262436244624562466247624862496250625162526253625462556256625762586259626062616262626362646265626662676268626962706271627262736274627562766277627862796280628162826283628462856286628762886289629062916292629362946295629662976298629963006301630263036304630563066307630863096310631163126313631463156316631763186319632063216322632363246325632663276328632963306331633263336334633563366337633863396340634163426343634463456346634763486349635063516352635363546355635663576358635963606361636263636364636563666367636863696370637163726373637463756376637763786379638063816382638363846385638663876388638963906391639263936394639563966397639863996400640164026403640464056406640764086409641064116412641364146415641664176418641964206421642264236424642564266427642864296430643164326433643464356436643764386439644064416442644364446445644664476448644964506451645264536454645564566457645864596460646164626463646464656466646764686469647064716472647364746475647664776478647964806481648264836484648564866487648864896490649164926493649464956496649764986499650065016502650365046505650665076508650965106511651265136514651565166517651865196520652165226523652465256526652765286529653065316532653365346535653665376538653965406541654265436544654565466547654865496550655165526553655465556556655765586559656065616562656365646565656665676568656965706571657265736574657565766577657865796580658165826583658465856586658765886589659065916592659365946595659665976598659966006601660266036604660566066607660866096610661166126613661466156616661766186619662066216622662366246625662666276628662966306631663266336634663566366637663866396640664166426643664466456646664766486649665066516652665366546655665666576658665966606661666266636664666566666667666866696670667166726673667466756676667766786679668066816682668366846685668666876688668966906691669266936694669566966697669866996700670167026703670467056706670767086709671067116712671367146715671667176718671967206721672267236724672567266727672867296730673167326733673467356736673767386739674067416742674367446745674667476748674967506751675267536754675567566757675867596760676167626763676467656766676767686769677067716772677367746775677667776778677967806781678267836784678567866787678867896790679167926793679467956796679767986799680068016802680368046805680668076808680968106811681268136814681568166817681868196820682168226823682468256826682768286829683068316832683368346835683668376838683968406841684268436844684568466847684868496850685168526853685468556856685768586859686068616862686368646865686668676868686968706871687268736874687568766877687868796880688168826883688468856886688768886889689068916892689368946895689668976898689969006901690269036904690569066907690869096910691169126913691469156916691769186919692069216922692369246925692669276928692969306931693269336934693569366937693869396940694169426943694469456946694769486949695069516952695369546955695669576958695969606961696269636964696569666967696869696970697169726973697469756976697769786979698069816982698369846985698669876988698969906991699269936994699569966997699869997000700170027003700470057006700770087009701070117012701370147015701670177018701970207021702270237024702570267027702870297030703170327033703470357036703770387039704070417042704370447045704670477048704970507051705270537054705570567057705870597060706170627063706470657066706770687069707070717072
  1. <?php
  2. date_default_timezone_set('UTC');
  3. if (defined('E_DEPRECATED')) {
  4. error_reporting(E_ALL & ~E_NOTICE & ~E_DEPRECATED);
  5. } else {
  6. error_reporting(E_ALL & ~E_NOTICE);
  7. }
  8. require_once 'config.php';
  9. if (DB_TYPE == "pgsql") {
  10. define('SUBSTRING_FOR_DATE', 'SUBSTRING_FOR_DATE');
  11. } else {
  12. define('SUBSTRING_FOR_DATE', 'SUBSTRING');
  13. }
  14. define('THEME_VERSION_REQUIRED', 1.1);
  15. /**
  16. * Return available translations names.
  17. *
  18. * @access public
  19. * @return array A array of available translations.
  20. */
  21. function get_translations() {
  22. $tr = array(
  23. "auto" => "Detect automatically",
  24. "ca_CA" => "Català",
  25. "en_US" => "English",
  26. "es_ES" => "Español",
  27. "de_DE" => "Deutsch",
  28. "fr_FR" => "Français",
  29. "hu_HU" => "Magyar (Hungarian)",
  30. "it_IT" => "Italiano",
  31. "ja_JP" => "日本語 (Japanese)",
  32. "nb_NO" => "Norwegian bokmål",
  33. "ru_RU" => "Русский",
  34. "pt_BR" => "Portuguese/Brazil",
  35. "zh_CN" => "Simplified Chinese");
  36. return $tr;
  37. }
  38. if (ENABLE_TRANSLATIONS == true) { // If translations are enabled.
  39. require_once "lib/accept-to-gettext.php";
  40. require_once "lib/gettext/gettext.inc";
  41. function startup_gettext() {
  42. # Get locale from Accept-Language header
  43. $lang = al2gt(array_keys(get_translations()), "text/html");
  44. if (defined('_TRANSLATION_OVERRIDE_DEFAULT')) {
  45. $lang = _TRANSLATION_OVERRIDE_DEFAULT;
  46. }
  47. if ($_COOKIE["ttrss_lang"] && $_COOKIE["ttrss_lang"] != "auto") {
  48. $lang = $_COOKIE["ttrss_lang"];
  49. }
  50. /* In login action of mobile version */
  51. if ($_POST["language"] && defined('MOBILE_VERSION')) {
  52. $lang = $_POST["language"];
  53. $_COOKIE["ttrss_lang"] = $lang;
  54. }
  55. if ($lang) {
  56. if (defined('LC_MESSAGES')) {
  57. _setlocale(LC_MESSAGES, $lang);
  58. } else if (defined('LC_ALL')) {
  59. _setlocale(LC_ALL, $lang);
  60. } else {
  61. die("can't setlocale(): please set ENABLE_TRANSLATIONS to false in config.php");
  62. }
  63. if (defined('MOBILE_VERSION')) {
  64. _bindtextdomain("messages", "../locale");
  65. } else {
  66. _bindtextdomain("messages", "locale");
  67. }
  68. _textdomain("messages");
  69. _bind_textdomain_codeset("messages", "UTF-8");
  70. }
  71. }
  72. startup_gettext();
  73. } else { // If translations are enabled.
  74. function __($msg) {
  75. return $msg;
  76. }
  77. function startup_gettext() {
  78. // no-op
  79. return true;
  80. }
  81. } // If translations are enabled.
  82. if (defined('MEMCACHE_SERVER')) {
  83. $memcache = new Memcache;
  84. $memcache->connect(MEMCACHE_SERVER, 11211);
  85. }
  86. require_once 'db-prefs.php';
  87. require_once 'errors.php';
  88. require_once 'version.php';
  89. require_once 'lib/phpmailer/class.phpmailer.php';
  90. require_once 'lib/sphinxapi.php';
  91. require_once 'lib/tmhoauth/tmhOAuth.php';
  92. //define('MAGPIE_USER_AGENT_EXT', ' (Tiny Tiny RSS/' . VERSION . ')');
  93. define('MAGPIE_OUTPUT_ENCODING', 'UTF-8');
  94. define('MAGPIE_CACHE_AGE', 60*15); // 15 minutes
  95. define('SELF_USER_AGENT', 'Tiny Tiny RSS/' . VERSION . ' (http://tt-rss.org/)');
  96. define('MAGPIE_USER_AGENT', SELF_USER_AGENT);
  97. ini_set('user_agent', SELF_USER_AGENT);
  98. require_once "lib/simplepie/simplepie.inc";
  99. require_once "lib/magpierss/rss_fetch.inc";
  100. require_once 'lib/magpierss/rss_utils.inc';
  101. require_once 'lib/htmlpurifier/library/HTMLPurifier.auto.php';
  102. $config = HTMLPurifier_Config::createDefault();
  103. $allowed = "p,a[href],i,em,b,strong,code,pre,blockquote,br,img[src|alt|title],ul,ol,li,h1,h2,h3,h4,s";
  104. $config->set('HTML', 'Allowed', $allowed);
  105. $purifier = new HTMLPurifier($config);
  106. /**
  107. * Print a timestamped debug message.
  108. *
  109. * @param string $msg The debug message.
  110. * @return void
  111. */
  112. function _debug($msg) {
  113. $ts = strftime("%H:%M:%S", time());
  114. if (function_exists('posix_getpid')) {
  115. $ts = "$ts/" . posix_getpid();
  116. }
  117. print "[$ts] $msg\n";
  118. } // function _debug
  119. /**
  120. * Purge a feed old posts.
  121. *
  122. * @param mixed $link A database connection.
  123. * @param mixed $feed_id The id of the purged feed.
  124. * @param mixed $purge_interval Olderness of purged posts.
  125. * @param boolean $debug Set to True to enable the debug. False by default.
  126. * @access public
  127. * @return void
  128. */
  129. function purge_feed($link, $feed_id, $purge_interval, $debug = false) {
  130. if (!$purge_interval) $purge_interval = feed_purge_interval($link, $feed_id);
  131. $rows = -1;
  132. $result = db_query($link,
  133. "SELECT owner_uid FROM ttrss_feeds WHERE id = '$feed_id'");
  134. $owner_uid = false;
  135. if (db_num_rows($result) == 1) {
  136. $owner_uid = db_fetch_result($result, 0, "owner_uid");
  137. }
  138. if ($purge_interval == -1 || !$purge_interval) {
  139. if ($owner_uid) {
  140. ccache_update($link, $feed_id, $owner_uid);
  141. }
  142. return;
  143. }
  144. if (!$owner_uid) return;
  145. if (FORCE_ARTICLE_PURGE == 0) {
  146. $purge_unread = get_pref($link, "PURGE_UNREAD_ARTICLES",
  147. $owner_uid, false);
  148. } else {
  149. $purge_unread = true;
  150. $purge_interval = FORCE_ARTICLE_PURGE;
  151. }
  152. if (!$purge_unread) $query_limit = " unread = false AND ";
  153. if (DB_TYPE == "pgsql") {
  154. /* $result = db_query($link, "DELETE FROM ttrss_user_entries WHERE
  155. marked = false AND feed_id = '$feed_id' AND
  156. (SELECT date_updated FROM ttrss_entries WHERE
  157. id = ref_id) < NOW() - INTERVAL '$purge_interval days'"); */
  158. $pg_version = get_pgsql_version($link);
  159. if (preg_match("/^7\./", $pg_version) || preg_match("/^8\.0/", $pg_version)) {
  160. $result = db_query($link, "DELETE FROM ttrss_user_entries WHERE
  161. ttrss_entries.id = ref_id AND
  162. marked = false AND
  163. feed_id = '$feed_id' AND
  164. $query_limit
  165. ttrss_entries.date_updated < NOW() - INTERVAL '$purge_interval days'");
  166. } else {
  167. $result = db_query($link, "DELETE FROM ttrss_user_entries
  168. USING ttrss_entries
  169. WHERE ttrss_entries.id = ref_id AND
  170. marked = false AND
  171. feed_id = '$feed_id' AND
  172. $query_limit
  173. ttrss_entries.date_updated < NOW() - INTERVAL '$purge_interval days'");
  174. }
  175. $rows = pg_affected_rows($result);
  176. } else {
  177. /* $result = db_query($link, "DELETE FROM ttrss_user_entries WHERE
  178. marked = false AND feed_id = '$feed_id' AND
  179. (SELECT date_updated FROM ttrss_entries WHERE
  180. id = ref_id) < DATE_SUB(NOW(), INTERVAL $purge_interval DAY)"); */
  181. $result = db_query($link, "DELETE FROM ttrss_user_entries
  182. USING ttrss_user_entries, ttrss_entries
  183. WHERE ttrss_entries.id = ref_id AND
  184. marked = false AND
  185. feed_id = '$feed_id' AND
  186. $query_limit
  187. ttrss_entries.date_updated < DATE_SUB(NOW(), INTERVAL $purge_interval DAY)");
  188. $rows = mysql_affected_rows($link);
  189. }
  190. ccache_update($link, $feed_id, $owner_uid);
  191. if ($debug) {
  192. _debug("Purged feed $feed_id ($purge_interval): deleted $rows articles");
  193. }
  194. } // function purge_feed
  195. /**
  196. * Purge old posts from old feeds. Not used anymore, purging is done after feed update.
  197. *
  198. * @param mixed $link A database connection
  199. * @param boolean $do_output Set to true to enable printed output, false by default.
  200. * @param integer $limit The maximal number of removed posts.
  201. * @access public
  202. * @return void
  203. */
  204. /* function global_purge_old_posts($link, $do_output = false, $limit = false) {
  205. $random_qpart = sql_random_function();
  206. if ($limit) {
  207. $limit_qpart = "LIMIT $limit";
  208. } else {
  209. $limit_qpart = "";
  210. }
  211. $result = db_query($link,
  212. "SELECT id,purge_interval,owner_uid FROM ttrss_feeds
  213. ORDER BY $random_qpart $limit_qpart");
  214. while ($line = db_fetch_assoc($result)) {
  215. $feed_id = $line["id"];
  216. $purge_interval = $line["purge_interval"];
  217. $owner_uid = $line["owner_uid"];
  218. if ($purge_interval == 0) {
  219. $tmp_result = db_query($link,
  220. "SELECT value FROM ttrss_user_prefs WHERE
  221. pref_name = 'PURGE_OLD_DAYS' AND owner_uid = '$owner_uid'");
  222. if (db_num_rows($tmp_result) != 0) {
  223. $purge_interval = db_fetch_result($tmp_result, 0, "value");
  224. }
  225. }
  226. if ($do_output) {
  227. // print "Feed $feed_id: purge interval = $purge_interval\n";
  228. }
  229. if ($purge_interval > 0 || FORCE_ARTICLE_PURGE) {
  230. purge_feed($link, $feed_id, $purge_interval, $do_output);
  231. }
  232. }
  233. purge_orphans($link, $do_output);
  234. } // function global_purge_old_posts */
  235. function feed_purge_interval($link, $feed_id) {
  236. $result = db_query($link, "SELECT purge_interval, owner_uid FROM ttrss_feeds
  237. WHERE id = '$feed_id'");
  238. if (db_num_rows($result) == 1) {
  239. $purge_interval = db_fetch_result($result, 0, "purge_interval");
  240. $owner_uid = db_fetch_result($result, 0, "owner_uid");
  241. if ($purge_interval == 0) $purge_interval = get_pref($link,
  242. 'PURGE_OLD_DAYS', $owner_uid);
  243. return $purge_interval;
  244. } else {
  245. return -1;
  246. }
  247. }
  248. function purge_old_posts($link) {
  249. $user_id = $_SESSION["uid"];
  250. $result = db_query($link, "SELECT id,purge_interval FROM ttrss_feeds
  251. WHERE owner_uid = '$user_id'");
  252. while ($line = db_fetch_assoc($result)) {
  253. $feed_id = $line["id"];
  254. $purge_interval = $line["purge_interval"];
  255. if ($purge_interval == 0) $purge_interval = get_pref($link, 'PURGE_OLD_DAYS');
  256. if ($purge_interval > 0) {
  257. purge_feed($link, $feed_id, $purge_interval);
  258. }
  259. }
  260. purge_orphans($link);
  261. }
  262. function purge_orphans($link, $do_output = false) {
  263. // purge orphaned posts in main content table
  264. $result = db_query($link, "DELETE FROM ttrss_entries WHERE
  265. (SELECT COUNT(int_id) FROM ttrss_user_entries WHERE ref_id = id) = 0");
  266. if ($do_output) {
  267. $rows = db_affected_rows($link, $result);
  268. _debug("Purged $rows orphaned posts.");
  269. }
  270. }
  271. function get_feed_update_interval($link, $feed_id) {
  272. $result = db_query($link, "SELECT owner_uid, update_interval FROM
  273. ttrss_feeds WHERE id = '$feed_id'");
  274. if (db_num_rows($result) == 1) {
  275. $update_interval = db_fetch_result($result, 0, "update_interval");
  276. $owner_uid = db_fetch_result($result, 0, "owner_uid");
  277. if ($update_interval != 0) {
  278. return $update_interval;
  279. } else {
  280. return get_pref($link, 'DEFAULT_UPDATE_INTERVAL', $owner_uid, false);
  281. }
  282. } else {
  283. return -1;
  284. }
  285. }
  286. function fetch_file_contents($url, $type = false, $login = false, $pass = false) {
  287. $login = urlencode($login);
  288. $pass = urlencode($pass);
  289. if (function_exists('curl_init')) {
  290. $ch = curl_init($url);
  291. curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 15);
  292. curl_setopt($ch, CURLOPT_TIMEOUT, 45);
  293. curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
  294. curl_setopt($ch, CURLOPT_MAXREDIRS, 20);
  295. curl_setopt($ch, CURLOPT_BINARYTRANSFER, true);
  296. curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  297. curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
  298. curl_setopt($fp, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
  299. if ($login && $pass)
  300. curl_setopt($ch, CURLOPT_USERPWD, "$login:$pass");
  301. $contents = @curl_exec($ch);
  302. if ($contents === false) {
  303. curl_close($ch);
  304. return false;
  305. }
  306. $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
  307. $content_type = curl_getinfo($ch, CURLINFO_CONTENT_TYPE);
  308. curl_close($ch);
  309. if ($http_code != 200 || $type && strpos($content_type, "$type") === false) {
  310. return false;
  311. }
  312. return $contents;
  313. } else {
  314. if ($login && $pass && $updated != 3) {
  315. $url_parts = array();
  316. preg_match("/(^[^:]*):\/\/(.*)/", $url, $url_parts);
  317. if ($url_parts[1] && $url_parts[2]) {
  318. $url = $url_parts[1] . "://$login:$pass@" . $url_parts[2];
  319. }
  320. }
  321. return @file_get_contents($url);
  322. }
  323. }
  324. /**
  325. * Try to determine the favicon URL for a feed.
  326. * adapted from wordpress favicon plugin by Jeff Minard (http://thecodepro.com/)
  327. * http://dev.wp-plugins.org/file/favatars/trunk/favatars.php
  328. *
  329. * @param string $url A feed or page URL
  330. * @access public
  331. * @return mixed The favicon URL, or false if none was found.
  332. */
  333. function get_favicon_url($url) {
  334. $favicon_url = false;
  335. if ($html = @fetch_file_contents($url)) {
  336. libxml_use_internal_errors(true);
  337. $doc = new DOMDocument();
  338. $doc->loadHTML($html);
  339. $xpath = new DOMXPath($doc);
  340. $entries = $xpath->query('/html/head/link[@rel="shortcut icon"]');
  341. if (count($entries) > 0) {
  342. foreach ($entries as $entry) {
  343. $favicon_url = rewrite_relative_url($url, $entry->getAttribute("href"));
  344. break;
  345. }
  346. }
  347. }
  348. if (!$favicon_url)
  349. $favicon_url = rewrite_relative_url($url, "/favicon.ico");
  350. return $favicon_url;
  351. } // function get_favicon_url
  352. function check_feed_favicon($site_url, $feed, $link) {
  353. $favicon_url = get_favicon_url($site_url);
  354. # print "FAVICON [$site_url]: $favicon_url\n";
  355. $icon_file = ICONS_DIR . "/$feed.ico";
  356. if ($favicon_url && !file_exists($icon_file)) {
  357. $contents = fetch_file_contents($favicon_url, "image");
  358. if ($contents) {
  359. $fp = fopen($icon_file, "w");
  360. if ($fp) {
  361. fwrite($fp, $contents);
  362. fclose($fp);
  363. chmod($icon_file, 0644);
  364. }
  365. }
  366. }
  367. }
  368. function update_rss_feed($link, $feed, $ignore_daemon = false) {
  369. global $memcache;
  370. /* Update all feeds with the same URL to utilize memcache */
  371. if ($memcache) {
  372. $result = db_query($link, "SELECT f1.id
  373. FROM ttrss_feeds AS f1, ttrss_feeds AS f2
  374. WHERE f2.feed_url = f1.feed_url AND f2.id = '$feed'");
  375. while ($line = db_fetch_assoc($result)) {
  376. update_rss_feed_real($link, $line["id"], $ignore_daemon);
  377. }
  378. } else {
  379. update_rss_feed_real($link, $feed, $ignore_daemon);
  380. }
  381. }
  382. function update_rss_feed_real($link, $feed, $ignore_daemon = false) {
  383. global $memcache;
  384. if (!$_REQUEST["daemon"] && !$ignore_daemon) {
  385. return false;
  386. }
  387. if (defined('DAEMON_EXTENDED_DEBUG') || $_REQUEST['xdebug']) {
  388. _debug("update_rss_feed: start");
  389. }
  390. if (!$ignore_daemon) {
  391. if (DB_TYPE == "pgsql") {
  392. $updstart_thresh_qpart = "(ttrss_feeds.last_update_started IS NULL OR ttrss_feeds.last_update_started < NOW() - INTERVAL '120 seconds')";
  393. } else {
  394. $updstart_thresh_qpart = "(ttrss_feeds.last_update_started IS NULL OR ttrss_feeds.last_update_started < DATE_SUB(NOW(), INTERVAL 120 SECOND))";
  395. }
  396. $result = db_query($link, "SELECT id,update_interval,auth_login,
  397. auth_pass,cache_images,update_method
  398. FROM ttrss_feeds WHERE id = '$feed' AND $updstart_thresh_qpart");
  399. } else {
  400. $result = db_query($link, "SELECT id,update_interval,auth_login,
  401. feed_url,auth_pass,cache_images,update_method,last_updated,
  402. owner_uid
  403. FROM ttrss_feeds WHERE id = '$feed'");
  404. }
  405. if (db_num_rows($result) == 0) {
  406. if (defined('DAEMON_EXTENDED_DEBUG') || $_REQUEST['xdebug']) {
  407. _debug("update_rss_feed: feed $feed NOT FOUND/SKIPPED");
  408. }
  409. return false;
  410. }
  411. $update_method = db_fetch_result($result, 0, "update_method");
  412. $last_updated = db_fetch_result($result, 0, "last_updated");
  413. $owner_uid = db_fetch_result($result, 0, "owner_uid");
  414. db_query($link, "UPDATE ttrss_feeds SET last_update_started = NOW()
  415. WHERE id = '$feed'");
  416. $auth_login = db_fetch_result($result, 0, "auth_login");
  417. $auth_pass = db_fetch_result($result, 0, "auth_pass");
  418. if ($update_method == 0)
  419. $update_method = DEFAULT_UPDATE_METHOD + 1;
  420. // 1 - Magpie
  421. // 2 - SimplePie
  422. // 3 - Twitter OAuth
  423. if ($update_method == 2)
  424. $use_simplepie = true;
  425. else
  426. $use_simplepie = false;
  427. if (defined('DAEMON_EXTENDED_DEBUG') || $_REQUEST['xdebug']) {
  428. _debug("update method: $update_method (feed setting: $update_method) (use simplepie: $use_simplepie)\n");
  429. }
  430. if ($update_method == 1) {
  431. $auth_login = urlencode($auth_login);
  432. $auth_pass = urlencode($auth_pass);
  433. }
  434. $update_interval = db_fetch_result($result, 0, "update_interval");
  435. $cache_images = sql_bool_to_bool(db_fetch_result($result, 0, "cache_images"));
  436. $fetch_url = db_fetch_result($result, 0, "feed_url");
  437. if ($update_interval < 0) { return; }
  438. $feed = db_escape_string($feed);
  439. if ($auth_login && $auth_pass && $updated != 3) {
  440. $url_parts = array();
  441. preg_match("/(^[^:]*):\/\/(.*)/", $fetch_url, $url_parts);
  442. if ($url_parts[1] && $url_parts[2]) {
  443. $fetch_url = $url_parts[1] . "://$auth_login:$auth_pass@" . $url_parts[2];
  444. }
  445. }
  446. if (defined('DAEMON_EXTENDED_DEBUG') || $_REQUEST['xdebug']) {
  447. _debug("update_rss_feed: fetching [$fetch_url]...");
  448. }
  449. $obj_id = md5("FDATA:$use_simplepie:$fetch_url");
  450. if ($memcache && $obj = $memcache->get($obj_id)) {
  451. if (defined('DAEMON_EXTENDED_DEBUG') || $_REQUEST['xdebug']) {
  452. _debug("update_rss_feed: data found in memcache.");
  453. }
  454. $rss = $obj;
  455. } else {
  456. if ($update_method == 3) {
  457. $rss = fetch_twitter_rss($link, $fetch_url, $owner_uid);
  458. } else if ($update_method == 1) {
  459. $rss = @fetch_rss($fetch_url);
  460. } else {
  461. if (!is_dir(SIMPLEPIE_CACHE_DIR)) {
  462. mkdir(SIMPLEPIE_CACHE_DIR);
  463. }
  464. $rss = new SimplePie();
  465. $rss->set_useragent(SELF_USER_AGENT);
  466. # $rss->set_timeout(10);
  467. $rss->set_feed_url($fetch_url);
  468. $rss->set_output_encoding('UTF-8');
  469. if (SIMPLEPIE_CACHE_IMAGES && $cache_images) {
  470. if (defined('DAEMON_EXTENDED_DEBUG') || $_REQUEST['xdebug']) {
  471. _debug("enabling image cache");
  472. }
  473. $rss->set_image_handler("image.php", 'i');
  474. }
  475. if (defined('DAEMON_EXTENDED_DEBUG') || $_REQUEST['xdebug']) {
  476. _debug("feed update interval (sec): " .
  477. get_feed_update_interval($link, $feed)*60);
  478. }
  479. if (is_dir(SIMPLEPIE_CACHE_DIR)) {
  480. $rss->set_cache_location(SIMPLEPIE_CACHE_DIR);
  481. $rss->set_cache_duration(get_feed_update_interval($link, $feed) * 60);
  482. }
  483. $rss->init();
  484. }
  485. if ($memcache && $rss) $memcache->add($obj_id, $rss, 0, 300);
  486. }
  487. // print_r($rss);
  488. if (defined('DAEMON_EXTENDED_DEBUG') || $_REQUEST['xdebug']) {
  489. _debug("update_rss_feed: fetch done, parsing...");
  490. }
  491. $feed = db_escape_string($feed);
  492. if ($update_method == 2) {
  493. $fetch_ok = !$rss->error();
  494. } else {
  495. $fetch_ok = !!$rss;
  496. }
  497. if ($fetch_ok) {
  498. if (defined('DAEMON_EXTENDED_DEBUG') || $_REQUEST['xdebug']) {
  499. _debug("update_rss_feed: processing feed data...");
  500. }
  501. // db_query($link, "BEGIN");
  502. $result = db_query($link, "SELECT title,icon_url,site_url,owner_uid
  503. FROM ttrss_feeds WHERE id = '$feed'");
  504. $registered_title = db_fetch_result($result, 0, "title");
  505. $orig_icon_url = db_fetch_result($result, 0, "icon_url");
  506. $orig_site_url = db_fetch_result($result, 0, "site_url");
  507. $owner_uid = db_fetch_result($result, 0, "owner_uid");
  508. if ($use_simplepie) {
  509. $site_url = $rss->get_link();
  510. } else {
  511. $site_url = $rss->channel["link"];
  512. }
  513. if (defined('DAEMON_EXTENDED_DEBUG') || $_REQUEST['xdebug']) {
  514. _debug("update_rss_feed: checking favicon...");
  515. }
  516. check_feed_favicon($site_url, $feed, $link);
  517. if (!$registered_title || $registered_title == "[Unknown]") {
  518. if ($use_simplepie) {
  519. $feed_title = db_escape_string($rss->get_title());
  520. } else {
  521. $feed_title = db_escape_string($rss->channel["title"]);
  522. }
  523. if (defined('DAEMON_EXTENDED_DEBUG') || $_REQUEST['xdebug']) {
  524. _debug("update_rss_feed: registering title: $feed_title");
  525. }
  526. db_query($link, "UPDATE ttrss_feeds SET
  527. title = '$feed_title' WHERE id = '$feed'");
  528. }
  529. // weird, weird Magpie
  530. if (!$use_simplepie) {
  531. if (!$site_url) $site_url = db_escape_string($rss->channel["link_"]);
  532. }
  533. if ($site_url && $orig_site_url != db_escape_string($site_url)) {
  534. db_query($link, "UPDATE ttrss_feeds SET
  535. site_url = '$site_url' WHERE id = '$feed'");
  536. }
  537. // print "I: " . $rss->channel["image"]["url"];
  538. if (!$use_simplepie) {
  539. $icon_url = db_escape_string($rss->image["url"]);
  540. } else {
  541. $icon_url = db_escape_string($rss->get_image_url());
  542. }
  543. $icon_url = substr($icon_url, 0, 250);
  544. if ($icon_url && $orig_icon_url != $icon_url) {
  545. db_query($link, "UPDATE ttrss_feeds SET icon_url = '$icon_url' WHERE id = '$feed'");
  546. }
  547. if (defined('DAEMON_EXTENDED_DEBUG') || $_REQUEST['xdebug']) {
  548. _debug("update_rss_feed: loading filters...");
  549. }
  550. $filters = load_filters($link, $feed, $owner_uid);
  551. if (defined('DAEMON_EXTENDED_DEBUG') || $_REQUEST['xdebug']) {
  552. print_r($filters);
  553. }
  554. if ($use_simplepie) {
  555. $iterator = $rss->get_items();
  556. } else {
  557. $iterator = $rss->items;
  558. if (!$iterator || !is_array($iterator)) $iterator = $rss->entries;
  559. if (!$iterator || !is_array($iterator)) $iterator = $rss;
  560. }
  561. if (!is_array($iterator)) {
  562. /* db_query($link, "UPDATE ttrss_feeds
  563. SET last_error = 'Parse error: can\'t find any articles.'
  564. WHERE id = '$feed'"); */
  565. // clear any errors and mark feed as updated if fetched okay
  566. // even if it's blank
  567. if (defined('DAEMON_EXTENDED_DEBUG') || $_REQUEST['xdebug']) {
  568. _debug("update_rss_feed: entry iterator is not an array, no articles?");
  569. }
  570. db_query($link, "UPDATE ttrss_feeds
  571. SET last_updated = NOW(), last_error = '' WHERE id = '$feed'");
  572. return; // no articles
  573. }
  574. if (defined('DAEMON_EXTENDED_DEBUG') || $_REQUEST['xdebug']) {
  575. _debug("update_rss_feed: processing articles...");
  576. }
  577. foreach ($iterator as $item) {
  578. if ($_REQUEST['xdebug'] == 2) {
  579. print_r($item);
  580. }
  581. if ($use_simplepie) {
  582. $entry_guid = $item->get_id();
  583. if (!$entry_guid) $entry_guid = $item->get_link();
  584. if (!$entry_guid) $entry_guid = make_guid_from_title($item->get_title());
  585. } else {
  586. $entry_guid = $item["id"];
  587. if (!$entry_guid) $entry_guid = $item["guid"];
  588. if (!$entry_guid) $entry_guid = $item["about"];
  589. if (!$entry_guid) $entry_guid = $item["link"];
  590. if (!$entry_guid) $entry_guid = make_guid_from_title($item["title"]);
  591. }
  592. if (defined('DAEMON_EXTENDED_DEBUG') || $_REQUEST['xdebug']) {
  593. _debug("update_rss_feed: guid $entry_guid");
  594. }
  595. if (!$entry_guid) continue;
  596. $entry_timestamp = "";
  597. if ($use_simplepie) {
  598. $entry_timestamp = strtotime($item->get_date());
  599. } else {
  600. $rss_2_date = $item['pubdate'];
  601. $rss_1_date = $item['dc']['date'];
  602. $atom_date = $item['issued'];
  603. if (!$atom_date) $atom_date = $item['updated'];
  604. if ($atom_date != "") $entry_timestamp = parse_w3cdtf($atom_date);
  605. if ($rss_1_date != "") $entry_timestamp = parse_w3cdtf($rss_1_date);
  606. if ($rss_2_date != "") $entry_timestamp = strtotime($rss_2_date);
  607. }
  608. if ($entry_timestamp == "" || $entry_timestamp == -1 || !$entry_timestamp) {
  609. $entry_timestamp = time();
  610. $no_orig_date = 'true';
  611. } else {
  612. $no_orig_date = 'false';
  613. }
  614. $entry_timestamp_fmt = strftime("%Y/%m/%d %H:%M:%S", $entry_timestamp);
  615. if (defined('DAEMON_EXTENDED_DEBUG') || $_REQUEST['xdebug']) {
  616. _debug("update_rss_feed: date $entry_timestamp [$entry_timestamp_fmt]");
  617. }
  618. if ($use_simplepie) {
  619. $entry_title = $item->get_title();
  620. } else {
  621. $entry_title = trim(strip_tags($item["title"]));
  622. }
  623. if ($use_simplepie) {
  624. $entry_link = $item->get_link();
  625. } else {
  626. // strange Magpie workaround
  627. $entry_link = $item["link_"];
  628. if (!$entry_link) $entry_link = $item["link"];
  629. }
  630. if (defined('DAEMON_EXTENDED_DEBUG') || $_REQUEST['xdebug']) {
  631. _debug("update_rss_feed: title $entry_title");
  632. }
  633. if (!$entry_title) $entry_title = date("Y-m-d H:i:s", $entry_timestamp);;
  634. $entry_link = strip_tags($entry_link);
  635. if ($use_simplepie) {
  636. $entry_content = $item->get_content();
  637. if (!$entry_content) $entry_content = $item->get_description();
  638. } else {
  639. $entry_content = $item["content:escaped"];
  640. if (!$entry_content) $entry_content = $item["content:encoded"];
  641. if (!$entry_content) $entry_content = $item["content"]["encoded"];
  642. if (!$entry_content) $entry_content = $item["content"];
  643. // Magpie bugs are getting ridiculous
  644. if (trim($entry_content) == "Array") $entry_content = false;
  645. if (!$entry_content) $entry_content = $item["atom_content"];
  646. if (!$entry_content) $entry_content = $item["summary"];
  647. if (!$entry_content ||
  648. strlen($entry_content) < strlen($item["description"])) {
  649. $entry_content = $item["description"];
  650. };
  651. // WTF
  652. if (is_array($entry_content)) {
  653. $entry_content = $entry_content["encoded"];
  654. if (!$entry_content) $entry_content = $entry_content["escaped"];
  655. }
  656. }
  657. if ($_REQUEST["xdebug"] == 2) {
  658. print "update_rss_feed: content: ";
  659. print_r(htmlspecialchars($entry_content));
  660. }
  661. $entry_content_unescaped = $entry_content;
  662. if ($use_simplepie) {
  663. $entry_comments = strip_tags($item->data["comments"]);
  664. if ($item->get_author()) {
  665. $entry_author_item = $item->get_author();
  666. $entry_author = $entry_author_item->get_name();
  667. if (!$entry_author) $entry_author = $entry_author_item->get_email();
  668. $entry_author = db_escape_string($entry_author);
  669. }
  670. } else {
  671. $entry_comments = strip_tags($item["comments"]);
  672. $entry_author = db_escape_string(strip_tags($item['dc']['creator']));
  673. if ($item['author']) {
  674. if (is_array($item['author'])) {
  675. if (!$entry_author) {
  676. $entry_author = db_escape_string(strip_tags($item['author']['name']));
  677. }
  678. if (!$entry_author) {
  679. $entry_author = db_escape_string(strip_tags($item['author']['email']));
  680. }
  681. }
  682. if (!$entry_author) {
  683. $entry_author = db_escape_string(strip_tags($item['author']));
  684. }
  685. }
  686. }
  687. if (preg_match('/^[\t\n\r ]*$/', $entry_author)) $entry_author = '';
  688. $entry_guid = db_escape_string(strip_tags($entry_guid));
  689. $entry_guid = mb_substr($entry_guid, 0, 250);
  690. $result = db_query($link, "SELECT id FROM ttrss_entries
  691. WHERE guid = '$entry_guid'");
  692. $entry_content = db_escape_string($entry_content, false);
  693. $content_hash = "SHA1:" . sha1(strip_tags($entry_content));
  694. $entry_title = db_escape_string($entry_title);
  695. $entry_link = db_escape_string($entry_link);
  696. $entry_comments = mb_substr(db_escape_string($entry_comments), 0, 250);
  697. $entry_author = mb_substr($entry_author, 0, 250);
  698. if ($use_simplepie) {
  699. $num_comments = 0; #FIXME#
  700. } else {
  701. $num_comments = db_escape_string($item["slash"]["comments"]);
  702. }
  703. if (!$num_comments) $num_comments = 0;
  704. if (defined('DAEMON_EXTENDED_DEBUG') || $_REQUEST['xdebug']) {
  705. _debug("update_rss_feed: looking for tags [1]...");
  706. }
  707. // parse <category> entries into tags
  708. $additional_tags = array();
  709. if ($use_simplepie) {
  710. $additional_tags_src = $item->get_categories();
  711. if (is_array($additional_tags_src)) {
  712. foreach ($additional_tags_src as $tobj) {
  713. array_push($additional_tags, $tobj->get_term());
  714. }
  715. }
  716. if (defined('DAEMON_EXTENDED_DEBUG') || $_REQUEST['xdebug']) {
  717. _debug("update_rss_feed: category tags:");
  718. print_r($additional_tags);
  719. }
  720. } else {
  721. $t_ctr = $item['category#'];
  722. if ($t_ctr == 0) {
  723. $additional_tags = array();
  724. } else if ($t_ctr > 0) {
  725. $additional_tags = array($item['category']);
  726. if ($item['category@term']) {
  727. array_push($additional_tags, $item['category@term']);
  728. }
  729. for ($i = 0; $i <= $t_ctr; $i++ ) {
  730. if ($item["category#$i"]) {
  731. array_push($additional_tags, $item["category#$i"]);
  732. }
  733. if ($item["category#$i@term"]) {
  734. array_push($additional_tags, $item["category#$i@term"]);
  735. }
  736. }
  737. }
  738. // parse <dc:subject> elements
  739. $t_ctr = $item['dc']['subject#'];
  740. if ($t_ctr > 0) {
  741. array_push($additional_tags, $item['dc']['subject']);
  742. for ($i = 0; $i <= $t_ctr; $i++ ) {
  743. if ($item['dc']["subject#$i"]) {
  744. array_push($additional_tags, $item['dc']["subject#$i"]);
  745. }
  746. }
  747. }
  748. }
  749. if (defined('DAEMON_EXTENDED_DEBUG') || $_REQUEST['xdebug']) {
  750. _debug("update_rss_feed: looking for tags [2]...");
  751. }
  752. /* taaaags */
  753. // <a href="..." rel="tag">Xorg</a>, //
  754. $entry_tags = null;
  755. preg_match_all("/<a.*?rel=['\"]tag['\"].*?\>([^<]+)<\/a>/i",
  756. $entry_content_unescaped, $entry_tags);
  757. $entry_tags = $entry_tags[1];
  758. $entry_tags = array_merge($entry_tags, $additional_tags);
  759. $entry_tags = array_unique($entry_tags);
  760. for ($i = 0; $i < count($entry_tags); $i++)
  761. $entry_tags[$i] = mb_strtolower($entry_tags[$i], 'utf-8');
  762. if (defined('DAEMON_EXTENDED_DEBUG') || $_REQUEST['xdebug']) {
  763. _debug("update_rss_feed: unfiltered tags found:");
  764. print_r($entry_tags);
  765. }
  766. # sanitize content
  767. $entry_content = sanitize_article_content($entry_content);
  768. $entry_title = sanitize_article_content($entry_title);
  769. if (defined('DAEMON_EXTENDED_DEBUG') || $_REQUEST['xdebug']) {
  770. _debug("update_rss_feed: done collecting data [TITLE:$entry_title]");
  771. }
  772. db_query($link, "BEGIN");
  773. if (db_num_rows($result) == 0) {
  774. if (defined('DAEMON_EXTENDED_DEBUG') || $_REQUEST['xdebug']) {
  775. _debug("update_rss_feed: base guid not found");
  776. }
  777. // base post entry does not exist, create it
  778. $result = db_query($link,
  779. "INSERT INTO ttrss_entries
  780. (title,
  781. guid,
  782. link,
  783. updated,
  784. content,
  785. content_hash,
  786. no_orig_date,
  787. date_updated,
  788. date_entered,
  789. comments,
  790. num_comments,
  791. author)
  792. VALUES
  793. ('$entry_title',
  794. '$entry_guid',
  795. '$entry_link',
  796. '$entry_timestamp_fmt',
  797. '$entry_content',
  798. '$content_hash',
  799. $no_orig_date,
  800. NOW(),
  801. NOW(),
  802. '$entry_comments',
  803. '$num_comments',
  804. '$entry_author')");
  805. } else {
  806. // we keep encountering the entry in feeds, so we need to
  807. // update date_updated column so that we don't get horrible
  808. // dupes when the entry gets purged and reinserted again e.g.
  809. // in the case of SLOW SLOW OMG SLOW updating feeds
  810. $base_entry_id = db_fetch_result($result, 0, "id");
  811. db_query($link, "UPDATE ttrss_entries SET date_updated = NOW()
  812. WHERE id = '$base_entry_id'");
  813. }
  814. // now it should exist, if not - bad luck then
  815. $result = db_query($link, "SELECT
  816. id,content_hash,no_orig_date,title,
  817. ".SUBSTRING_FOR_DATE."(date_updated,1,19) as date_updated,
  818. ".SUBSTRING_FOR_DATE."(updated,1,19) as updated,
  819. num_comments
  820. FROM
  821. ttrss_entries
  822. WHERE guid = '$entry_guid'");
  823. $entry_ref_id = 0;
  824. $entry_int_id = 0;
  825. if (db_num_rows($result) == 1) {
  826. if (defined('DAEMON_EXTENDED_DEBUG') || $_REQUEST['xdebug']) {
  827. _debug("update_rss_feed: base guid found, checking for user record");
  828. }
  829. // this will be used below in update handler
  830. $orig_content_hash = db_fetch_result($result, 0, "content_hash");
  831. $orig_title = db_fetch_result($result, 0, "title");
  832. $orig_num_comments = db_fetch_result($result, 0, "num_comments");
  833. $orig_date_updated = strtotime(db_fetch_result($result,
  834. 0, "date_updated"));
  835. $ref_id = db_fetch_result($result, 0, "id");
  836. $entry_ref_id = $ref_id;
  837. // check for user post link to main table
  838. // do we allow duplicate posts with same GUID in different feeds?
  839. if (get_pref($link, "ALLOW_DUPLICATE_POSTS", $owner_uid, false)) {
  840. $dupcheck_qpart = "AND (feed_id = '$feed' OR feed_id IS NULL)";
  841. } else {
  842. $dupcheck_qpart = "";
  843. }
  844. /* Collect article tags here so we could filter by them: */
  845. $article_filters = get_article_filters($filters, $entry_title,
  846. $entry_content, $entry_link, $entry_timestamp, $entry_author,
  847. $entry_tags);
  848. if (defined('DAEMON_EXTENDED_DEBUG') || $_REQUEST['xdebug']) {
  849. _debug("update_rss_feed: article filters: ");
  850. if (count($article_filters) != 0) {
  851. print_r($article_filters);
  852. }
  853. }
  854. if (find_article_filter($article_filters, "filter")) {
  855. db_query($link, "COMMIT"); // close transaction in progress
  856. continue;
  857. }
  858. $score = calculate_article_score($article_filters);
  859. if (defined('DAEMON_EXTENDED_DEBUG') || $_REQUEST['xdebug']) {
  860. _debug("update_rss_feed: initial score: $score");
  861. }
  862. $query = "SELECT ref_id, int_id FROM ttrss_user_entries WHERE
  863. ref_id = '$ref_id' AND owner_uid = '$owner_uid'
  864. $dupcheck_qpart";
  865. // if ($_REQUEST["xdebug"]) print "$query\n";
  866. $result = db_query($link, $query);
  867. // okay it doesn't exist - create user entry
  868. if (db_num_rows($result) == 0) {
  869. if (defined('DAEMON_EXTENDED_DEBUG') || $_REQUEST['xdebug']) {
  870. _debug("update_rss_feed: user record not found, creating...");
  871. }
  872. if ($score >= -500 && !find_article_filter($article_filters, 'catchup')) {
  873. $unread = 'true';
  874. $last_read_qpart = 'NULL';
  875. } else {
  876. $unread = 'false';
  877. $last_read_qpart = 'NOW()';
  878. }
  879. if (find_article_filter($article_filters, 'mark') || $score > 1000) {
  880. $marked = 'true';
  881. } else {
  882. $marked = 'false';
  883. }
  884. if (find_article_filter($article_filters, 'publish')) {
  885. $published = 'true';
  886. } else {
  887. $published = 'false';
  888. }
  889. $result = db_query($link,
  890. "INSERT INTO ttrss_user_entries
  891. (ref_id, owner_uid, feed_id, unread, last_read, marked,
  892. published, score, tag_cache, label_cache)
  893. VALUES ('$ref_id', '$owner_uid', '$feed', $unread,
  894. $last_read_qpart, $marked, $published, '$score', '', '')");
  895. $result = db_query($link,
  896. "SELECT int_id FROM ttrss_user_entries WHERE
  897. ref_id = '$ref_id' AND owner_uid = '$owner_uid' AND
  898. feed_id = '$feed' LIMIT 1");
  899. if (db_num_rows($result) == 1) {
  900. $entry_int_id = db_fetch_result($result, 0, "int_id");
  901. }
  902. } else {
  903. if (defined('DAEMON_EXTENDED_DEBUG') || $_REQUEST['xdebug']) {
  904. _debug("update_rss_feed: user record FOUND");
  905. }
  906. $entry_ref_id = db_fetch_result($result, 0, "ref_id");
  907. $entry_int_id = db_fetch_result($result, 0, "int_id");
  908. }
  909. if (defined('DAEMON_EXTENDED_DEBUG') || $_REQUEST['xdebug']) {
  910. _debug("update_rss_feed: RID: $entry_ref_id, IID: $entry_int_id");
  911. }
  912. $post_needs_update = false;
  913. if (get_pref($link, "UPDATE_POST_ON_CHECKSUM_CHANGE", $owner_uid, false) &&
  914. ($content_hash != $orig_content_hash)) {
  915. // print "<!-- [$entry_title] $content_hash vs $orig_content_hash -->";
  916. $post_needs_update = true;
  917. }
  918. if (db_escape_string($orig_title) != $entry_title) {
  919. $post_needs_update = true;
  920. }
  921. if ($orig_num_comments != $num_comments) {
  922. $post_needs_update = true;
  923. }
  924. // this doesn't seem to be very reliable
  925. //
  926. // if ($orig_timestamp != $entry_timestamp && !$orig_no_orig_date) {
  927. // $post_needs_update = true;
  928. // }
  929. // if post needs update, update it and mark all user entries
  930. // linking to this post as updated
  931. if ($post_needs_update) {
  932. if (defined('DAEMON_EXTENDED_DEBUG')) {
  933. _debug("update_rss_feed: post $entry_guid needs update...");
  934. }
  935. // print "<!-- post $orig_title needs update : $post_needs_update -->";
  936. db_query($link, "UPDATE ttrss_entries
  937. SET title = '$entry_title', content = '$entry_content',
  938. content_hash = '$content_hash',
  939. num_comments = '$num_comments'
  940. WHERE id = '$ref_id'");
  941. if (get_pref($link, "MARK_UNREAD_ON_UPDATE", $owner_uid, false)) {
  942. db_query($link, "UPDATE ttrss_user_entries
  943. SET last_read = null, unread = true WHERE ref_id = '$ref_id'");
  944. } else {
  945. db_query($link, "UPDATE ttrss_user_entries
  946. SET last_read = null WHERE ref_id = '$ref_id' AND unread = false");
  947. }
  948. }
  949. }
  950. db_query($link, "COMMIT");
  951. if (defined('DAEMON_EXTENDED_DEBUG') || $_REQUEST['xdebug']) {
  952. _debug("update_rss_feed: assigning labels...");
  953. }
  954. assign_article_to_labels($link, $entry_ref_id, $article_filters,
  955. $owner_uid);
  956. if (defined('DAEMON_EXTENDED_DEBUG') || $_REQUEST['xdebug']) {
  957. _debug("update_rss_feed: looking for enclosures...");
  958. }
  959. // enclosures
  960. $enclosures = array();
  961. if ($use_simplepie) {
  962. $encs = $item->get_enclosures();
  963. if (is_array($encs)) {
  964. foreach ($encs as $e) {
  965. $e_item = array(
  966. $e->link, $e->type, $e->length);
  967. array_push($enclosures, $e_item);
  968. }
  969. }
  970. } else {
  971. // <enclosure>
  972. $e_ctr = $item['enclosure#'];
  973. if ($e_ctr > 0) {
  974. $e_item = array($item['enclosure@url'],
  975. $item['enclosure@type'],
  976. $item['enclosure@length']);
  977. array_push($enclosures, $e_item);
  978. for ($i = 0; $i <= $e_ctr; $i++ ) {
  979. if ($item["enclosure#$i@url"]) {
  980. $e_item = array($item["enclosure#$i@url"],
  981. $item["enclosure#$i@type"],
  982. $item["enclosure#$i@length"]);
  983. array_push($enclosures, $e_item);
  984. }
  985. }
  986. }
  987. // <media:content>
  988. // can there be many of those? yes -fox
  989. $m_ctr = $item['media']['content#'];
  990. if ($m_ctr > 0) {
  991. $e_item = array($item['media']['content@url'],
  992. $item['media']['content@medium'],
  993. $item['media']['content@length']);
  994. array_push($enclosures, $e_item);
  995. for ($i = 0; $i <= $m_ctr; $i++ ) {
  996. if ($item["media"]["content#$i@url"]) {
  997. $e_item = array($item["media"]["content#$i@url"],
  998. $item["media"]["content#$i@medium"],
  999. $item["media"]["content#$i@length"]);
  1000. array_push($enclosures, $e_item);
  1001. }
  1002. }
  1003. }
  1004. }
  1005. if (defined('DAEMON_EXTENDED_DEBUG') || $_REQUEST['xdebug']) {
  1006. _debug("update_rss_feed: article enclosures:");
  1007. print_r($enclosures);
  1008. }
  1009. db_query($link, "BEGIN");
  1010. foreach ($enclosures as $enc) {
  1011. $enc_url = db_escape_string($enc[0]);
  1012. $enc_type = db_escape_string($enc[1]);
  1013. $enc_dur = db_escape_string($enc[2]);
  1014. $result = db_query($link, "SELECT id FROM ttrss_enclosures
  1015. WHERE content_url = '$enc_url' AND post_id = '$entry_ref_id'");
  1016. if (db_num_rows($result) == 0) {
  1017. db_query($link, "INSERT INTO ttrss_enclosures
  1018. (content_url, content_type, title, duration, post_id) VALUES
  1019. ('$enc_url', '$enc_type', '', '$enc_dur', '$entry_ref_id')");
  1020. }
  1021. }
  1022. db_query($link, "COMMIT");
  1023. // check for manual tags (we have to do it here since they're loaded from filters)
  1024. foreach ($article_filters as $f) {
  1025. if ($f[0] == "tag") {
  1026. $manual_tags = trim_array(split(",", $f[1]));
  1027. foreach ($manual_tags as $tag) {
  1028. if (tag_is_valid($tag)) {
  1029. array_push($entry_tags, $tag);
  1030. }
  1031. }
  1032. }
  1033. }
  1034. // Skip boring tags
  1035. $boring_tags = trim_array(split(",", mb_strtolower(get_pref($link,
  1036. 'BLACKLISTED_TAGS', $owner_uid, ''), 'utf-8')));
  1037. $filtered_tags = array();
  1038. $tags_to_cache = array();
  1039. if ($entry_tags && is_array($entry_tags)) {
  1040. foreach ($entry_tags as $tag) {
  1041. if (array_search($tag, $boring_tags) === false) {
  1042. array_push($filtered_tags, $tag);
  1043. }
  1044. }
  1045. }
  1046. $filtered_tags = array_unique($filtered_tags);
  1047. if (defined('DAEMON_EXTENDED_DEBUG') || $_REQUEST['xdebug']) {
  1048. _debug("update_rss_feed: filtered article tags:");
  1049. print_r($filtered_tags);
  1050. }
  1051. // Save article tags in the database
  1052. if (count($filtered_tags) > 0) {
  1053. db_query($link, "BEGIN");
  1054. foreach ($filtered_tags as $tag) {
  1055. $tag = sanitize_tag($tag);
  1056. $tag = db_escape_string($tag);
  1057. if (!tag_is_valid($tag)) continue;
  1058. $result = db_query($link, "SELECT id FROM ttrss_tags
  1059. WHERE tag_name = '$tag' AND post_int_id = '$entry_int_id' AND
  1060. owner_uid = '$owner_uid' LIMIT 1");
  1061. if ($result && db_num_rows($result) == 0) {
  1062. db_query($link, "INSERT INTO ttrss_tags
  1063. (owner_uid,tag_name,post_int_id)
  1064. VALUES ('$owner_uid','$tag', '$entry_int_id')");
  1065. }
  1066. array_push($tags_to_cache, $tag);
  1067. }
  1068. /* update the cache */
  1069. $tags_to_cache = array_unique($tags_to_cache);
  1070. $tags_str = db_escape_string(join(",", $tags_to_cache));
  1071. db_query($link, "UPDATE ttrss_user_entries
  1072. SET tag_cache = '$tags_str' WHERE ref_id = '$entry_ref_id'
  1073. AND owner_uid = $owner_uid");
  1074. db_query($link, "COMMIT");
  1075. }
  1076. if (defined('DAEMON_EXTENDED_DEBUG') || $_REQUEST['xdebug']) {
  1077. _debug("update_rss_feed: article processed");
  1078. }
  1079. }
  1080. if (!$last_updated) {
  1081. if (defined('DAEMON_EXTENDED_DEBUG') || $_REQUEST['xdebug']) {
  1082. _debug("update_rss_feed: new feed, catching it up...");
  1083. }
  1084. catchup_feed($link, $feed, false, $owner_uid);
  1085. }
  1086. purge_feed($link, $feed, 0);
  1087. db_query($link, "UPDATE ttrss_feeds
  1088. SET last_updated = NOW(), last_error = '' WHERE id = '$feed'");
  1089. // db_query($link, "COMMIT");
  1090. } else {
  1091. if ($use_simplepie) {
  1092. $error_msg = mb_substr($rss->error(), 0, 250);
  1093. } else {
  1094. $error_msg = mb_substr(magpie_error(), 0, 250);
  1095. }
  1096. if (defined('DAEMON_EXTENDED_DEBUG') || $_REQUEST['xdebug']) {
  1097. _debug("update_rss_feed: error fetching feed: $error_msg");
  1098. }
  1099. $error_msg = db_escape_string($error_msg);
  1100. db_query($link,
  1101. "UPDATE ttrss_feeds SET last_error = '$error_msg',
  1102. last_updated = NOW() WHERE id = '$feed'");
  1103. }
  1104. if ($use_simplepie) {
  1105. unset($rss);
  1106. }
  1107. if (defined('DAEMON_EXTENDED_DEBUG') || $_REQUEST['xdebug']) {
  1108. _debug("update_rss_feed: done");
  1109. }
  1110. }
  1111. function print_select($id, $default, $values, $attributes = "") {
  1112. print "<select name=\"$id\" id=\"$id\" $attributes>";
  1113. foreach ($values as $v) {
  1114. if ($v == $default)
  1115. $sel = "selected=\"1\"";
  1116. else
  1117. $sel = "";
  1118. print "<option value=\"$v\" $sel>$v</option>";
  1119. }
  1120. print "</select>";
  1121. }
  1122. function print_select_hash($id, $default, $values, $attributes = "") {
  1123. print "<select name=\"$id\" id='$id' $attributes>";
  1124. foreach (array_keys($values) as $v) {
  1125. if ($v == $default)
  1126. $sel = 'selected="selected"';
  1127. else
  1128. $sel = "";
  1129. print "<option $sel value=\"$v\">".$values[$v]."</option>";
  1130. }
  1131. print "</select>";
  1132. }
  1133. function get_article_filters($filters, $title, $content, $link, $timestamp, $author, $tags) {
  1134. $matches = array();
  1135. if ($filters["title"]) {
  1136. foreach ($filters["title"] as $filter) {
  1137. $reg_exp = $filter["reg_exp"];
  1138. $inverse = $filter["inverse"];
  1139. if ((!$inverse && @preg_match("/$reg_exp/i", $title)) ||
  1140. ($inverse && !@preg_match("/$reg_exp/i", $title))) {
  1141. array_push($matches, array($filter["action"], $filter["action_param"]));
  1142. }
  1143. }
  1144. }
  1145. if ($filters["content"]) {
  1146. foreach ($filters["content"] as $filter) {
  1147. $reg_exp = $filter["reg_exp"];
  1148. $inverse = $filter["inverse"];
  1149. if ((!$inverse && @preg_match("/$reg_exp/i", $content)) ||
  1150. ($inverse && !@preg_match("/$reg_exp/i", $content))) {
  1151. array_push($matches, array($filter["action"], $filter["action_param"]));
  1152. }
  1153. }
  1154. }
  1155. if ($filters["both"]) {
  1156. foreach ($filters["both"] as $filter) {
  1157. $reg_exp = $filter["reg_exp"];
  1158. $inverse = $filter["inverse"];
  1159. if ($inverse) {
  1160. if (!@preg_match("/$reg_exp/i", $title) && !preg_match("/$reg_exp/i", $content)) {
  1161. array_push($matches, array($filter["action"], $filter["action_param"]));
  1162. }
  1163. } else {
  1164. if (@preg_match("/$reg_exp/i", $title) || preg_match("/$reg_exp/i", $content)) {
  1165. array_push($matches, array($filter["action"], $filter["action_param"]));
  1166. }
  1167. }
  1168. }
  1169. }
  1170. if ($filters["link"]) {
  1171. $reg_exp = $filter["reg_exp"];
  1172. foreach ($filters["link"] as $filter) {
  1173. $reg_exp = $filter["reg_exp"];
  1174. $inverse = $filter["inverse"];
  1175. if ((!$inverse && @preg_match("/$reg_exp/i", $link)) ||
  1176. ($inverse && !@preg_match("/$reg_exp/i", $link))) {
  1177. array_push($matches, array($filter["action"], $filter["action_param"]));
  1178. }
  1179. }
  1180. }
  1181. if ($filters["date"]) {
  1182. $reg_exp = $filter["reg_exp"];
  1183. foreach ($filters["date"] as $filter) {
  1184. $date_modifier = $filter["filter_param"];
  1185. $inverse = $filter["inverse"];
  1186. $check_timestamp = strtotime($filter["reg_exp"]);
  1187. # no-op when timestamp doesn't parse to prevent misfires
  1188. if ($check_timestamp) {
  1189. $match_ok = false;
  1190. if ($date_modifier == "before" && $timestamp < $check_timestamp ||
  1191. $date_modifier == "after" && $timestamp > $check_timestamp) {
  1192. $match_ok = true;
  1193. }
  1194. if ($inverse) $match_ok = !$match_ok;
  1195. if ($match_ok) {
  1196. array_push($matches, array($filter["action"], $filter["action_param"]));
  1197. }
  1198. }
  1199. }
  1200. }
  1201. if ($filters["author"]) {
  1202. foreach ($filters["author"] as $filter) {
  1203. $reg_exp = $filter["reg_exp"];
  1204. $inverse = $filter["inverse"];
  1205. if ((!$inverse && @preg_match("/$reg_exp/i", $author)) ||
  1206. ($inverse && !@preg_match("/$reg_exp/i", $author))) {
  1207. array_push($matches, array($filter["action"], $filter["action_param"]));
  1208. }
  1209. }
  1210. }
  1211. if ($filters["tag"]) {
  1212. $tag_string = join(",", $tags);
  1213. foreach ($filters["tag"] as $filter) {
  1214. $reg_exp = $filter["reg_exp"];
  1215. $inverse = $filter["inverse"];
  1216. if ((!$inverse && @preg_match("/$reg_exp/i", $tag_string)) ||
  1217. ($inverse && !@preg_match("/$reg_exp/i", $tag_string))) {
  1218. array_push($matches, array($filter["action"], $filter["action_param"]));
  1219. }
  1220. }
  1221. }
  1222. return $matches;
  1223. }
  1224. function find_article_filter($filters, $filter_name) {
  1225. foreach ($filters as $f) {
  1226. if ($f[0] == $filter_name) {
  1227. return $f;
  1228. };
  1229. }
  1230. return false;
  1231. }
  1232. function calculate_article_score($filters) {
  1233. $score = 0;
  1234. foreach ($filters as $f) {
  1235. if ($f[0] == "score") {
  1236. $score += $f[1];
  1237. };
  1238. }
  1239. return $score;
  1240. }
  1241. function assign_article_to_labels($link, $id, $filters, $owner_uid) {
  1242. foreach ($filters as $f) {
  1243. if ($f[0] == "label") {
  1244. label_add_article($link, $id, $f[1], $owner_uid);
  1245. };
  1246. }
  1247. }
  1248. function getmicrotime() {
  1249. list($usec, $sec) = explode(" ",microtime());
  1250. return ((float)$usec + (float)$sec);
  1251. }
  1252. function print_radio($id, $default, $true_is, $values, $attributes = "") {
  1253. foreach ($values as $v) {
  1254. if ($v == $default)
  1255. $sel = "checked";
  1256. else
  1257. $sel = "";
  1258. if ($v == $true_is) {
  1259. $sel .= " value=\"1\"";
  1260. } else {
  1261. $sel .= " value=\"0\"";
  1262. }
  1263. print "<input class=\"noborder\" dojoType=\"dijit.form.RadioButton\"
  1264. type=\"radio\" $sel $attributes name=\"$id\">&nbsp;$v&nbsp;";
  1265. }
  1266. }
  1267. function initialize_user_prefs($link, $uid, $profile = false) {
  1268. $uid = db_escape_string($uid);
  1269. if (!$profile) {
  1270. $profile = "NULL";
  1271. $profile_qpart = "AND profile IS NULL";
  1272. } else {
  1273. $profile_qpart = "AND profile = '$profile'";
  1274. }
  1275. if (get_schema_version($link) < 63) $profile_qpart = "";
  1276. db_query($link, "BEGIN");
  1277. $result = db_query($link, "SELECT pref_name,def_value FROM ttrss_prefs");
  1278. $u_result = db_query($link, "SELECT pref_name
  1279. FROM ttrss_user_prefs WHERE owner_uid = '$uid' $profile_qpart");
  1280. $active_prefs = array();
  1281. while ($line = db_fetch_assoc($u_result)) {
  1282. array_push($active_prefs, $line["pref_name"]);
  1283. }
  1284. while ($line = db_fetch_assoc($result)) {
  1285. if (array_search($line["pref_name"], $active_prefs) === FALSE) {
  1286. // print "adding " . $line["pref_name"] . "<br>";
  1287. if (get_schema_version($link) < 63) {
  1288. db_query($link, "INSERT INTO ttrss_user_prefs
  1289. (owner_uid,pref_name,value) VALUES
  1290. ('$uid', '".$line["pref_name"]."','".$line["def_value"]."')");
  1291. } else {
  1292. db_query($link, "INSERT INTO ttrss_user_prefs
  1293. (owner_uid,pref_name,value, profile) VALUES
  1294. ('$uid', '".$line["pref_name"]."','".$line["def_value"]."', $profile)");
  1295. }
  1296. }
  1297. }
  1298. db_query($link, "COMMIT");
  1299. }
  1300. function lookup_user_id($link, $user) {
  1301. $result = db_query($link, "SELECT id FROM ttrss_users WHERE
  1302. login = '$login'");
  1303. if (db_num_rows($result) == 1) {
  1304. return db_fetch_result($result, 0, "id");
  1305. } else {
  1306. return false;
  1307. }
  1308. }
  1309. function http_authenticate_user($link) {
  1310. // error_log("http_authenticate_user: ".$_SERVER["PHP_AUTH_USER"]."\n", 3, '/tmp/tt-rss.log');
  1311. if (!$_SERVER["PHP_AUTH_USER"]) {
  1312. header('WWW-Authenticate: Basic realm="Tiny Tiny RSS RSSGen"');
  1313. header('HTTP/1.0 401 Unauthorized');
  1314. exit;
  1315. } else {
  1316. $auth_result = authenticate_user($link,
  1317. $_SERVER["PHP_AUTH_USER"], $_SERVER["PHP_AUTH_PW"]);
  1318. if (!$auth_result) {
  1319. header('WWW-Authenticate: Basic realm="Tiny Tiny RSS RSSGen"');
  1320. header('HTTP/1.0 401 Unauthorized');
  1321. exit;
  1322. }
  1323. }
  1324. return true;
  1325. }
  1326. function authenticate_user($link, $login, $password, $force_auth = false) {
  1327. if (!SINGLE_USER_MODE) {
  1328. $pwd_hash1 = encrypt_password($password);
  1329. $pwd_hash2 = encrypt_password($password, $login);
  1330. $login = db_escape_string($login);
  1331. if (defined('ALLOW_REMOTE_USER_AUTH') && ALLOW_REMOTE_USER_AUTH
  1332. && $_SERVER["REMOTE_USER"] && $login != "admin") {
  1333. $login = db_escape_string($_SERVER["REMOTE_USER"]);
  1334. $query = "SELECT id,login,access_level,pwd_hash
  1335. FROM ttrss_users WHERE
  1336. login = '$login'";
  1337. } else {
  1338. $query = "SELECT id,login,access_level,pwd_hash
  1339. FROM ttrss_users WHERE
  1340. login = '$login' AND (pwd_hash = '$pwd_hash1' OR
  1341. pwd_hash = '$pwd_hash2')";
  1342. }
  1343. $result = db_query($link, $query);
  1344. if (db_num_rows($result) == 1) {
  1345. $_SESSION["uid"] = db_fetch_result($result, 0, "id");
  1346. $_SESSION["name"] = db_fetch_result($result, 0, "login");
  1347. $_SESSION["access_level"] = db_fetch_result($result, 0, "access_level");
  1348. db_query($link, "UPDATE ttrss_users SET last_login = NOW() WHERE id = " .
  1349. $_SESSION["uid"]);
  1350. $_SESSION["ip_address"] = $_SERVER["REMOTE_ADDR"];
  1351. $_SESSION["pwd_hash"] = db_fetch_result($result, 0, "pwd_hash");
  1352. $_SESSION["last_version_check"] = time();
  1353. initialize_user_prefs($link, $_SESSION["uid"]);
  1354. return true;
  1355. }
  1356. return false;
  1357. } else {
  1358. $_SESSION["uid"] = 1;
  1359. $_SESSION["name"] = "admin";
  1360. $_SESSION["ip_address"] = $_SERVER["REMOTE_ADDR"];
  1361. initialize_user_prefs($link, $_SESSION["uid"]);
  1362. return true;
  1363. }
  1364. }
  1365. function make_password($length = 8) {
  1366. $password = "";
  1367. $possible = "0123456789abcdfghjkmnpqrstvwxyzABCDFGHJKMNPQRSTVWXYZ";
  1368. $i = 0;
  1369. while ($i < $length) {
  1370. $char = substr($possible, mt_rand(0, strlen($possible)-1), 1);
  1371. if (!strstr($password, $char)) {
  1372. $password .= $char;
  1373. $i++;
  1374. }
  1375. }
  1376. return $password;
  1377. }
  1378. // this is called after user is created to initialize default feeds, labels
  1379. // or whatever else
  1380. // user preferences are checked on every login, not here
  1381. function initialize_user($link, $uid) {
  1382. db_query($link, "insert into ttrss_feeds (owner_uid,title,feed_url)
  1383. values ('$uid', 'Tiny Tiny RSS: New Releases',
  1384. 'http://tt-rss.org/releases.rss')");
  1385. db_query($link, "insert into ttrss_feeds (owner_uid,title,feed_url)
  1386. values ('$uid', 'Tiny Tiny RSS: Forum',
  1387. 'http://tt-rss.org/forum/rss.php')");
  1388. }
  1389. function logout_user() {
  1390. session_destroy();
  1391. if (isset($_COOKIE[session_name()])) {
  1392. setcookie(session_name(), '', time()-42000, '/');
  1393. }
  1394. }
  1395. function get_script_urlpath() {
  1396. return preg_replace('/\/[^\/]*$/', "", $_SERVER["REQUEST_URI"]);
  1397. }
  1398. function validate_session($link) {
  1399. if (SINGLE_USER_MODE) return true;
  1400. $check_ip = $_SESSION['ip_address'];
  1401. switch (SESSION_CHECK_ADDRESS) {
  1402. case 0:
  1403. $check_ip = '';
  1404. break;
  1405. case 1:
  1406. $check_ip = substr($check_ip, 0, strrpos($check_ip, '.')+1);
  1407. break;
  1408. case 2:
  1409. $check_ip = substr($check_ip, 0, strrpos($check_ip, '.'));
  1410. $check_ip = substr($check_ip, 0, strrpos($check_ip, '.')+1);
  1411. break;
  1412. };
  1413. if ($check_ip && strpos($_SERVER['REMOTE_ADDR'], $check_ip) !== 0) {
  1414. $_SESSION["login_error_msg"] =
  1415. __("Session failed to validate (incorrect IP)");
  1416. return false;
  1417. }
  1418. if ($_SESSION["ref_schema_version"] != get_schema_version($link, true))
  1419. return false;
  1420. if ($_SESSION["uid"]) {
  1421. $result = db_query($link,
  1422. "SELECT pwd_hash FROM ttrss_users WHERE id = '".$_SESSION["uid"]."'");
  1423. $pwd_hash = db_fetch_result($result, 0, "pwd_hash");
  1424. if ($pwd_hash != $_SESSION["pwd_hash"]) {
  1425. return false;
  1426. }
  1427. }
  1428. /* if ($_SESSION["cookie_lifetime"] && $_SESSION["uid"]) {
  1429. //print_r($_SESSION);
  1430. if (time() > $_SESSION["cookie_lifetime"]) {
  1431. return false;
  1432. }
  1433. } */
  1434. return true;
  1435. }
  1436. function login_sequence($link, $mobile = false) {
  1437. if (!SINGLE_USER_MODE) {
  1438. $login_action = $_POST["login_action"];
  1439. # try to authenticate user if called from login form
  1440. if ($login_action == "do_login") {
  1441. $login = $_POST["login"];
  1442. $password = $_POST["password"];
  1443. $remember_me = $_POST["remember_me"];
  1444. if (authenticate_user($link, $login, $password)) {
  1445. $_POST["password"] = "";
  1446. $_SESSION["language"] = $_POST["language"];
  1447. $_SESSION["ref_schema_version"] = get_schema_version($link, true);
  1448. $_SESSION["bw_limit"] = !!$_POST["bw_limit"];
  1449. if ($_POST["profile"]) {
  1450. $profile = db_escape_string($_POST["profile"]);
  1451. $result = db_query($link, "SELECT id FROM ttrss_settings_profiles
  1452. WHERE id = '$profile' AND owner_uid = " . $_SESSION["uid"]);
  1453. if (db_num_rows($result) != 0) {
  1454. $_SESSION["profile"] = $profile;
  1455. $_SESSION["prefs_cache"] = array();
  1456. }
  1457. }
  1458. if ($_REQUEST['return']) {
  1459. header("Location: " . $_REQUEST['return']);
  1460. } else {
  1461. header("Location: " . $_SERVER["REQUEST_URI"]);
  1462. }
  1463. exit;
  1464. return;
  1465. } else {
  1466. $_SESSION["login_error_msg"] = __("Incorrect username or password");
  1467. }
  1468. }
  1469. if (!$_SESSION["uid"] || !validate_session($link)) {
  1470. if (defined('ALLOW_REMOTE_USER_AUTH') && ALLOW_REMOTE_USER_AUTH
  1471. && $_SERVER["REMOTE_USER"] && defined('AUTO_LOGIN') && AUTO_LOGIN) {
  1472. authenticate_user($link,$_SERVER['REMOTE_USER'],null);
  1473. $_SESSION["ref_schema_version"] = get_schema_version($link, true);
  1474. } else {
  1475. render_login_form($link, $mobile);
  1476. //header("Location: login.php");
  1477. exit;
  1478. }
  1479. } else {
  1480. /* bump login timestamp */
  1481. db_query($link, "UPDATE ttrss_users SET last_login = NOW() WHERE id = " .
  1482. $_SESSION["uid"]);
  1483. if ($_SESSION["language"] && SESSION_COOKIE_LIFETIME > 0) {
  1484. setcookie("ttrss_lang", $_SESSION["language"],
  1485. time() + SESSION_COOKIE_LIFETIME);
  1486. }
  1487. }
  1488. } else {
  1489. return authenticate_user($link, "admin", null);
  1490. }
  1491. }
  1492. function truncate_string($str, $max_len, $suffix = '&hellip;') {
  1493. if (mb_strlen($str, "utf-8") > $max_len - 3) {
  1494. return mb_substr($str, 0, $max_len, "utf-8") . $suffix;
  1495. } else {
  1496. return $str;
  1497. }
  1498. }
  1499. function theme_image($link, $filename) {
  1500. if ($link) {
  1501. $theme_path = get_user_theme_path($link);
  1502. if ($theme_path && is_file($theme_path.$filename)) {
  1503. return $theme_path.$filename;
  1504. } else {
  1505. return $filename;
  1506. }
  1507. } else {
  1508. return $filename;
  1509. }
  1510. }
  1511. function get_user_theme($link) {
  1512. if (get_schema_version($link) >= 63 && $_SESSION["uid"]) {
  1513. $theme_name = get_pref($link, "_THEME_ID");
  1514. if (is_dir("themes/$theme_name")) {
  1515. return $theme_name;
  1516. } else {
  1517. return '';
  1518. }
  1519. } else {
  1520. return '';
  1521. }
  1522. }
  1523. function get_user_theme_path($link) {
  1524. $theme_path = '';
  1525. if (get_schema_version($link) >= 63 && $_SESSION["uid"]) {
  1526. $theme_name = get_pref($link, "_THEME_ID");
  1527. if ($theme_name && is_dir("themes/$theme_name")) {
  1528. $theme_path = "themes/$theme_name/";
  1529. } else {
  1530. $theme_name = '';
  1531. }
  1532. } else {
  1533. $theme_path = '';
  1534. }
  1535. if ($theme_path) {
  1536. if (is_file("$theme_path/theme.ini")) {
  1537. $ini = parse_ini_file("$theme_path/theme.ini", true);
  1538. if ($ini['theme']['version'] >= THEME_VERSION_REQUIRED) {
  1539. return $theme_path;
  1540. }
  1541. }
  1542. }
  1543. return '';
  1544. }
  1545. function get_user_theme_options($link) {
  1546. $t = get_user_theme_path($link);
  1547. if ($t) {
  1548. if (is_file("$t/theme.ini")) {
  1549. $ini = parse_ini_file("$t/theme.ini", true);
  1550. if ($ini['theme']['version']) {
  1551. return $ini['theme']['options'];
  1552. }
  1553. }
  1554. }
  1555. return '';
  1556. }
  1557. function print_theme_includes($link) {
  1558. $t = get_user_theme_path($link);
  1559. $time = time();
  1560. if ($t) {
  1561. print "<link rel=\"stylesheet\" type=\"text/css\"
  1562. href=\"$t/theme.css?$time \">";
  1563. if (file_exists("$t/theme.js")) {
  1564. print "<script type=\"text/javascript\" src=\"$t/theme.js?$time\">
  1565. </script>";
  1566. }
  1567. }
  1568. }
  1569. function get_all_themes() {
  1570. $themes = glob("themes/*");
  1571. asort($themes);
  1572. $rv = array();
  1573. foreach ($themes as $t) {
  1574. if (is_file("$t/theme.ini")) {
  1575. $ini = parse_ini_file("$t/theme.ini", true);
  1576. if ($ini['theme']['version'] >= THEME_VERSION_REQUIRED &&
  1577. !$ini['theme']['disabled']) {
  1578. $entry = array();
  1579. $entry["path"] = $t;
  1580. $entry["base"] = basename($t);
  1581. $entry["name"] = $ini['theme']['name'];
  1582. $entry["version"] = $ini['theme']['version'];
  1583. $entry["author"] = $ini['theme']['author'];
  1584. $entry["options"] = $ini['theme']['options'];
  1585. array_push($rv, $entry);
  1586. }
  1587. }
  1588. }
  1589. return $rv;
  1590. }
  1591. function convert_timestamp($timestamp, $source_tz, $dest_tz) {
  1592. try {
  1593. $source_tz = new DateTimeZone($source_tz);
  1594. } catch (Exception $e) {
  1595. $source_tz = new DateTimeZone('UTC');
  1596. }
  1597. try {
  1598. $dest_tz = new DateTimeZone($dest_tz);
  1599. } catch (Exception $e) {
  1600. $dest_tz = new DateTimeZone('UTC');
  1601. }
  1602. $dt = new DateTime(date('Y-m-d H:i:s', $timestamp), $source_tz);
  1603. return $dt->format('U') + $dest_tz->getOffset($dt);
  1604. }
  1605. function make_local_datetime($link, $timestamp, $long, $owner_uid = false,
  1606. $no_smart_dt = false) {
  1607. if (!$owner_uid) $owner_uid = $_SESSION['uid'];
  1608. if (!$timestamp) $timestamp = '1970-01-01 0:00';
  1609. $user_tz_string = get_pref($link, 'USER_TIMEZONE', $owner_uid);
  1610. try {
  1611. $user_tz = new DateTimeZone($user_tz_string);
  1612. } catch (Exception $e) {
  1613. $user_tz = new DateTimeZone('UTC');
  1614. }
  1615. # We store date in UTC internally
  1616. $dt = new DateTime($timestamp, new DateTimeZone('UTC'));
  1617. $user_timestamp = $dt->format('U') + $user_tz->getOffset($dt);
  1618. if (!$no_smart_dt && get_pref($link, 'HEADLINES_SMART_DATE', $owner_uid)) {
  1619. return smart_date_time($link, $user_timestamp,
  1620. $user_tz->getOffset($dt), $owner_uid);
  1621. } else {
  1622. if ($long)
  1623. $format = get_pref($link, 'LONG_DATE_FORMAT', $owner_uid);
  1624. else
  1625. $format = get_pref($link, 'SHORT_DATE_FORMAT', $owner_uid);
  1626. return date($format, $user_timestamp);
  1627. }
  1628. }
  1629. function smart_date_time($link, $timestamp, $tz_offset = 0, $owner_uid = false) {
  1630. if (!$owner_uid) $owner_uid = $_SESSION['uid'];
  1631. if (date("Y.m.d", $timestamp) == date("Y.m.d", time() + $tz_offset)) {
  1632. return date("G:i", $timestamp);
  1633. } else if (date("Y", $timestamp) == date("Y", time() + $tz_offset)) {
  1634. $format = get_pref($link, 'SHORT_DATE_FORMAT', $owner_uid);
  1635. return date($format, $timestamp);
  1636. } else {
  1637. $format = get_pref($link, 'LONG_DATE_FORMAT', $owner_uid);
  1638. return date($format, $timestamp);
  1639. }
  1640. }
  1641. function smart_date($timestamp) {
  1642. if (date("Y.m.d", $timestamp) == date("Y.m.d")) {
  1643. return "Today";
  1644. } else if (date("Y", $timestamp) == date("Y")) {
  1645. return date("D m", $timestamp);
  1646. } else {
  1647. return date("Y/m/d", $timestamp);
  1648. }
  1649. }
  1650. function sql_bool_to_string($s) {
  1651. if ($s == "t" || $s == "1") {
  1652. return "true";
  1653. } else {
  1654. return "false";
  1655. }
  1656. }
  1657. function sql_bool_to_bool($s) {
  1658. if ($s == "t" || $s == "1") {
  1659. return true;
  1660. } else {
  1661. return false;
  1662. }
  1663. }
  1664. function bool_to_sql_bool($s) {
  1665. if ($s) {
  1666. return "true";
  1667. } else {
  1668. return "false";
  1669. }
  1670. }
  1671. function toggleEvenOdd($a) {
  1672. if ($a == "even")
  1673. return "odd";
  1674. else
  1675. return "even";
  1676. }
  1677. function get_schema_version($link, $nocache = false) {
  1678. if (!$_SESSION["schema_version"] || $nocache) {
  1679. $result = db_query($link, "SELECT schema_version FROM ttrss_version");
  1680. $version = db_fetch_result($result, 0, "schema_version");
  1681. $_SESSION["schema_version"] = $version;
  1682. return $version;
  1683. } else {
  1684. return $_SESSION["schema_version"];
  1685. }
  1686. }
  1687. function sanity_check($link) {
  1688. global $ERRORS;
  1689. $error_code = 0;
  1690. $schema_version = get_schema_version($link);
  1691. if ($schema_version != SCHEMA_VERSION) {
  1692. $error_code = 5;
  1693. }
  1694. if (DB_TYPE == "mysql") {
  1695. $result = db_query($link, "SELECT true", false);
  1696. if (db_num_rows($result) != 1) {
  1697. $error_code = 10;
  1698. }
  1699. }
  1700. if (db_escape_string("testTEST") != "testTEST") {
  1701. $error_code = 12;
  1702. }
  1703. return array("code" => $error_code, "message" => $ERRORS[$error_code]);
  1704. }
  1705. function file_is_locked($filename) {
  1706. if (function_exists('flock')) {
  1707. $fp = @fopen(LOCK_DIRECTORY . "/$filename", "r");
  1708. if ($fp) {
  1709. if (flock($fp, LOCK_EX | LOCK_NB)) {
  1710. flock($fp, LOCK_UN);
  1711. fclose($fp);
  1712. return false;
  1713. }
  1714. fclose($fp);
  1715. return true;
  1716. } else {
  1717. return false;
  1718. }
  1719. }
  1720. return true; // consider the file always locked and skip the test
  1721. }
  1722. function make_lockfile($filename) {
  1723. $fp = fopen(LOCK_DIRECTORY . "/$filename", "w");
  1724. if (flock($fp, LOCK_EX | LOCK_NB)) {
  1725. if (function_exists('posix_getpid')) {
  1726. fwrite($fp, posix_getpid() . "\n");
  1727. }
  1728. return $fp;
  1729. } else {
  1730. return false;
  1731. }
  1732. }
  1733. function make_stampfile($filename) {
  1734. $fp = fopen(LOCK_DIRECTORY . "/$filename", "w");
  1735. if (flock($fp, LOCK_EX | LOCK_NB)) {
  1736. fwrite($fp, time() . "\n");
  1737. flock($fp, LOCK_UN);
  1738. fclose($fp);
  1739. return true;
  1740. } else {
  1741. return false;
  1742. }
  1743. }
  1744. function sql_random_function() {
  1745. if (DB_TYPE == "mysql") {
  1746. return "RAND()";
  1747. } else {
  1748. return "RANDOM()";
  1749. }
  1750. }
  1751. function catchup_feed($link, $feed, $cat_view, $owner_uid = false) {
  1752. if (!$owner_uid) $owner_uid = $_SESSION['uid'];
  1753. if (preg_match("/^-?[0-9][0-9]*$/", $feed) != false) {
  1754. if ($cat_view) {
  1755. if ($feed >= 0) {
  1756. if ($feed > 0) {
  1757. $cat_qpart = "cat_id = '$feed'";
  1758. } else {
  1759. $cat_qpart = "cat_id IS NULL";
  1760. }
  1761. $tmp_result = db_query($link, "SELECT id
  1762. FROM ttrss_feeds WHERE $cat_qpart AND owner_uid = $owner_uid");
  1763. while ($tmp_line = db_fetch_assoc($tmp_result)) {
  1764. $tmp_feed = $tmp_line["id"];
  1765. db_query($link, "UPDATE ttrss_user_entries
  1766. SET unread = false,last_read = NOW()
  1767. WHERE feed_id = '$tmp_feed' AND owner_uid = $owner_uid");
  1768. }
  1769. } else if ($feed == -2) {
  1770. db_query($link, "UPDATE ttrss_user_entries
  1771. SET unread = false,last_read = NOW() WHERE (SELECT COUNT(*)
  1772. FROM ttrss_user_labels2 WHERE article_id = ref_id) > 0
  1773. AND unread = true AND owner_uid = $owner_uid");
  1774. }
  1775. } else if ($feed > 0) {
  1776. db_query($link, "UPDATE ttrss_user_entries
  1777. SET unread = false,last_read = NOW()
  1778. WHERE feed_id = '$feed' AND owner_uid = $owner_uid");
  1779. } else if ($feed < 0 && $feed > -10) { // special, like starred
  1780. if ($feed == -1) {
  1781. db_query($link, "UPDATE ttrss_user_entries
  1782. SET unread = false,last_read = NOW()
  1783. WHERE marked = true AND owner_uid = $owner_uid");
  1784. }
  1785. if ($feed == -2) {
  1786. db_query($link, "UPDATE ttrss_user_entries
  1787. SET unread = false,last_read = NOW()
  1788. WHERE published = true AND owner_uid = $owner_uid");
  1789. }
  1790. if ($feed == -3) {
  1791. $intl = get_pref($link, "FRESH_ARTICLE_MAX_AGE");
  1792. if (DB_TYPE == "pgsql") {
  1793. $match_part = "updated > NOW() - INTERVAL '$intl hour' ";
  1794. } else {
  1795. $match_part = "updated > DATE_SUB(NOW(),
  1796. INTERVAL $intl HOUR) ";
  1797. }
  1798. $result = db_query($link, "SELECT id FROM ttrss_entries,
  1799. ttrss_user_entries WHERE $match_part AND
  1800. unread = true AND
  1801. ttrss_user_entries.ref_id = ttrss_entries.id AND
  1802. owner_uid = $owner_uid");
  1803. $affected_ids = array();
  1804. while ($line = db_fetch_assoc($result)) {
  1805. array_push($affected_ids, $line["id"]);
  1806. }
  1807. catchupArticlesById($link, $affected_ids, 0);
  1808. }
  1809. if ($feed == -4) {
  1810. db_query($link, "UPDATE ttrss_user_entries
  1811. SET unread = false,last_read = NOW()
  1812. WHERE owner_uid = $owner_uid");
  1813. }
  1814. } else if ($feed < -10) { // label
  1815. $label_id = -$feed - 11;
  1816. db_query($link, "UPDATE ttrss_user_entries, ttrss_user_labels2
  1817. SET unread = false, last_read = NOW()
  1818. WHERE label_id = '$label_id' AND unread = true
  1819. AND owner_uid = '$owner_uid' AND ref_id = article_id");
  1820. }
  1821. ccache_update($link, $feed, $owner_uid, $cat_view);
  1822. } else { // tag
  1823. db_query($link, "BEGIN");
  1824. $tag_name = db_escape_string($feed);
  1825. $result = db_query($link, "SELECT post_int_id FROM ttrss_tags
  1826. WHERE tag_name = '$tag_name' AND owner_uid = $owner_uid");
  1827. while ($line = db_fetch_assoc($result)) {
  1828. db_query($link, "UPDATE ttrss_user_entries SET
  1829. unread = false, last_read = NOW()
  1830. WHERE int_id = " . $line["post_int_id"]);
  1831. }
  1832. db_query($link, "COMMIT");
  1833. }
  1834. }
  1835. function getAllCounters($link, $omode = "flc", $active_feed = false) {
  1836. if (!$omode) $omode = "flc";
  1837. $data = getGlobalCounters($link);
  1838. $data = array_merge($data, getVirtCounters($link));
  1839. if (strchr($omode, "l")) $data = array_merge($data, getLabelCounters($link));
  1840. if (strchr($omode, "f")) $data = array_merge($data, getFeedCounters($link, $active_feed));
  1841. if (strchr($omode, "t")) $data = array_merge($data, getTagCounters($link));
  1842. if (strchr($omode, "c")) {
  1843. if (get_pref($link, 'ENABLE_FEED_CATS')) {
  1844. $data = array_merge($data, getCategoryCounters($link));
  1845. }
  1846. }
  1847. return $data;
  1848. }
  1849. function getCategoryCounters($link) {
  1850. $ret_arr = array();
  1851. /* Labels category */
  1852. $cv = array("id" => -2, "kind" => "cat",
  1853. "counter" => getCategoryUnread($link, -2));
  1854. array_push($ret_arr, $cv);
  1855. $age_qpart = getMaxAgeSubquery();
  1856. $result = db_query($link, "SELECT id AS cat_id, value AS unread
  1857. FROM ttrss_feed_categories, ttrss_cat_counters_cache
  1858. WHERE ttrss_cat_counters_cache.feed_id = id AND
  1859. ttrss_feed_categories.owner_uid = " . $_SESSION["uid"]);
  1860. while ($line = db_fetch_assoc($result)) {
  1861. $line["cat_id"] = (int) $line["cat_id"];
  1862. $cv = array("id" => $line["cat_id"], "kind" => "cat",
  1863. "counter" => $line["unread"]);
  1864. array_push($ret_arr, $cv);
  1865. }
  1866. /* Special case: NULL category doesn't actually exist in the DB */
  1867. $cv = array("id" => 0, "kind" => "cat",
  1868. "counter" => ccache_find($link, 0, $_SESSION["uid"], true));
  1869. array_push($ret_arr, $cv);
  1870. return $ret_arr;
  1871. }
  1872. function getCategoryUnread($link, $cat, $owner_uid = false) {
  1873. if (!$owner_uid) $owner_uid = $_SESSION["uid"];
  1874. if ($cat >= 0) {
  1875. if ($cat != 0) {
  1876. $cat_query = "cat_id = '$cat'";
  1877. } else {
  1878. $cat_query = "cat_id IS NULL";
  1879. }
  1880. $age_qpart = getMaxAgeSubquery();
  1881. $result = db_query($link, "SELECT id FROM ttrss_feeds WHERE $cat_query
  1882. AND owner_uid = " . $owner_uid);
  1883. $cat_feeds = array();
  1884. while ($line = db_fetch_assoc($result)) {
  1885. array_push($cat_feeds, "feed_id = " . $line["id"]);
  1886. }
  1887. if (count($cat_feeds) == 0) return 0;
  1888. $match_part = implode(" OR ", $cat_feeds);
  1889. $result = db_query($link, "SELECT COUNT(int_id) AS unread
  1890. FROM ttrss_user_entries,ttrss_entries
  1891. WHERE unread = true AND ($match_part) AND id = ref_id
  1892. AND $age_qpart AND owner_uid = " . $owner_uid);
  1893. $unread = 0;
  1894. # this needs to be rewritten
  1895. while ($line = db_fetch_assoc($result)) {
  1896. $unread += $line["unread"];
  1897. }
  1898. return $unread;
  1899. } else if ($cat == -1) {
  1900. return getFeedUnread($link, -1) + getFeedUnread($link, -2) + getFeedUnread($link, -3) + getFeedUnread($link, 0);
  1901. } else if ($cat == -2) {
  1902. $result = db_query($link, "
  1903. SELECT COUNT(unread) AS unread FROM
  1904. ttrss_user_entries, ttrss_labels2, ttrss_user_labels2, ttrss_feeds
  1905. WHERE label_id = ttrss_labels2.id AND article_id = ref_id AND
  1906. ttrss_labels2.owner_uid = '$owner_uid'
  1907. AND unread = true AND feed_id = ttrss_feeds.id
  1908. AND ttrss_user_entries.owner_uid = '$owner_uid'");
  1909. $unread = db_fetch_result($result, 0, "unread");
  1910. return $unread;
  1911. }
  1912. }
  1913. function getMaxAgeSubquery($days = COUNTERS_MAX_AGE) {
  1914. if (DB_TYPE == "pgsql") {
  1915. return "ttrss_entries.date_updated >
  1916. NOW() - INTERVAL '$days days'";
  1917. } else {
  1918. return "ttrss_entries.date_updated >
  1919. DATE_SUB(NOW(), INTERVAL $days DAY)";
  1920. }
  1921. }
  1922. function getFeedUnread($link, $feed, $is_cat = false) {
  1923. return getFeedArticles($link, $feed, $is_cat, true, $_SESSION["uid"]);
  1924. }
  1925. function getLabelUnread($link, $label_id, $owner_uid = false) {
  1926. if (!$owner_uid) $owner_uid = $_SESSION["uid"];
  1927. $result = db_query($link, "
  1928. SELECT COUNT(unread) AS unread FROM
  1929. ttrss_user_entries, ttrss_labels2, ttrss_user_labels2, ttrss_feeds
  1930. WHERE label_id = ttrss_labels2.id AND article_id = ref_id AND
  1931. ttrss_labels2.owner_uid = '$owner_uid' AND ttrss_labels2.id = '$label_id'
  1932. AND unread = true AND feed_id = ttrss_feeds.id
  1933. AND ttrss_user_entries.owner_uid = '$owner_uid'");
  1934. if (db_num_rows($result) != 0) {
  1935. return db_fetch_result($result, 0, "unread");
  1936. } else {
  1937. return 0;
  1938. }
  1939. }
  1940. function getFeedArticles($link, $feed, $is_cat = false, $unread_only = false,
  1941. $owner_uid = false) {
  1942. $n_feed = (int) $feed;
  1943. if (!$owner_uid) $owner_uid = $_SESSION["uid"];
  1944. if ($unread_only) {
  1945. $unread_qpart = "unread = true";
  1946. } else {
  1947. $unread_qpart = "true";
  1948. }
  1949. $age_qpart = getMaxAgeSubquery();
  1950. if ($is_cat) {
  1951. return getCategoryUnread($link, $n_feed, $owner_uid);
  1952. } if ($feed != "0" && $n_feed == 0) {
  1953. $feed = db_escape_string($feed);
  1954. $result = db_query($link, "SELECT SUM((SELECT COUNT(int_id)
  1955. FROM ttrss_user_entries,ttrss_entries WHERE int_id = post_int_id
  1956. AND ref_id = id AND $age_qpart
  1957. AND $unread_qpart)) AS count FROM ttrss_tags
  1958. WHERE owner_uid = $owner_uid AND tag_name = '$feed'");
  1959. return db_fetch_result($result, 0, "count");
  1960. } else if ($n_feed == -1) {
  1961. $match_part = "marked = true";
  1962. } else if ($n_feed == -2) {
  1963. $match_part = "published = true";
  1964. } else if ($n_feed == -3) {
  1965. $match_part = "unread = true AND score >= 0";
  1966. $intl = get_pref($link, "FRESH_ARTICLE_MAX_AGE", $owner_uid);
  1967. if (DB_TYPE == "pgsql") {
  1968. $match_part .= " AND updated > NOW() - INTERVAL '$intl hour' ";
  1969. } else {
  1970. $match_part .= " AND updated > DATE_SUB(NOW(), INTERVAL $intl HOUR) ";
  1971. }
  1972. } else if ($n_feed == -4) {
  1973. $match_part = "true";
  1974. } else if ($n_feed >= 0) {
  1975. if ($n_feed != 0) {
  1976. $match_part = "feed_id = '$n_feed'";
  1977. } else {
  1978. $match_part = "feed_id IS NULL";
  1979. }
  1980. } else if ($feed < -10) {
  1981. $label_id = -$feed - 11;
  1982. return getLabelUnread($link, $label_id, $owner_uid);
  1983. }
  1984. if ($match_part) {
  1985. if ($n_feed != 0) {
  1986. $from_qpart = "ttrss_user_entries,ttrss_feeds,ttrss_entries";
  1987. $feeds_qpart = "ttrss_user_entries.feed_id = ttrss_feeds.id AND";
  1988. } else {
  1989. $from_qpart = "ttrss_user_entries,ttrss_entries";
  1990. $feeds_qpart = '';
  1991. }
  1992. $query = "SELECT count(int_id) AS unread
  1993. FROM $from_qpart WHERE
  1994. ttrss_user_entries.ref_id = ttrss_entries.id AND
  1995. $age_qpart AND
  1996. $feeds_qpart
  1997. $unread_qpart AND ($match_part) AND ttrss_user_entries.owner_uid = $owner_uid";
  1998. $result = db_query($link, $query);
  1999. } else {
  2000. $result = db_query($link, "SELECT COUNT(post_int_id) AS unread
  2001. FROM ttrss_tags,ttrss_user_entries,ttrss_entries
  2002. WHERE tag_name = '$feed' AND post_int_id = int_id AND ref_id = ttrss_entries.id
  2003. AND $unread_qpart AND $age_qpart AND
  2004. ttrss_tags.owner_uid = " . $owner_uid);
  2005. }
  2006. $unread = db_fetch_result($result, 0, "unread");
  2007. return $unread;
  2008. }
  2009. function getGlobalUnread($link, $user_id = false) {
  2010. if (!$user_id) {
  2011. $user_id = $_SESSION["uid"];
  2012. }
  2013. $result = db_query($link, "SELECT SUM(value) AS c_id FROM ttrss_counters_cache
  2014. WHERE owner_uid = '$user_id' AND feed_id > 0");
  2015. $c_id = db_fetch_result($result, 0, "c_id");
  2016. return $c_id;
  2017. }
  2018. function getGlobalCounters($link, $global_unread = -1) {
  2019. $ret_arr = array();
  2020. if ($global_unread == -1) {
  2021. $global_unread = getGlobalUnread($link);
  2022. }
  2023. $cv = array("id" => "global-unread",
  2024. "counter" => $global_unread);
  2025. array_push($ret_arr, $cv);
  2026. $result = db_query($link, "SELECT COUNT(id) AS fn FROM
  2027. ttrss_feeds WHERE owner_uid = " . $_SESSION["uid"]);
  2028. $subscribed_feeds = db_fetch_result($result, 0, "fn");
  2029. $cv = array("id" => "subscribed-feeds",
  2030. "counter" => $subscribed_feeds);
  2031. array_push($ret_arr, $cv);
  2032. return $ret_arr;
  2033. }
  2034. function getSubscribedFeeds($link) {
  2035. $result = db_query($link, "SELECT COUNT(id) AS fn FROM
  2036. ttrss_feeds WHERE owner_uid = " . $_SESSION["uid"]);
  2037. return db_fetch_result($result, 0, "fn");
  2038. }
  2039. function getTagCounters($link) {
  2040. $ret_arr = array();
  2041. $age_qpart = getMaxAgeSubquery();
  2042. $result = db_query($link, "SELECT tag_name,SUM((SELECT COUNT(int_id)
  2043. FROM ttrss_user_entries,ttrss_entries WHERE int_id = post_int_id
  2044. AND ref_id = id AND $age_qpart
  2045. AND unread = true)) AS count FROM ttrss_tags
  2046. WHERE owner_uid = ".$_SESSION['uid']." GROUP BY tag_name
  2047. ORDER BY count DESC LIMIT 55");
  2048. $tags = array();
  2049. while ($line = db_fetch_assoc($result)) {
  2050. $tags[$line["tag_name"]] += $line["count"];
  2051. }
  2052. foreach (array_keys($tags) as $tag) {
  2053. $unread = $tags[$tag];
  2054. $tag = htmlspecialchars($tag);
  2055. $cv = array("id" => $tag,
  2056. "kind" => "tag",
  2057. "counter" => $unread);
  2058. array_push($ret_arr, $cv);
  2059. }
  2060. return $ret_arr;
  2061. }
  2062. function getVirtCounters($link) {
  2063. $ret_arr = array();
  2064. for ($i = 0; $i >= -4; $i--) {
  2065. $count = getFeedUnread($link, $i);
  2066. $cv = array("id" => $i,
  2067. "counter" => $count);
  2068. // if (get_pref($link, 'EXTENDED_FEEDLIST'))
  2069. // $cv["xmsg"] = getFeedArticles($link, $i)." ".__("total");
  2070. array_push($ret_arr, $cv);
  2071. }
  2072. return $ret_arr;
  2073. }
  2074. function getLabelCounters($link, $descriptions = false) {
  2075. $ret_arr = array();
  2076. $age_qpart = getMaxAgeSubquery();
  2077. $owner_uid = $_SESSION["uid"];
  2078. $result = db_query($link, "SELECT id, caption FROM ttrss_labels2
  2079. WHERE owner_uid = '$owner_uid'");
  2080. while ($line = db_fetch_assoc($result)) {
  2081. $id = -$line["id"] - 11;
  2082. $label_name = $line["caption"];
  2083. $count = getFeedUnread($link, $id);
  2084. $cv = array("id" => $id,
  2085. "counter" => $count);
  2086. if ($descriptions)
  2087. $cv["description"] = $label_name;
  2088. // if (get_pref($link, 'EXTENDED_FEEDLIST'))
  2089. // $cv["xmsg"] = getFeedArticles($link, $id)." ".__("total");
  2090. array_push($ret_arr, $cv);
  2091. }
  2092. return $ret_arr;
  2093. }
  2094. function getFeedCounters($link, $active_feed = false) {
  2095. $ret_arr = array();
  2096. $age_qpart = getMaxAgeSubquery();
  2097. $query = "SELECT ttrss_feeds.id,
  2098. ttrss_feeds.title,
  2099. ".SUBSTRING_FOR_DATE."(ttrss_feeds.last_updated,1,19) AS last_updated,
  2100. last_error, value AS count
  2101. FROM ttrss_feeds, ttrss_counters_cache
  2102. WHERE ttrss_feeds.owner_uid = ".$_SESSION["uid"]."
  2103. AND ttrss_counters_cache.feed_id = id";
  2104. $result = db_query($link, $query);
  2105. $fctrs_modified = false;
  2106. while ($line = db_fetch_assoc($result)) {
  2107. $id = $line["id"];
  2108. $count = $line["count"];
  2109. $last_error = htmlspecialchars($line["last_error"]);
  2110. $last_updated = make_local_datetime($link, $line['last_updated'], false);
  2111. $has_img = feed_has_icon($id);
  2112. if (date('Y') - date('Y', strtotime($line['last_updated'])) > 2)
  2113. $last_updated = '';
  2114. $cv = array("id" => $id,
  2115. "updated" => $last_updated,
  2116. "counter" => $count,
  2117. "has_img" => (int) $has_img);
  2118. if ($last_error)
  2119. $cv["error"] = $last_error;
  2120. // if (get_pref($link, 'EXTENDED_FEEDLIST'))
  2121. // $cv["xmsg"] = getFeedArticles($link, $id)." ".__("total");
  2122. if ($active_feed && $id == $active_feed)
  2123. $cv["title"] = truncate_string($line["title"], 30);
  2124. array_push($ret_arr, $cv);
  2125. }
  2126. return $ret_arr;
  2127. }
  2128. function get_pgsql_version($link) {
  2129. $result = db_query($link, "SELECT version() AS version");
  2130. $version = split(" ", db_fetch_result($result, 0, "version"));
  2131. return $version[1];
  2132. }
  2133. /**
  2134. * Subscribes the user to the given feed
  2135. *
  2136. * @param resource $link Database connection
  2137. * @param string $url Feed URL to subscribe to
  2138. * @param integer $cat_id Category ID the feed shall be added to
  2139. * @param string $auth_login (optional) Feed username
  2140. * @param string $auth_pass (optional) Feed password
  2141. *
  2142. * @return integer Status code:
  2143. * 0 - OK, Feed already exists
  2144. * 1 - OK, Feed added
  2145. * 2 - Invalid URL
  2146. * 3 - URL content is HTML, no feeds available
  2147. * 4 - URL content is HTML which contains multiple feeds.
  2148. * Here you should call extractfeedurls in rpc-backend
  2149. * to get all possible feeds.
  2150. * 5 - Couldn't download the URL content.
  2151. */
  2152. function subscribe_to_feed($link, $url, $cat_id = 0,
  2153. $auth_login = '', $auth_pass = '') {
  2154. $url = fix_url($url);
  2155. if (!$url || !validate_feed_url($url)) return 2;
  2156. $update_method = 0;
  2157. $result = db_query($link, "SELECT twitter_oauth FROM ttrss_users
  2158. WHERE id = ".$_SESSION['uid']);
  2159. $has_oauth = db_fetch_result($result, 0, 'twitter_oauth');
  2160. if (!$has_oauth || strpos($url, '://api.twitter.com') === false) {
  2161. if (!fetch_file_contents($url, false, $auth_login, $auth_pass)) return 5;
  2162. if (url_is_html($url, $auth_login, $auth_pass)) {
  2163. $feedUrls = get_feeds_from_html($url, $auth_login, $auth_pass);
  2164. if (count($feedUrls) == 0) {
  2165. return 3;
  2166. } else if (count($feedUrls) > 1) {
  2167. return 4;
  2168. }
  2169. //use feed url as new URL
  2170. $url = key($feedUrls);
  2171. }
  2172. } else {
  2173. if (!fetch_twitter_rss($link, $url, $_SESSION['uid']))
  2174. return 5;
  2175. $update_method = 3;
  2176. }
  2177. if ($cat_id == "0" || !$cat_id) {
  2178. $cat_qpart = "NULL";
  2179. } else {
  2180. $cat_qpart = "'$cat_id'";
  2181. }
  2182. $result = db_query($link,
  2183. "SELECT id FROM ttrss_feeds
  2184. WHERE feed_url = '$url' AND owner_uid = ".$_SESSION["uid"]);
  2185. if (db_num_rows($result) == 0) {
  2186. $result = db_query($link,
  2187. "INSERT INTO ttrss_feeds
  2188. (owner_uid,feed_url,title,cat_id, auth_login,auth_pass,update_method)
  2189. VALUES ('".$_SESSION["uid"]."', '$url',
  2190. '[Unknown]', $cat_qpart, '$auth_login', '$auth_pass', '$update_method')");
  2191. $result = db_query($link,
  2192. "SELECT id FROM ttrss_feeds WHERE feed_url = '$url'
  2193. AND owner_uid = " . $_SESSION["uid"]);
  2194. $feed_id = db_fetch_result($result, 0, "id");
  2195. if ($feed_id) {
  2196. update_rss_feed($link, $feed_id, true);
  2197. }
  2198. return 1;
  2199. } else {
  2200. return 0;
  2201. }
  2202. }
  2203. function print_feed_select($link, $id, $default_id = "",
  2204. $attributes = "", $include_all_feeds = true) {
  2205. print "<select id=\"$id\" name=\"$id\" $attributes>";
  2206. if ($include_all_feeds) {
  2207. print "<option value=\"0\">".__('All feeds')."</option>";
  2208. }
  2209. $result = db_query($link, "SELECT id,title FROM ttrss_feeds
  2210. WHERE owner_uid = ".$_SESSION["uid"]." ORDER BY title");
  2211. if (db_num_rows($result) > 0 && $include_all_feeds) {
  2212. print "<option disabled>--------</option>";
  2213. }
  2214. while ($line = db_fetch_assoc($result)) {
  2215. if ($line["id"] == $default_id) {
  2216. $is_selected = "selected=\"1\"";
  2217. } else {
  2218. $is_selected = "";
  2219. }
  2220. $title = truncate_string(htmlspecialchars($line["title"]), 40);
  2221. printf("<option $is_selected value='%d'>%s</option>",
  2222. $line["id"], $title);
  2223. }
  2224. print "</select>";
  2225. }
  2226. function print_feed_cat_select($link, $id, $default_id = "",
  2227. $attributes = "", $include_all_cats = true) {
  2228. print "<select id=\"$id\" name=\"$id\" default=\"$default_id\" onchange=\"catSelectOnChange(this)\" $attributes>";
  2229. if ($include_all_cats) {
  2230. print "<option value=\"0\">".__('Uncategorized')."</option>";
  2231. }
  2232. $result = db_query($link, "SELECT id,title FROM ttrss_feed_categories
  2233. WHERE owner_uid = ".$_SESSION["uid"]." ORDER BY title");
  2234. if (db_num_rows($result) > 0 && $include_all_cats) {
  2235. print "<option disabled=\"1\">--------</option>";
  2236. }
  2237. while ($line = db_fetch_assoc($result)) {
  2238. if ($line["id"] == $default_id) {
  2239. $is_selected = "selected=\"1\"";
  2240. } else {
  2241. $is_selected = "";
  2242. }
  2243. if ($line["title"])
  2244. printf("<option $is_selected value='%d'>%s</option>",
  2245. $line["id"], htmlspecialchars($line["title"]));
  2246. }
  2247. # print "<option value=\"ADD_CAT\">" .__("Add category...") . "</option>";
  2248. print "</select>";
  2249. }
  2250. function checkbox_to_sql_bool($val) {
  2251. return ($val == "on") ? "true" : "false";
  2252. }
  2253. function getFeedCatTitle($link, $id) {
  2254. if ($id == -1) {
  2255. return __("Special");
  2256. } else if ($id < -10) {
  2257. return __("Labels");
  2258. } else if ($id > 0) {
  2259. $result = db_query($link, "SELECT ttrss_feed_categories.title
  2260. FROM ttrss_feeds, ttrss_feed_categories WHERE ttrss_feeds.id = '$id' AND
  2261. cat_id = ttrss_feed_categories.id");
  2262. if (db_num_rows($result) == 1) {
  2263. return db_fetch_result($result, 0, "title");
  2264. } else {
  2265. return __("Uncategorized");
  2266. }
  2267. } else {
  2268. return "getFeedCatTitle($id) failed";
  2269. }
  2270. }
  2271. function getFeedIcon($id) {
  2272. switch ($id) {
  2273. case 0:
  2274. return "images/archive.png";
  2275. break;
  2276. case -1:
  2277. return "images/mark_set.png";
  2278. break;
  2279. case -2:
  2280. return "images/pub_set.png";
  2281. break;
  2282. case -3:
  2283. return "images/fresh.png";
  2284. break;
  2285. case -4:
  2286. return "images/tag.png";
  2287. break;
  2288. default:
  2289. if ($id < -10) {
  2290. return "images/label.png";
  2291. } else {
  2292. if (file_exists(ICONS_DIR . "/$id.ico"))
  2293. return ICONS_URL . "/$id.ico";
  2294. }
  2295. break;
  2296. }
  2297. }
  2298. function getFeedTitle($link, $id) {
  2299. if ($id == -1) {
  2300. return __("Starred articles");
  2301. } else if ($id == -2) {
  2302. return __("Published articles");
  2303. } else if ($id == -3) {
  2304. return __("Fresh articles");
  2305. } else if ($id == -4) {
  2306. return __("All articles");
  2307. } else if ($id === 0 || $id === "0") {
  2308. return __("Archived articles");
  2309. } else if ($id < -10) {
  2310. $label_id = -$id - 11;
  2311. $result = db_query($link, "SELECT caption FROM ttrss_labels2 WHERE id = '$label_id'");
  2312. if (db_num_rows($result) == 1) {
  2313. return db_fetch_result($result, 0, "caption");
  2314. } else {
  2315. return "Unknown label ($label_id)";
  2316. }
  2317. } else if ($id > 0) {
  2318. $result = db_query($link, "SELECT title FROM ttrss_feeds WHERE id = '$id'");
  2319. if (db_num_rows($result) == 1) {
  2320. return db_fetch_result($result, 0, "title");
  2321. } else {
  2322. return "Unknown feed ($id)";
  2323. }
  2324. } else {
  2325. return $id;
  2326. }
  2327. }
  2328. function get_session_cookie_name() {
  2329. return ((!defined('TTRSS_SESSION_NAME')) ? "ttrss_sid" : TTRSS_SESSION_NAME);
  2330. }
  2331. function make_init_params($link) {
  2332. $params = array();
  2333. $params["theme"] = get_user_theme($link);
  2334. $params["theme_options"] = get_user_theme_options($link);
  2335. $params["daemon_enabled"] = ENABLE_UPDATE_DAEMON;
  2336. $params["sign_progress"] = theme_image($link, "images/indicator_white.gif");
  2337. $params["sign_progress_tiny"] = theme_image($link, "images/indicator_tiny.gif");
  2338. $params["sign_excl"] = theme_image($link, "images/sign_excl.png");
  2339. $params["sign_info"] = theme_image($link, "images/sign_info.png");
  2340. foreach (array("ON_CATCHUP_SHOW_NEXT_FEED", "HIDE_READ_FEEDS",
  2341. "ENABLE_FEED_CATS", "FEEDS_SORT_BY_UNREAD", "CONFIRM_FEED_CATCHUP",
  2342. "CDM_AUTO_CATCHUP", "FRESH_ARTICLE_MAX_AGE", "DEFAULT_ARTICLE_LIMIT",
  2343. "HIDE_READ_SHOWS_SPECIAL", "COMBINED_DISPLAY_MODE") as $param) {
  2344. $params[strtolower($param)] = (int) get_pref($link, $param);
  2345. }
  2346. $params["icons_url"] = ICONS_URL;
  2347. $params["cookie_lifetime"] = SESSION_COOKIE_LIFETIME;
  2348. $params["default_view_mode"] = get_pref($link, "_DEFAULT_VIEW_MODE");
  2349. $params["default_view_limit"] = (int) get_pref($link, "_DEFAULT_VIEW_LIMIT");
  2350. $params["default_view_order_by"] = get_pref($link, "_DEFAULT_VIEW_ORDER_BY");
  2351. $params["bw_limit"] = (int) $_SESSION["bw_limit"];
  2352. $result = db_query($link, "SELECT MAX(id) AS mid, COUNT(*) AS nf FROM
  2353. ttrss_feeds WHERE owner_uid = " . $_SESSION["uid"]);
  2354. $max_feed_id = db_fetch_result($result, 0, "mid");
  2355. $num_feeds = db_fetch_result($result, 0, "nf");
  2356. $params["max_feed_id"] = (int) $max_feed_id;
  2357. $params["num_feeds"] = (int) $num_feeds;
  2358. $params["collapsed_feedlist"] = (int) get_pref($link, "_COLLAPSED_FEEDLIST");
  2359. return $params;
  2360. }
  2361. function print_runtime_info($link) {
  2362. print "<runtime-info><![CDATA[";
  2363. print json_encode(make_runtime_info($link));
  2364. print "]]></runtime-info>";
  2365. }
  2366. function make_runtime_info($link) {
  2367. $data = array();
  2368. $result = db_query($link, "SELECT MAX(id) AS mid, COUNT(*) AS nf FROM
  2369. ttrss_feeds WHERE owner_uid = " . $_SESSION["uid"]);
  2370. $max_feed_id = db_fetch_result($result, 0, "mid");
  2371. $num_feeds = db_fetch_result($result, 0, "nf");
  2372. $data["max_feed_id"] = (int) $max_feed_id;
  2373. $data["num_feeds"] = (int) $num_feeds;
  2374. $data['last_article_id'] = getLastArticleId($link);
  2375. $data['cdm_expanded'] = get_pref($link, 'CDM_EXPANDED');
  2376. if (ENABLE_UPDATE_DAEMON) {
  2377. $data['daemon_is_running'] = (int) file_is_locked("update_daemon.lock");
  2378. if (time() - $_SESSION["daemon_stamp_check"] > 30) {
  2379. $stamp = (int) @file_get_contents(LOCK_DIRECTORY . "/update_daemon.stamp");
  2380. if ($stamp) {
  2381. $stamp_delta = time() - $stamp;
  2382. if ($stamp_delta > 1800) {
  2383. $stamp_check = 0;
  2384. } else {
  2385. $stamp_check = 1;
  2386. $_SESSION["daemon_stamp_check"] = time();
  2387. }
  2388. $data['daemon_stamp_ok'] = $stamp_check;
  2389. $stamp_fmt = date("Y.m.d, G:i", $stamp);
  2390. $data['daemon_stamp'] = $stamp_fmt;
  2391. }
  2392. }
  2393. }
  2394. if ($_SESSION["last_version_check"] + 86400 + rand(-1000, 1000) < time()) {
  2395. $new_version_details = @check_for_update($link);
  2396. $data['new_version_available'] = (int) ($new_version_details != false);
  2397. $_SESSION["last_version_check"] = time();
  2398. }
  2399. return $data;
  2400. }
  2401. function getSearchSql($link, $search, $match_on) {
  2402. $search_query_part = "";
  2403. $keywords = split(" ", $search);
  2404. $query_keywords = array();
  2405. foreach ($keywords as $k) {
  2406. if (strpos($k, "-") === 0) {
  2407. $k = substr($k, 1);
  2408. $not = "NOT";
  2409. } else {
  2410. $not = "";
  2411. }
  2412. if (strpos($k, "@") === 0) {
  2413. $user_tz_string = get_pref($link, 'USER_TIMEZONE', $_SESSION['uid']);
  2414. $orig_ts = strtotime(substr($k, 1));
  2415. $k = date("Y-m-d", convert_timestamp($orig_ts, $user_tz_string, 'UTC'));
  2416. array_push($query_keywords, "(".SUBSTRING_FOR_DATE."(updated,1,LENGTH('$k')) $not = '$k')");
  2417. } else if ($match_on == "both") {
  2418. array_push($query_keywords, "(UPPER(ttrss_entries.title) $not LIKE UPPER('%$k%')
  2419. OR UPPER(ttrss_entries.content) $not LIKE UPPER('%$k%'))");
  2420. } else if ($match_on == "title") {
  2421. array_push($query_keywords, "(UPPER(ttrss_entries.title) $not LIKE UPPER('%$k%'))");
  2422. } else if ($match_on == "content") {
  2423. array_push($query_keywords, "(UPPER(ttrss_entries.content) $not LIKE UPPER('%$k%'))");
  2424. }
  2425. }
  2426. $search_query_part = implode("AND", $query_keywords);
  2427. return $search_query_part;
  2428. }
  2429. function queryFeedHeadlines($link, $feed, $limit, $view_mode, $cat_view, $search, $search_mode, $match_on, $override_order = false, $offset = 0, $owner_uid = 0) {
  2430. if (!$owner_uid) $owner_uid = $_SESSION["uid"];
  2431. $ext_tables_part = "";
  2432. if ($search) {
  2433. if (SPHINX_ENABLED) {
  2434. $ids = join(",", @sphinx_search($search, 0, 500));
  2435. if ($ids)
  2436. $search_query_part = "ref_id IN ($ids) AND ";
  2437. else
  2438. $search_query_part = "ref_id = -1 AND ";
  2439. } else {
  2440. $search_query_part = getSearchSql($link, $search, $match_on);
  2441. $search_query_part .= " AND ";
  2442. }
  2443. } else {
  2444. $search_query_part = "";
  2445. }
  2446. $view_query_part = "";
  2447. if ($view_mode == "adaptive" || $view_query_part == "noscores") {
  2448. if ($search) {
  2449. $view_query_part = " ";
  2450. } else if ($feed != -1) {
  2451. $unread = getFeedUnread($link, $feed, $cat_view);
  2452. if ($unread > 0) {
  2453. $view_query_part = " unread = true AND ";
  2454. }
  2455. }
  2456. }
  2457. if ($view_mode == "marked") {
  2458. $view_query_part = " marked = true AND ";
  2459. }
  2460. if ($view_mode == "published") {
  2461. $view_query_part = " published = true AND ";
  2462. }
  2463. if ($view_mode == "unread") {
  2464. $view_query_part = " unread = true AND ";
  2465. }
  2466. if ($view_mode == "updated") {
  2467. $view_query_part = " (last_read is null and unread = false) AND ";
  2468. }
  2469. if ($limit > 0) {
  2470. $limit_query_part = "LIMIT " . $limit;
  2471. }
  2472. $vfeed_query_part = "";
  2473. // override query strategy and enable feed display when searching globally
  2474. if ($search && $search_mode == "all_feeds") {
  2475. $query_strategy_part = "ttrss_entries.id > 0";
  2476. $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
  2477. /* tags */
  2478. } else if (preg_match("/^-?[0-9][0-9]*$/", $feed) == false) {
  2479. $query_strategy_part = "ttrss_entries.id > 0";
  2480. $vfeed_query_part = "(SELECT title FROM ttrss_feeds WHERE
  2481. id = feed_id) as feed_title,";
  2482. } else if ($feed > 0 && $search && $search_mode == "this_cat") {
  2483. $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
  2484. $tmp_result = false;
  2485. if ($cat_view) {
  2486. $tmp_result = db_query($link, "SELECT id
  2487. FROM ttrss_feeds WHERE cat_id = '$feed'");
  2488. } else {
  2489. $tmp_result = db_query($link, "SELECT id
  2490. FROM ttrss_feeds WHERE cat_id = (SELECT cat_id FROM ttrss_feeds
  2491. WHERE id = '$feed') AND id != '$feed'");
  2492. }
  2493. $cat_siblings = array();
  2494. if (db_num_rows($tmp_result) > 0) {
  2495. while ($p = db_fetch_assoc($tmp_result)) {
  2496. array_push($cat_siblings, "feed_id = " . $p["id"]);
  2497. }
  2498. $query_strategy_part = sprintf("(feed_id = %d OR %s)",
  2499. $feed, implode(" OR ", $cat_siblings));
  2500. } else {
  2501. $query_strategy_part = "ttrss_entries.id > 0";
  2502. }
  2503. } else if ($feed > 0) {
  2504. if ($cat_view) {
  2505. if ($feed > 0) {
  2506. $query_strategy_part = "cat_id = '$feed'";
  2507. } else {
  2508. $query_strategy_part = "cat_id IS NULL";
  2509. }
  2510. $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
  2511. } else {
  2512. $query_strategy_part = "feed_id = '$feed'";
  2513. }
  2514. } else if ($feed == 0 && !$cat_view) { // archive virtual feed
  2515. $query_strategy_part = "feed_id IS NULL";
  2516. } else if ($feed == 0 && $cat_view) { // uncategorized
  2517. $query_strategy_part = "cat_id IS NULL";
  2518. $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
  2519. } else if ($feed == -1) { // starred virtual feed
  2520. $query_strategy_part = "marked = true";
  2521. $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
  2522. } else if ($feed == -2) { // published virtual feed OR labels category
  2523. if (!$cat_view) {
  2524. $query_strategy_part = "published = true";
  2525. $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
  2526. } else {
  2527. $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
  2528. $ext_tables_part = ",ttrss_labels2,ttrss_user_labels2";
  2529. $query_strategy_part = "ttrss_labels2.id = ttrss_user_labels2.label_id AND
  2530. ttrss_user_labels2.article_id = ref_id";
  2531. }
  2532. } else if ($feed == -3) { // fresh virtual feed
  2533. $query_strategy_part = "unread = true AND score >= 0";
  2534. $intl = get_pref($link, "FRESH_ARTICLE_MAX_AGE", $owner_uid);
  2535. if (DB_TYPE == "pgsql") {
  2536. $query_strategy_part .= " AND updated > NOW() - INTERVAL '$intl hour' ";
  2537. } else {
  2538. $query_strategy_part .= " AND updated > DATE_SUB(NOW(), INTERVAL $intl HOUR) ";
  2539. }
  2540. $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
  2541. } else if ($feed == -4) { // all articles virtual feed
  2542. $query_strategy_part = "true";
  2543. $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
  2544. } else if ($feed <= -10) { // labels
  2545. $label_id = -$feed - 11;
  2546. $query_strategy_part = "label_id = '$label_id' AND
  2547. ttrss_labels2.id = ttrss_user_labels2.label_id AND
  2548. ttrss_user_labels2.article_id = ref_id";
  2549. $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
  2550. $ext_tables_part = ",ttrss_labels2,ttrss_user_labels2";
  2551. } else {
  2552. $query_strategy_part = "id > 0"; // dumb
  2553. }
  2554. if (get_pref($link, "SORT_HEADLINES_BY_FEED_DATE", $owner_uid)) {
  2555. $date_sort_field = "updated";
  2556. } else {
  2557. $date_sort_field = "date_entered";
  2558. }
  2559. if (get_pref($link, 'REVERSE_HEADLINES', $owner_uid)) {
  2560. $order_by = "$date_sort_field";
  2561. } else {
  2562. $order_by = "$date_sort_field DESC";
  2563. }
  2564. if ($view_mode != "noscores") {
  2565. $order_by = "score DESC, $order_by";
  2566. }
  2567. if ($override_order) {
  2568. $order_by = $override_order;
  2569. }
  2570. $feed_title = "";
  2571. if ($search) {
  2572. $feed_title = "Search results";
  2573. } else {
  2574. if ($cat_view) {
  2575. $feed_title = getCategoryTitle($link, $feed);
  2576. } else {
  2577. if ((int)$feed == $feed && $feed > 0) {
  2578. $result = db_query($link, "SELECT title,site_url,last_error
  2579. FROM ttrss_feeds WHERE id = '$feed' AND owner_uid = $owner_uid");
  2580. $feed_title = db_fetch_result($result, 0, "title");
  2581. $feed_site_url = db_fetch_result($result, 0, "site_url");
  2582. $last_error = db_fetch_result($result, 0, "last_error");
  2583. } else {
  2584. $feed_title = getFeedTitle($link, $feed);
  2585. }
  2586. }
  2587. }
  2588. $content_query_part = "content as content_preview,";
  2589. if (preg_match("/^-?[0-9][0-9]*$/", $feed) != false) {
  2590. if ($feed >= 0) {
  2591. $feed_kind = "Feeds";
  2592. } else {
  2593. $feed_kind = "Labels";
  2594. }
  2595. if ($limit_query_part) {
  2596. $offset_query_part = "OFFSET $offset";
  2597. }
  2598. if ($vfeed_query_part && get_pref($link, 'VFEED_GROUP_BY_FEED', $owner_uid)) {
  2599. if (!$override_order) {
  2600. $order_by = "ttrss_feeds.title, $order_by";
  2601. }
  2602. }
  2603. if ($feed != "0") {
  2604. $from_qpart = "ttrss_entries,ttrss_user_entries,ttrss_feeds$ext_tables_part";
  2605. $feed_check_qpart = "ttrss_user_entries.feed_id = ttrss_feeds.id AND";
  2606. } else {
  2607. $from_qpart = "ttrss_entries,ttrss_user_entries$ext_tables_part
  2608. LEFT JOIN ttrss_feeds ON (feed_id = ttrss_feeds.id)";
  2609. }
  2610. $query = "SELECT DISTINCT
  2611. date_entered,
  2612. guid,
  2613. ttrss_entries.id,ttrss_entries.title,
  2614. updated,
  2615. note,
  2616. unread,feed_id,marked,published,link,last_read,orig_feed_id,
  2617. ".SUBSTRING_FOR_DATE."(last_read,1,19) as last_read_noms,
  2618. $vfeed_query_part
  2619. $content_query_part
  2620. ".SUBSTRING_FOR_DATE."(updated,1,19) as updated_noms,
  2621. author,score
  2622. FROM
  2623. $from_qpart
  2624. WHERE
  2625. $feed_check_qpart
  2626. ttrss_user_entries.ref_id = ttrss_entries.id AND
  2627. ttrss_user_entries.owner_uid = '$owner_uid' AND
  2628. $search_query_part
  2629. $view_query_part
  2630. $query_strategy_part ORDER BY $order_by
  2631. $limit_query_part $offset_query_part";
  2632. if ($_REQUEST["debug"]) print $query;
  2633. $result = db_query($link, $query);
  2634. } else {
  2635. // browsing by tag
  2636. $feed_kind = "Tags";
  2637. $result = db_query($link, "SELECT DISTINCT
  2638. date_entered,
  2639. guid,
  2640. note,
  2641. ttrss_entries.id as id,title,
  2642. updated,
  2643. unread,feed_id,orig_feed_id,
  2644. marked,link,last_read,
  2645. ".SUBSTRING_FOR_DATE."(last_read,1,19) as last_read_noms,
  2646. $vfeed_query_part
  2647. $content_query_part
  2648. ".SUBSTRING_FOR_DATE."(updated,1,19) as updated_noms,
  2649. score
  2650. FROM
  2651. ttrss_entries,ttrss_user_entries,ttrss_tags
  2652. WHERE
  2653. ref_id = ttrss_entries.id AND
  2654. ttrss_user_entries.owner_uid = '$owner_uid' AND
  2655. post_int_id = int_id AND tag_name = '$feed' AND
  2656. $view_query_part
  2657. $search_query_part
  2658. $query_strategy_part ORDER BY $order_by
  2659. $limit_query_part");
  2660. }
  2661. return array($result, $feed_title, $feed_site_url, $last_error);
  2662. }
  2663. function generate_syndicated_feed($link, $owner_uid, $feed, $is_cat,
  2664. $limit, $search, $search_mode, $match_on, $view_mode = false) {
  2665. $note_style = "float : right; background-color : #fff7d5; border-width : 1px; ".
  2666. "padding : 5px; border-style : dashed; border-color : #e7d796;".
  2667. "margin-bottom : 1em; color : #9a8c59;";
  2668. if (!$limit) $limit = 30;
  2669. if (get_pref($link, "SORT_HEADLINES_BY_FEED_DATE", $owner_uid)) {
  2670. $date_sort_field = "updated";
  2671. } else {
  2672. $date_sort_field = "date_entered";
  2673. }
  2674. $qfh_ret = queryFeedHeadlines($link, $feed,
  2675. $limit, $view_mode, $is_cat, $search, $search_mode,
  2676. $match_on, "$date_sort_field DESC", 0, $owner_uid);
  2677. $result = $qfh_ret[0];
  2678. $feed_title = htmlspecialchars($qfh_ret[1]);
  2679. $feed_site_url = $qfh_ret[2];
  2680. $last_error = $qfh_ret[3];
  2681. // if (!$feed_site_url) $feed_site_url = "http://localhost/";
  2682. print "<?xml version=\"1.0\" encoding=\"utf-8\"?>
  2683. <?xml-stylesheet type=\"text/xsl\" href=\"rss.xsl\"?>
  2684. <rss version=\"2.0\">
  2685. <channel>
  2686. <title>$feed_title</title>
  2687. <link>$feed_site_url</link>
  2688. <description>Feed generated by Tiny Tiny RSS</description>";
  2689. while ($line = db_fetch_assoc($result)) {
  2690. print "<item>";
  2691. print "<guid>" . htmlspecialchars($line["guid"]) . "</guid>";
  2692. print "<link>" . htmlspecialchars($line["link"]) . "</link>";
  2693. $tags = get_article_tags($link, $line["id"], $owner_uid);
  2694. foreach ($tags as $tag) {
  2695. print "<category>" . htmlspecialchars($tag) . "</category>";
  2696. }
  2697. $rfc822_date = date('r', strtotime($line["updated"]));
  2698. print "<pubDate>$rfc822_date</pubDate>";
  2699. if ($line["author"]) {
  2700. print "<author>" . htmlspecialchars($line["author"]) . "</author>";
  2701. }
  2702. print "<title><![CDATA[" .
  2703. htmlspecialchars($line["title"]) . "]]></title>";
  2704. print "<description><![CDATA[";
  2705. if ($line["note"]) {
  2706. print "<div style='$note_style'>";
  2707. print $line["note"];
  2708. print "</div>";
  2709. }
  2710. print sanitize_rss($link, $line["content_preview"], false, $owner_uid);
  2711. print "]]></description>";
  2712. $enclosures = get_article_enclosures($link, $line["id"]);
  2713. foreach ($enclosures as $e) {
  2714. $type = htmlspecialchars($e['content_type']);
  2715. $url = htmlspecialchars($e['content_url']);
  2716. $length = $e['duration'];
  2717. print "<enclosure url=\"$url\" type=\"$type\" length=\"$length\"/>";
  2718. }
  2719. print "</item>";
  2720. }
  2721. print "</channel></rss>";
  2722. }
  2723. function getCategoryTitle($link, $cat_id) {
  2724. if ($cat_id == -1) {
  2725. return __("Special");
  2726. } else if ($cat_id == -2) {
  2727. return __("Labels");
  2728. } else {
  2729. $result = db_query($link, "SELECT title FROM ttrss_feed_categories WHERE
  2730. id = '$cat_id'");
  2731. if (db_num_rows($result) == 1) {
  2732. return db_fetch_result($result, 0, "title");
  2733. } else {
  2734. return "Uncategorized";
  2735. }
  2736. }
  2737. }
  2738. function sanitize_rss($link, $str, $force_strip_tags = false, $owner = false, $site_url = false) {
  2739. global $purifier;
  2740. if (!$owner) $owner = $_SESSION["uid"];
  2741. $res = trim($str); if (!$res) return '';
  2742. if (get_pref($link, "STRIP_UNSAFE_TAGS", $owner) || $force_strip_tags) {
  2743. $res = $purifier->purify($res);
  2744. }
  2745. if (get_pref($link, "STRIP_IMAGES", $owner)) {
  2746. $res = preg_replace('/<img[^>]+>/is', '', $res);
  2747. }
  2748. if (strpos($res, "href=") === false)
  2749. $res = rewrite_urls($res);
  2750. $charset_hack = '<head>
  2751. <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
  2752. </head>';
  2753. $res = trim($res); if (!$res) return '';
  2754. libxml_use_internal_errors(true);
  2755. $doc = new DOMDocument();
  2756. $doc->loadHTML($charset_hack . $res);
  2757. $xpath = new DOMXPath($doc);
  2758. $entries = $xpath->query('(//a[@href]|//img[@src])');
  2759. $br_inserted = 0;
  2760. foreach ($entries as $entry) {
  2761. if ($site_url) {
  2762. if ($entry->hasAttribute('href'))
  2763. $entry->setAttribute('href',
  2764. rewrite_relative_url($site_url, $entry->getAttribute('href')));
  2765. if ($entry->hasAttribute('src'))
  2766. if (preg_match('/^image.php\?i=[a-z0-9]+$/', $entry->getAttribute('src')) == 0)
  2767. $entry->setAttribute('src',
  2768. rewrite_relative_url($site_url, $entry->getAttribute('src')));
  2769. }
  2770. if (strtolower($entry->nodeName) == "a") {
  2771. $entry->setAttribute("target", "_blank");
  2772. }
  2773. if (strtolower($entry->nodeName) == "img" && !$br_inserted) {
  2774. $br = $doc->createElement("br");
  2775. if ($entry->parentNode->nextSibling) {
  2776. $entry->parentNode->insertBefore($br, $entry->nextSibling);
  2777. $br_inserted = 1;
  2778. }
  2779. }
  2780. }
  2781. $node = $doc->getElementsByTagName('body')->item(0);
  2782. return $doc->saveXML($node);
  2783. }
  2784. /**
  2785. * Send by mail a digest of last articles.
  2786. *
  2787. * @param mixed $link The database connection.
  2788. * @param integer $limit The maximum number of articles by digest.
  2789. * @return boolean Return false if digests are not enabled.
  2790. */
  2791. function send_headlines_digests($link, $limit = 100) {
  2792. if (!DIGEST_ENABLE) return false;
  2793. $user_limit = DIGEST_EMAIL_LIMIT;
  2794. $days = 1;
  2795. print "Sending digests, batch of max $user_limit users, days = $days, headline limit = $limit\n\n";
  2796. if (DB_TYPE == "pgsql") {
  2797. $interval_query = "last_digest_sent < NOW() - INTERVAL '$days days'";
  2798. } else if (DB_TYPE == "mysql") {
  2799. $interval_query = "last_digest_sent < DATE_SUB(NOW(), INTERVAL $days DAY)";
  2800. }
  2801. $result = db_query($link, "SELECT id,email FROM ttrss_users
  2802. WHERE email != '' AND (last_digest_sent IS NULL OR $interval_query)");
  2803. while ($line = db_fetch_assoc($result)) {
  2804. if (get_pref($link, 'DIGEST_ENABLE', $line['id'], false)) {
  2805. print "Sending digest for UID:" . $line['id'] . " - " . $line["email"] . " ... ";
  2806. $do_catchup = get_pref($link, 'DIGEST_CATCHUP', $line['id'], false);
  2807. $tuple = prepare_headlines_digest($link, $line["id"], $days, $limit);
  2808. $digest = $tuple[0];
  2809. $headlines_count = $tuple[1];
  2810. $affected_ids = $tuple[2];
  2811. $digest_text = $tuple[3];
  2812. if ($headlines_count > 0) {
  2813. $mail = new PHPMailer();
  2814. $mail->PluginDir = "lib/phpmailer/";
  2815. $mail->SetLanguage("en", "lib/phpmailer/language/");
  2816. $mail->CharSet = "UTF-8";
  2817. $mail->From = DIGEST_FROM_ADDRESS;
  2818. $mail->FromName = DIGEST_FROM_NAME;
  2819. $mail->AddAddress($line["email"], $line["login"]);
  2820. if (DIGEST_SMTP_HOST) {
  2821. $mail->Host = DIGEST_SMTP_HOST;
  2822. $mail->Mailer = "smtp";
  2823. $mail->SMTPAuth = DIGEST_SMTP_LOGIN != '';
  2824. $mail->Username = DIGEST_SMTP_LOGIN;
  2825. $mail->Password = DIGEST_SMTP_PASSWORD;
  2826. }
  2827. $mail->IsHTML(true);
  2828. $mail->Subject = DIGEST_SUBJECT;
  2829. $mail->Body = $digest;
  2830. $mail->AltBody = $digest_text;
  2831. $rc = $mail->Send();
  2832. if (!$rc) print "ERROR: " . $mail->ErrorInfo;
  2833. print "RC=$rc\n";
  2834. if ($rc && $do_catchup) {
  2835. print "Marking affected articles as read...\n";
  2836. catchupArticlesById($link, $affected_ids, 0, $line["id"]);
  2837. }
  2838. } else {
  2839. print "No headlines\n";
  2840. }
  2841. db_query($link, "UPDATE ttrss_users SET last_digest_sent = NOW()
  2842. WHERE id = " . $line["id"]);
  2843. }
  2844. }
  2845. print "All done.\n";
  2846. }
  2847. function prepare_headlines_digest($link, $user_id, $days = 1, $limit = 100) {
  2848. require_once "lib/MiniTemplator.class.php";
  2849. $tpl = new MiniTemplator;
  2850. $tpl_t = new MiniTemplator;
  2851. $tpl->readTemplateFromFile("templates/digest_template_html.txt");
  2852. $tpl_t->readTemplateFromFile("templates/digest_template.txt");
  2853. $tpl->setVariable('CUR_DATE', date('Y/m/d'));
  2854. $tpl->setVariable('CUR_TIME', date('G:i'));
  2855. $tpl_t->setVariable('CUR_DATE', date('Y/m/d'));
  2856. $tpl_t->setVariable('CUR_TIME', date('G:i'));
  2857. $affected_ids = array();
  2858. if (DB_TYPE == "pgsql") {
  2859. $interval_query = "ttrss_entries.date_updated > NOW() - INTERVAL '$days days'";
  2860. } else if (DB_TYPE == "mysql") {
  2861. $interval_query = "ttrss_entries.date_updated > DATE_SUB(NOW(), INTERVAL $days DAY)";
  2862. }
  2863. $result = db_query($link, "SELECT ttrss_entries.title,
  2864. ttrss_feeds.title AS feed_title,
  2865. date_updated,
  2866. ttrss_user_entries.ref_id,
  2867. link,
  2868. SUBSTRING(content, 1, 120) AS excerpt,
  2869. ".SUBSTRING_FOR_DATE."(last_updated,1,19) AS last_updated
  2870. FROM
  2871. ttrss_user_entries,ttrss_entries,ttrss_feeds
  2872. WHERE
  2873. ref_id = ttrss_entries.id AND feed_id = ttrss_feeds.id
  2874. AND include_in_digest = true
  2875. AND $interval_query
  2876. AND ttrss_user_entries.owner_uid = $user_id
  2877. AND unread = true
  2878. ORDER BY ttrss_feeds.title, date_updated DESC
  2879. LIMIT $limit");
  2880. $cur_feed_title = "";
  2881. $headlines_count = db_num_rows($result);
  2882. $headlines = array();
  2883. while ($line = db_fetch_assoc($result)) {
  2884. array_push($headlines, $line);
  2885. }
  2886. for ($i = 0; $i < sizeof($headlines); $i++) {
  2887. $line = $headlines[$i];
  2888. array_push($affected_ids, $line["ref_id"]);
  2889. $updated = make_local_datetime($link, $line['last_updated'], false,
  2890. $user_id);
  2891. $tpl->setVariable('FEED_TITLE', $line["feed_title"]);
  2892. $tpl->setVariable('ARTICLE_TITLE', $line["title"]);
  2893. $tpl->setVariable('ARTICLE_LINK', $line["link"]);
  2894. $tpl->setVariable('ARTICLE_UPDATED', $updated);
  2895. $tpl->setVariable('ARTICLE_EXCERPT',
  2896. truncate_string(strip_tags($line["excerpt"]), 100));
  2897. $tpl->addBlock('article');
  2898. $tpl_t->setVariable('FEED_TITLE', $line["feed_title"]);
  2899. $tpl_t->setVariable('ARTICLE_TITLE', $line["title"]);
  2900. $tpl_t->setVariable('ARTICLE_LINK', $line["link"]);
  2901. $tpl_t->setVariable('ARTICLE_UPDATED', $updated);
  2902. // $tpl_t->setVariable('ARTICLE_EXCERPT',
  2903. // truncate_string(strip_tags($line["excerpt"]), 100));
  2904. $tpl_t->addBlock('article');
  2905. if ($headlines[$i]['feed_title'] != $headlines[$i+1]['feed_title']) {
  2906. $tpl->addBlock('feed');
  2907. $tpl_t->addBlock('feed');
  2908. }
  2909. }
  2910. $tpl->addBlock('digest');
  2911. $tpl->generateOutputToString($tmp);
  2912. $tpl_t->addBlock('digest');
  2913. $tpl_t->generateOutputToString($tmp_t);
  2914. return array($tmp, $headlines_count, $affected_ids, $tmp_t);
  2915. }
  2916. function check_for_update($link) {
  2917. if (CHECK_FOR_NEW_VERSION && $_SESSION['access_level'] >= 10) {
  2918. $version_url = "http://tt-rss.org/version.php?ver=" . VERSION;
  2919. $version_data = @fetch_file_contents($version_url);
  2920. if ($version_data) {
  2921. $version_data = json_decode($version_data, true);
  2922. if ($version_data && $version_data['version']) {
  2923. if (version_compare(VERSION, $version_data['version']) == -1) {
  2924. return $version_data;
  2925. }
  2926. }
  2927. }
  2928. }
  2929. return false;
  2930. }
  2931. function markArticlesById($link, $ids, $cmode) {
  2932. $tmp_ids = array();
  2933. foreach ($ids as $id) {
  2934. array_push($tmp_ids, "ref_id = '$id'");
  2935. }
  2936. $ids_qpart = join(" OR ", $tmp_ids);
  2937. if ($cmode == 0) {
  2938. db_query($link, "UPDATE ttrss_user_entries SET
  2939. marked = false,last_read = NOW()
  2940. WHERE ($ids_qpart) AND owner_uid = " . $_SESSION["uid"]);
  2941. } else if ($cmode == 1) {
  2942. db_query($link, "UPDATE ttrss_user_entries SET
  2943. marked = true
  2944. WHERE ($ids_qpart) AND owner_uid = " . $_SESSION["uid"]);
  2945. } else {
  2946. db_query($link, "UPDATE ttrss_user_entries SET
  2947. marked = NOT marked,last_read = NOW()
  2948. WHERE ($ids_qpart) AND owner_uid = " . $_SESSION["uid"]);
  2949. }
  2950. }
  2951. function publishArticlesById($link, $ids, $cmode) {
  2952. $tmp_ids = array();
  2953. foreach ($ids as $id) {
  2954. array_push($tmp_ids, "ref_id = '$id'");
  2955. }
  2956. $ids_qpart = join(" OR ", $tmp_ids);
  2957. if ($cmode == 0) {
  2958. db_query($link, "UPDATE ttrss_user_entries SET
  2959. published = false,last_read = NOW()
  2960. WHERE ($ids_qpart) AND owner_uid = " . $_SESSION["uid"]);
  2961. } else if ($cmode == 1) {
  2962. db_query($link, "UPDATE ttrss_user_entries SET
  2963. published = true
  2964. WHERE ($ids_qpart) AND owner_uid = " . $_SESSION["uid"]);
  2965. } else {
  2966. db_query($link, "UPDATE ttrss_user_entries SET
  2967. published = NOT published,last_read = NOW()
  2968. WHERE ($ids_qpart) AND owner_uid = " . $_SESSION["uid"]);
  2969. }
  2970. }
  2971. function catchupArticlesById($link, $ids, $cmode, $owner_uid = false) {
  2972. if (!$owner_uid) $owner_uid = $_SESSION["uid"];
  2973. if (count($ids) == 0) return;
  2974. $tmp_ids = array();
  2975. foreach ($ids as $id) {
  2976. array_push($tmp_ids, "ref_id = '$id'");
  2977. }
  2978. $ids_qpart = join(" OR ", $tmp_ids);
  2979. if ($cmode == 0) {
  2980. db_query($link, "UPDATE ttrss_user_entries SET
  2981. unread = false,last_read = NOW()
  2982. WHERE ($ids_qpart) AND owner_uid = $owner_uid");
  2983. } else if ($cmode == 1) {
  2984. db_query($link, "UPDATE ttrss_user_entries SET
  2985. unread = true
  2986. WHERE ($ids_qpart) AND owner_uid = $owner_uid");
  2987. } else {
  2988. db_query($link, "UPDATE ttrss_user_entries SET
  2989. unread = NOT unread,last_read = NOW()
  2990. WHERE ($ids_qpart) AND owner_uid = $owner_uid");
  2991. }
  2992. /* update ccache */
  2993. $result = db_query($link, "SELECT DISTINCT feed_id FROM ttrss_user_entries
  2994. WHERE ($ids_qpart) AND owner_uid = $owner_uid");
  2995. while ($line = db_fetch_assoc($result)) {
  2996. ccache_update($link, $line["feed_id"], $owner_uid);
  2997. }
  2998. }
  2999. function catchupArticleById($link, $id, $cmode) {
  3000. if ($cmode == 0) {
  3001. db_query($link, "UPDATE ttrss_user_entries SET
  3002. unread = false,last_read = NOW()
  3003. WHERE ref_id = '$id' AND owner_uid = " . $_SESSION["uid"]);
  3004. } else if ($cmode == 1) {
  3005. db_query($link, "UPDATE ttrss_user_entries SET
  3006. unread = true
  3007. WHERE ref_id = '$id' AND owner_uid = " . $_SESSION["uid"]);
  3008. } else {
  3009. db_query($link, "UPDATE ttrss_user_entries SET
  3010. unread = NOT unread,last_read = NOW()
  3011. WHERE ref_id = '$id' AND owner_uid = " . $_SESSION["uid"]);
  3012. }
  3013. $feed_id = getArticleFeed($link, $id);
  3014. ccache_update($link, $feed_id, $_SESSION["uid"]);
  3015. }
  3016. function make_guid_from_title($title) {
  3017. return preg_replace("/[ \"\',.:;]/", "-",
  3018. mb_strtolower(strip_tags($title), 'utf-8'));
  3019. }
  3020. function format_headline_subtoolbar($link, $feed_site_url, $feed_title,
  3021. $feed_id, $is_cat, $search, $match_on,
  3022. $search_mode, $view_mode, $error) {
  3023. $page_prev_link = "viewFeedGoPage(-1)";
  3024. $page_next_link = "viewFeedGoPage(1)";
  3025. $page_first_link = "viewFeedGoPage(0)";
  3026. $catchup_page_link = "catchupPage()";
  3027. $catchup_feed_link = "catchupCurrentFeed()";
  3028. $catchup_sel_link = "catchupSelection()";
  3029. $archive_sel_link = "archiveSelection()";
  3030. $delete_sel_link = "deleteSelection()";
  3031. $sel_all_link = "selectArticles('all')";
  3032. $sel_unread_link = "selectArticles('unread')";
  3033. $sel_none_link = "selectArticles('none')";
  3034. $sel_inv_link = "selectArticles('invert')";
  3035. $tog_unread_link = "selectionToggleUnread()";
  3036. $tog_marked_link = "selectionToggleMarked()";
  3037. $tog_published_link = "selectionTogglePublished()";
  3038. $reply = "<div id=\"subtoolbar_main\">";
  3039. $reply .= __('Select:')."
  3040. <a href=\"#\" onclick=\"$sel_all_link\">".__('All')."</a>,
  3041. <a href=\"#\" onclick=\"$sel_unread_link\">".__('Unread')."</a>,
  3042. <a href=\"#\" onclick=\"$sel_inv_link\">".__('Invert')."</a>,
  3043. <a href=\"#\" onclick=\"$sel_none_link\">".__('None')."</a></li>";
  3044. $reply .= " ";
  3045. $reply .= "<select dojoType=\"dijit.form.Select\"
  3046. onchange=\"headlineActionsChange(this)\">";
  3047. $reply .= "<option value=\"false\">".__('Actions...')."</option>";
  3048. $reply .= "<option value=\"0\" disabled=\"1\">".__('Selection toggle:')."</option>";
  3049. $reply .= "<option value=\"$tog_unread_link\">".__('Unread')."</option>
  3050. <option value=\"$tog_marked_link\">".__('Starred')."</option>
  3051. <option value=\"$tog_published_link\">".__('Published')."</option>";
  3052. $reply .= "<option value=\"0\" disabled=\"1\">".__('Selection:')."</option>";
  3053. $reply .= "<option value=\"$catchup_sel_link\">".__('Mark as read')."</option>";
  3054. if ($feed_id != "0") {
  3055. $reply .= "<option value=\"$archive_sel_link\">".__('Archive')."</option>";
  3056. } else {
  3057. $reply .= "<option value=\"$archive_sel_link\">".__('Move back')."</option>";
  3058. $reply .= "<option value=\"$delete_sel_link\">".__('Delete')."</option>";
  3059. }
  3060. $reply .= "<option value=\"emailArticle(false)\">".__('Forward by email').
  3061. "</option>";
  3062. $rss_link = htmlspecialchars(get_self_url_prefix() .
  3063. "/backend.php?op=rss&id=$feed_id&is_cat=$is_cat&view_mode=$view_mode$search_q");
  3064. $reply .= "<option value=\"0\" disabled=\"1\">".__('Feed:')."</option>";
  3065. $reply .= "<option value=\"catchupPage()\">".__('Mark as read')."</option>";
  3066. $reply .= "<option value=\"displayDlg('generatedFeed', '$feed_id:$is_cat:$rss_link')\">".__('View as RSS')."</option>";
  3067. $reply .= "</select>";
  3068. $reply .= "</div>";
  3069. $reply .= "<div id=\"subtoolbar_ftitle\">";
  3070. if ($feed_site_url) {
  3071. $target = "target=\"_blank\"";
  3072. $reply .= "<a title=\"".__("Visit the website")."\" $target href=\"$feed_site_url\">".
  3073. truncate_string($feed_title,30)."</a>";
  3074. if ($error) {
  3075. $reply .= " (<span class=\"error\" title=\"$error\">Error</span>)";
  3076. }
  3077. } else {
  3078. if ($feed_id < -10) {
  3079. $label_id = -11-$feed_id;
  3080. $result = db_query($link, "SELECT fg_color, bg_color
  3081. FROM ttrss_labels2 WHERE id = '$label_id' AND owner_uid = " .
  3082. $_SESSION["uid"]);
  3083. if (db_num_rows($result) != 0) {
  3084. $fg_color = db_fetch_result($result, 0, "fg_color");
  3085. $bg_color = db_fetch_result($result, 0, "bg_color");
  3086. $reply .= "<span style='background : $bg_color; color : $fg_color'>";
  3087. $reply .= $feed_title;
  3088. $reply .= "</span>";
  3089. } else {
  3090. $reply .= $feed_title;
  3091. }
  3092. } else {
  3093. $reply .= $feed_title;
  3094. }
  3095. }
  3096. if ($search) {
  3097. $search_q = "&q=$search&m=$match_on&smode=$search_mode";
  3098. } else {
  3099. $search_q = "";
  3100. }
  3101. // Adaptive doesn't really make any sense for generated feeds
  3102. // All Articles is the default, so no need to insert it either
  3103. if ($view_mode == "adaptive" || $view_mode == "all_articles")
  3104. $view_mode = "";
  3105. else
  3106. $view_mode = "&view-mode=$view_mode";
  3107. $reply .= "
  3108. <a href=\"#\"
  3109. title=\"".__("View as RSS feed")."\"
  3110. onclick=\"displayDlg('generatedFeed', '$feed_id:$is_cat:$rss_link')\">
  3111. <img class=\"noborder\" style=\"vertical-align : middle\" src=\"images/feed-icon-12x12.png\"></a>";
  3112. $reply .= "</div>";
  3113. return $reply;
  3114. }
  3115. function outputFeedList($link, $special = true) {
  3116. $feedlist = array();
  3117. $enable_cats = get_pref($link, 'ENABLE_FEED_CATS');
  3118. $feedlist['identifier'] = 'id';
  3119. $feedlist['label'] = 'name';
  3120. $feedlist['items'] = array();
  3121. $owner_uid = $_SESSION["uid"];
  3122. /* virtual feeds */
  3123. if ($special) {
  3124. if ($enable_cats) {
  3125. $cat_hidden = get_pref($link, "_COLLAPSED_SPECIAL");
  3126. $cat = feedlist_init_cat($link, -1, $cat_hidden);
  3127. } else {
  3128. $cat['items'] = array();
  3129. }
  3130. foreach (array(-4, -3, -1, -2, 0) as $i) {
  3131. array_push($cat['items'], feedlist_init_feed($link, $i));
  3132. }
  3133. if ($enable_cats) {
  3134. array_push($feedlist['items'], $cat);
  3135. } else {
  3136. $feedlist['items'] = array_merge($feedlist['items'], $cat['items']);
  3137. }
  3138. $result = db_query($link, "SELECT * FROM
  3139. ttrss_labels2 WHERE owner_uid = '$owner_uid' ORDER by caption");
  3140. if (db_num_rows($result) > 0) {
  3141. if (get_pref($link, 'ENABLE_FEED_CATS')) {
  3142. $cat_hidden = get_pref($link, "_COLLAPSED_LABELS");
  3143. $cat = feedlist_init_cat($link, -2, $cat_hidden);
  3144. } else {
  3145. $cat['items'] = array();
  3146. }
  3147. while ($line = db_fetch_assoc($result)) {
  3148. $label_id = -$line['id'] - 11;
  3149. $count = getFeedUnread($link, $label_id);
  3150. $feed = feedlist_init_feed($link, $label_id, false, $count);
  3151. $feed['fg_color'] = $line['fg_color'];
  3152. $feed['bg_color'] = $line['bg_color'];
  3153. array_push($cat['items'], $feed);
  3154. }
  3155. if ($enable_cats) {
  3156. array_push($feedlist['items'], $cat);
  3157. } else {
  3158. $feedlist['items'] = array_merge($feedlist['items'], $cat['items']);
  3159. }
  3160. }
  3161. }
  3162. /* if (get_pref($link, 'ENABLE_FEED_CATS')) {
  3163. if (get_pref($link, "FEEDS_SORT_BY_UNREAD")) {
  3164. $order_by_qpart = "order_id,category,unread DESC,title";
  3165. } else {
  3166. $order_by_qpart = "order_id,category,title";
  3167. }
  3168. } else {
  3169. if (get_pref($link, "FEEDS_SORT_BY_UNREAD")) {
  3170. $order_by_qpart = "unread DESC,title";
  3171. } else {
  3172. $order_by_qpart = "title";
  3173. }
  3174. } */
  3175. /* real feeds */
  3176. if ($enable_cats)
  3177. $order_by_qpart = "ttrss_feed_categories.order_id,category,
  3178. ttrss_feeds.order_id,title";
  3179. else
  3180. $order_by_qpart = "title";
  3181. $age_qpart = getMaxAgeSubquery();
  3182. $query = "SELECT ttrss_feeds.id, ttrss_feeds.title,
  3183. ".SUBSTRING_FOR_DATE."(last_updated,1,19) AS last_updated_noms,
  3184. cat_id,last_error,
  3185. ttrss_feed_categories.title AS category,
  3186. ttrss_feed_categories.collapsed,
  3187. value AS unread
  3188. FROM ttrss_feeds LEFT JOIN ttrss_feed_categories
  3189. ON (ttrss_feed_categories.id = cat_id)
  3190. LEFT JOIN ttrss_counters_cache
  3191. ON
  3192. (ttrss_feeds.id = feed_id)
  3193. WHERE
  3194. ttrss_feeds.owner_uid = '$owner_uid'
  3195. ORDER BY $order_by_qpart";
  3196. $result = db_query($link, $query);
  3197. $actid = $_REQUEST["actid"];
  3198. if (db_num_rows($result) > 0) {
  3199. $category = "";
  3200. if (!$enable_cats)
  3201. $cat['items'] = array();
  3202. else
  3203. $cat = false;
  3204. while ($line = db_fetch_assoc($result)) {
  3205. $feed = htmlspecialchars(trim($line["title"]));
  3206. if (!$feed) $feed = "[Untitled]";
  3207. $feed_id = $line["id"];
  3208. $unread = $line["unread"];
  3209. $cat_id = $line["cat_id"];
  3210. $tmp_category = $line["category"];
  3211. if (!$tmp_category) $tmp_category = __("Uncategorized");
  3212. if ($category != $tmp_category && $enable_cats) {
  3213. $category = $tmp_category;
  3214. $collapsed = sql_bool_to_bool($line["collapsed"]);
  3215. // workaround for NULL category
  3216. if ($category == __("Uncategorized")) {
  3217. $collapsed = get_pref($link, "_COLLAPSED_UNCAT");
  3218. }
  3219. if ($cat) array_push($feedlist['items'], $cat);
  3220. $cat = feedlist_init_cat($link, $cat_id, $collapsed);
  3221. }
  3222. $updated = make_local_datetime($link, $line["updated_noms"], false);
  3223. array_push($cat['items'], feedlist_init_feed($link, $feed_id,
  3224. $feed, $unread, $line['last_error'], $updated));
  3225. }
  3226. if ($enable_cats) {
  3227. array_push($feedlist['items'], $cat);
  3228. } else {
  3229. $feedlist['items'] = array_merge($feedlist['items'], $cat['items']);
  3230. }
  3231. }
  3232. return $feedlist;
  3233. }
  3234. function get_article_tags($link, $id, $owner_uid = 0) {
  3235. global $memcache;
  3236. $a_id = db_escape_string($id);
  3237. if (!$owner_uid) $owner_uid = $_SESSION["uid"];
  3238. $query = "SELECT DISTINCT tag_name,
  3239. owner_uid as owner FROM
  3240. ttrss_tags WHERE post_int_id = (SELECT int_id FROM ttrss_user_entries WHERE
  3241. ref_id = '$a_id' AND owner_uid = '$owner_uid' LIMIT 1) ORDER BY tag_name";
  3242. $obj_id = md5("TAGS:$owner_uid:$id");
  3243. $tags = array();
  3244. if ($memcache && $obj = $memcache->get($obj_id)) {
  3245. $tags = $obj;
  3246. } else {
  3247. /* check cache first */
  3248. $result = db_query($link, "SELECT tag_cache FROM ttrss_user_entries
  3249. WHERE ref_id = '$id' AND owner_uid = " . $_SESSION["uid"]);
  3250. $tag_cache = db_fetch_result($result, 0, "tag_cache");
  3251. if ($tag_cache) {
  3252. $tags = explode(",", $tag_cache);
  3253. } else {
  3254. /* do it the hard way */
  3255. $tmp_result = db_query($link, $query);
  3256. while ($tmp_line = db_fetch_assoc($tmp_result)) {
  3257. array_push($tags, $tmp_line["tag_name"]);
  3258. }
  3259. /* update the cache */
  3260. $tags_str = db_escape_string(join(",", $tags));
  3261. db_query($link, "UPDATE ttrss_user_entries
  3262. SET tag_cache = '$tags_str' WHERE ref_id = '$id'
  3263. AND owner_uid = " . $_SESSION["uid"]);
  3264. }
  3265. if ($memcache) $memcache->add($obj_id, $tags, 0, 3600);
  3266. }
  3267. return $tags;
  3268. }
  3269. function trim_value(&$value) {
  3270. $value = trim($value);
  3271. }
  3272. function trim_array($array) {
  3273. $tmp = $array;
  3274. array_walk($tmp, 'trim_value');
  3275. return $tmp;
  3276. }
  3277. function tag_is_valid($tag) {
  3278. if ($tag == '') return false;
  3279. if (preg_match("/^[0-9]*$/", $tag)) return false;
  3280. if (mb_strlen($tag) > 250) return false;
  3281. if (function_exists('iconv')) {
  3282. $tag = iconv("utf-8", "utf-8", $tag);
  3283. }
  3284. if (!$tag) return false;
  3285. return true;
  3286. }
  3287. function render_login_form($link, $mobile = 0) {
  3288. switch ($mobile) {
  3289. case 0:
  3290. require_once "login_form.php";
  3291. break;
  3292. case 1:
  3293. require_once "mobile/login_form.php";
  3294. break;
  3295. case 2:
  3296. require_once "mobile/classic/login_form.php";
  3297. }
  3298. }
  3299. // from http://developer.apple.com/internet/safari/faq.html
  3300. function no_cache_incantation() {
  3301. header("Expires: Mon, 22 Dec 1980 00:00:00 GMT"); // Happy birthday to me :)
  3302. header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT"); // always modified
  3303. header("Cache-Control: no-store, no-cache, must-revalidate, max-age=0"); // HTTP/1.1
  3304. header("Cache-Control: post-check=0, pre-check=0", false);
  3305. header("Pragma: no-cache"); // HTTP/1.0
  3306. }
  3307. function format_warning($msg, $id = "") {
  3308. global $link;
  3309. return "<div class=\"warning\" id=\"$id\">
  3310. <img src=\"".theme_image($link, "images/sign_excl.png")."\">$msg</div>";
  3311. }
  3312. function format_notice($msg, $id = "") {
  3313. global $link;
  3314. return "<div class=\"notice\" id=\"$id\">
  3315. <img src=\"".theme_image($link, "images/sign_info.png")."\">$msg</div>";
  3316. }
  3317. function format_error($msg, $id = "") {
  3318. global $link;
  3319. return "<div class=\"error\" id=\"$id\">
  3320. <img src=\"".theme_image($link, "images/sign_excl.png")."\">$msg</div>";
  3321. }
  3322. function print_notice($msg) {
  3323. return print format_notice($msg);
  3324. }
  3325. function print_warning($msg) {
  3326. return print format_warning($msg);
  3327. }
  3328. function print_error($msg) {
  3329. return print format_error($msg);
  3330. }
  3331. function T_sprintf() {
  3332. $args = func_get_args();
  3333. return vsprintf(__(array_shift($args)), $args);
  3334. }
  3335. function format_inline_player($link, $url, $ctype) {
  3336. $entry = "";
  3337. if (strpos($ctype, "audio/") === 0) {
  3338. if ($_SESSION["hasAudio"] && (strpos($ctype, "ogg") !== false ||
  3339. strpos($_SERVER['HTTP_USER_AGENT'], "Chrome") !== false ||
  3340. strpos($_SERVER['HTTP_USER_AGENT'], "Safari") !== false )) {
  3341. $id = 'AUDIO-' . uniqid();
  3342. $entry .= "<audio id=\"$id\"\">
  3343. <source src=\"$url\"></source>
  3344. </audio>";
  3345. $entry .= "<span onclick=\"player(this)\"
  3346. title=\"".__("Click to play")."\" status=\"0\"
  3347. class=\"player\" audio-id=\"$id\">".__("Play")."</span>";
  3348. } else {
  3349. $entry .= "<object type=\"application/x-shockwave-flash\"
  3350. data=\"extras/button/musicplayer.swf?song_url=$url\"
  3351. width=\"17\" height=\"17\" style='float : left; margin-right : 5px;'>
  3352. <param name=\"movie\"
  3353. value=\"extras/button/musicplayer.swf?song_url=$url\" />
  3354. </object>";
  3355. }
  3356. }
  3357. $filename = substr($url, strrpos($url, "/")+1);
  3358. $entry .= " <a target=\"_blank\" href=\"" . htmlspecialchars($url) . "\">" .
  3359. $filename . " (" . $ctype . ")" . "</a>";
  3360. return $entry;
  3361. }
  3362. function format_article($link, $id, $feed_id, $mark_as_read = true,
  3363. $zoom_mode = false) {
  3364. $rv = array();
  3365. $rv['id'] = $id;
  3366. /* we can figure out feed_id from article id anyway, why do we
  3367. * pass feed_id here? let's ignore the argument :( */
  3368. $result = db_query($link, "SELECT feed_id FROM ttrss_user_entries
  3369. WHERE ref_id = '$id'");
  3370. $feed_id = (int) db_fetch_result($result, 0, "feed_id");
  3371. $rv['feed_id'] = $feed_id;
  3372. //if (!$zoom_mode) { print "<article id='$id'><![CDATA["; };
  3373. $result = db_query($link, "SELECT rtl_content, always_display_enclosures FROM ttrss_feeds
  3374. WHERE id = '$feed_id' AND owner_uid = " . $_SESSION["uid"]);
  3375. if (db_num_rows($result) == 1) {
  3376. $rtl_content = sql_bool_to_bool(db_fetch_result($result, 0, "rtl_content"));
  3377. $always_display_enclosures = sql_bool_to_bool(db_fetch_result($result, 0, "always_display_enclosures"));
  3378. } else {
  3379. $rtl_content = false;
  3380. $always_display_enclosures = false;
  3381. }
  3382. if ($rtl_content) {
  3383. $rtl_tag = "dir=\"RTL\"";
  3384. $rtl_class = "RTL";
  3385. } else {
  3386. $rtl_tag = "";
  3387. $rtl_class = "";
  3388. }
  3389. if ($mark_as_read) {
  3390. $result = db_query($link, "UPDATE ttrss_user_entries
  3391. SET unread = false,last_read = NOW()
  3392. WHERE ref_id = '$id' AND owner_uid = " . $_SESSION["uid"]);
  3393. ccache_update($link, $feed_id, $_SESSION["uid"]);
  3394. }
  3395. $result = db_query($link, "SELECT title,link,content,feed_id,comments,int_id,
  3396. ".SUBSTRING_FOR_DATE."(updated,1,16) as updated,
  3397. (SELECT icon_url FROM ttrss_feeds WHERE id = feed_id) as icon_url,
  3398. (SELECT site_url FROM ttrss_feeds WHERE id = feed_id) as site_url,
  3399. num_comments,
  3400. author,
  3401. orig_feed_id,
  3402. note
  3403. FROM ttrss_entries,ttrss_user_entries
  3404. WHERE id = '$id' AND ref_id = id AND owner_uid = " . $_SESSION["uid"]);
  3405. if ($result) {
  3406. $line = db_fetch_assoc($result);
  3407. if ($line["icon_url"]) {
  3408. $feed_icon = "<img src=\"" . $line["icon_url"] . "\">";
  3409. } else {
  3410. $feed_icon = "&nbsp;";
  3411. }
  3412. $feed_site_url = $line['site_url'];
  3413. $num_comments = $line["num_comments"];
  3414. $entry_comments = "";
  3415. if ($num_comments > 0) {
  3416. if ($line["comments"]) {
  3417. $comments_url = $line["comments"];
  3418. } else {
  3419. $comments_url = $line["link"];
  3420. }
  3421. $entry_comments = "<a target='_blank' href=\"$comments_url\">$num_comments comments</a>";
  3422. } else {
  3423. if ($line["comments"] && $line["link"] != $line["comments"]) {
  3424. $entry_comments = "<a target='_blank' href=\"".$line["comments"]."\">comments</a>";
  3425. }
  3426. }
  3427. if ($zoom_mode) {
  3428. header("Content-Type: text/html");
  3429. $rv['content'] .= "<html><head>
  3430. <meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"/>
  3431. <title>Tiny Tiny RSS - ".$line["title"]."</title>
  3432. <link rel=\"stylesheet\" type=\"text/css\" href=\"tt-rss.css\">
  3433. </head><body>";
  3434. }
  3435. $rv['content'] .= "<div id=\"PTITLE-$id\" style=\"display : none\">" .
  3436. truncate_string(strip_tags($line['title']), 15) . "</div>";
  3437. $rv['content'] .= "<div class=\"postReply\" id=\"POST-$id\">";
  3438. $rv['content'] .= "<div onclick=\"return postClicked(event, $id)\"
  3439. class=\"postHeader\" id=\"POSTHDR-$id\">";
  3440. $entry_author = $line["author"];
  3441. if ($entry_author) {
  3442. $entry_author = __(" - ") . $entry_author;
  3443. }
  3444. $parsed_updated = make_local_datetime($link, $line["updated"], true,
  3445. false, true);
  3446. $rv['content'] .= "<div class=\"postDate$rtl_class\">$parsed_updated</div>";
  3447. if ($line["link"]) {
  3448. $rv['content'] .= "<div clear='both'><a target='_blank'
  3449. title=\"".htmlspecialchars($line['title'])."\"
  3450. href=\"" .
  3451. $line["link"] . "\">" .
  3452. truncate_string($line["title"], 100) .
  3453. "<span class='author'>$entry_author</span></a></div>";
  3454. } else {
  3455. $rv['content'] .= "<div clear='both'>" . $line["title"] . "$entry_author</div>";
  3456. }
  3457. $tags_str = format_tags_string(get_article_tags($link, $id), $id);
  3458. if (!$entry_comments) $entry_comments = "&nbsp;"; # placeholder
  3459. $rv['content'] .= "<div style='float : right'>
  3460. <img src='".theme_image($link, 'images/tag.png')."'
  3461. class='tagsPic' alt='Tags' title='Tags'>&nbsp;";
  3462. if (!$zoom_mode) {
  3463. $rv['content'] .= "<span id=\"ATSTR-$id\">$tags_str</span>
  3464. <a title=\"".__('Edit tags for this article')."\"
  3465. href=\"#\" onclick=\"editArticleTags($id, $feed_id)\">(+)</a>";
  3466. $rv['content'] .= "<img src=\"".theme_image($link, 'images/art-zoom.png')."\"
  3467. class='tagsPic' style=\"cursor : pointer\"
  3468. onclick=\"postOpenInNewTab(event, $id)\"
  3469. alt='Zoom' title='".__('Open article in new tab')."'>";
  3470. //$note_escaped = htmlspecialchars($line['note'], ENT_QUOTES);
  3471. $rv['content'] .= "<img src=\"".theme_image($link, 'images/art-pub-note.png')."\"
  3472. class='tagsPic' style=\"cursor : pointer\"
  3473. onclick=\"editArticleNote($id)\"
  3474. alt='PubNote' title='".__('Edit article note')."'>";
  3475. if (DIGEST_ENABLE) {
  3476. $rv['content'] .= "<img src=\"".theme_image($link, 'images/art-email.png')."\"
  3477. class='tagsPic' style=\"cursor : pointer\"
  3478. onclick=\"emailArticle($id)\"
  3479. alt='Zoom' title='".__('Forward by email')."'>";
  3480. }
  3481. if (ENABLE_TWEET_BUTTON) {
  3482. $rv['content'] .= "<img src=\"".theme_image($link, 'images/art-tweet.png')."\"
  3483. class='tagsPic' style=\"cursor : pointer\"
  3484. onclick=\"tweetArticle($id)\"
  3485. alt='Zoom' title='".__('Share on Twitter')."'>";
  3486. }
  3487. $rv['content'] .= "<img src=\"".theme_image($link, 'images/digest_checkbox.png')."\"
  3488. class='tagsPic' style=\"cursor : pointer\"
  3489. onclick=\"closeArticlePanel($id)\"
  3490. alt='Zoom' title='".__('Close this panel')."'>";
  3491. } else {
  3492. $tags_str = strip_tags($tags_str);
  3493. $rv['content'] .= "<span id=\"ATSTR-$id\">$tags_str</span>";
  3494. }
  3495. $rv['content'] .= "</div>";
  3496. $rv['content'] .= "<div clear='both'>$entry_comments</div>";
  3497. if ($line["orig_feed_id"]) {
  3498. $tmp_result = db_query($link, "SELECT * FROM ttrss_archived_feeds
  3499. WHERE id = ".$line["orig_feed_id"]);
  3500. if (db_num_rows($tmp_result) != 0) {
  3501. $rv['content'] .= "<div clear='both'>";
  3502. $rv['content'] .= __("Originally from:");
  3503. $rv['content'] .= "&nbsp;";
  3504. $tmp_line = db_fetch_assoc($tmp_result);
  3505. $rv['content'] .= "<a target='_blank'
  3506. href=' " . htmlspecialchars($tmp_line['site_url']) . "'>" .
  3507. $tmp_line['title'] . "</a>";
  3508. $rv['content'] .= "&nbsp;";
  3509. $rv['content'] .= "<a target='_blank' href='" . htmlspecialchars($tmp_line['feed_url']) . "'>";
  3510. $rv['content'] .= "<img title='".__('Feed URL')."'class='tinyFeedIcon' src='images/pub_set.gif'></a>";
  3511. $rv['content'] .= "</div>";
  3512. }
  3513. }
  3514. $rv['content'] .= "</div>";
  3515. $rv['content'] .= "<div class=\"postIcon\">" .
  3516. "<a target=\"_blank\" title=\"".__("Visit the website")."\"$
  3517. href=\"".htmlspecialchars($feed_site_url)."\">".
  3518. $feed_icon . "</a></div>";
  3519. $rv['content'] .= "<div id=\"POSTNOTE-$id\">";
  3520. if ($line['note']) {
  3521. $rv['content'] .= format_article_note($id, $line['note']);
  3522. }
  3523. $rv['content'] .= "</div>";
  3524. $rv['content'] .= "<div class=\"postContent\">";
  3525. $article_content = sanitize_rss($link, $line["content"], false, false,
  3526. $feed_site_url);
  3527. $rv['content'] .= $article_content;
  3528. $rv['content'] .= format_article_enclosures($link, $id,
  3529. $always_display_enclosures, $article_content);
  3530. $rv['content'] .= "</div>";
  3531. $rv['content'] .= "</div>";
  3532. }
  3533. if ($zoom_mode) {
  3534. $rv['content'] .= "
  3535. <div style=\"text-align : center\">
  3536. <button onclick=\"return window.close()\">".
  3537. __("Close this window")."</button></div>";
  3538. $rv['content'] .= "</body></html>";
  3539. }
  3540. return $rv;
  3541. }
  3542. function format_headlines_list($link, $feed, $subop, $view_mode, $limit, $cat_view,
  3543. $next_unread_feed, $offset, $vgr_last_feed = false,
  3544. $override_order = false) {
  3545. $disable_cache = false;
  3546. $reply = array();
  3547. $timing_info = getmicrotime();
  3548. $topmost_article_ids = array();
  3549. if (!$offset) {
  3550. $offset = 0;
  3551. }
  3552. if ($subop == "undefined") $subop = "";
  3553. $subop_split = split(":", $subop);
  3554. if ($subop == "CatchupSelected") {
  3555. $ids = split(",", db_escape_string($_REQUEST["ids"]));
  3556. $cmode = sprintf("%d", $_REQUEST["cmode"]);
  3557. catchupArticlesById($link, $ids, $cmode);
  3558. }
  3559. if ($subop == "ForceUpdate" && sprintf("%d", $feed) > 0) {
  3560. update_rss_feed($link, $feed, true);
  3561. }
  3562. if ($subop == "MarkAllRead") {
  3563. catchup_feed($link, $feed, $cat_view);
  3564. if (get_pref($link, 'ON_CATCHUP_SHOW_NEXT_FEED')) {
  3565. if ($next_unread_feed) {
  3566. $feed = $next_unread_feed;
  3567. }
  3568. }
  3569. }
  3570. if ($subop_split[0] == "MarkAllReadGR") {
  3571. catchup_feed($link, $subop_split[1], false);
  3572. }
  3573. // FIXME: might break tag display?
  3574. if ($feed > 0 && !$cat_view) {
  3575. $result = db_query($link,
  3576. "SELECT id FROM ttrss_feeds WHERE id = '$feed' LIMIT 1");
  3577. if (db_num_rows($result) == 0) {
  3578. $reply['content'] = "<div align='center'>".__('Feed not found.')."</div>";
  3579. }
  3580. }
  3581. if (preg_match("/^-?[0-9][0-9]*$/", $feed) != false) {
  3582. $result = db_query($link, "SELECT rtl_content FROM ttrss_feeds
  3583. WHERE id = '$feed' AND owner_uid = " . $_SESSION["uid"]);
  3584. if (db_num_rows($result) == 1) {
  3585. $rtl_content = sql_bool_to_bool(db_fetch_result($result, 0, "rtl_content"));
  3586. } else {
  3587. $rtl_content = false;
  3588. }
  3589. if ($rtl_content) {
  3590. $rtl_tag = "dir=\"RTL\"";
  3591. } else {
  3592. $rtl_tag = "";
  3593. }
  3594. } else {
  3595. $rtl_tag = "";
  3596. $rtl_content = false;
  3597. }
  3598. /// START /////////////////////////////////////////////////////////////////////////////////
  3599. @$search = db_escape_string($_REQUEST["query"]);
  3600. if ($search) {
  3601. $disable_cache = true;
  3602. }
  3603. @$search_mode = db_escape_string($_REQUEST["search_mode"]);
  3604. @$match_on = db_escape_string($_REQUEST["match_on"]);
  3605. if (!$match_on) {
  3606. $match_on = "both";
  3607. }
  3608. $real_offset = $offset * $limit;
  3609. if ($_REQUEST["debug"]) $timing_info = print_checkpoint("H0", $timing_info);
  3610. $qfh_ret = queryFeedHeadlines($link, $feed, $limit, $view_mode, $cat_view,
  3611. $search, $search_mode, $match_on, $override_order, $real_offset);
  3612. if ($_REQUEST["debug"]) $timing_info = print_checkpoint("H1", $timing_info);
  3613. $result = $qfh_ret[0];
  3614. $feed_title = $qfh_ret[1];
  3615. $feed_site_url = $qfh_ret[2];
  3616. $last_error = $qfh_ret[3];
  3617. $vgroup_last_feed = $vgr_last_feed;
  3618. /// STOP //////////////////////////////////////////////////////////////////////////////////
  3619. if (!$offset) {
  3620. if (db_num_rows($result) > 0) {
  3621. $reply['toolbar'] = format_headline_subtoolbar($link, $feed_site_url, $feed_title,
  3622. $feed, $cat_view, $search, $match_on, $search_mode, $view_mode,
  3623. $last_error);
  3624. }
  3625. }
  3626. $headlines_count = db_num_rows($result);
  3627. if (db_num_rows($result) > 0) {
  3628. $lnum = $limit*$offset;
  3629. $num_unread = 0;
  3630. $cur_feed_title = '';
  3631. $fresh_intl = get_pref($link, "FRESH_ARTICLE_MAX_AGE") * 60 * 60;
  3632. while ($line = db_fetch_assoc($result)) {
  3633. $class = ($lnum % 2) ? "even" : "odd";
  3634. $id = $line["id"];
  3635. $feed_id = $line["feed_id"];
  3636. $labels = get_article_labels($link, $id);
  3637. $labels_str = "<span id=\"HLLCTR-$id\">";
  3638. $labels_str .= format_article_labels($labels, $id);
  3639. $labels_str .= "</span>";
  3640. if (count($topmost_article_ids) < 3) {
  3641. array_push($topmost_article_ids, $id);
  3642. }
  3643. if ($line["last_read"] == "" && !sql_bool_to_bool($line["unread"])) {
  3644. $update_pic = "<img id='FUPDPIC-$id' src=\"".
  3645. theme_image($link, 'images/updated.png')."\"
  3646. alt=\"Updated\">";
  3647. } else {
  3648. $update_pic = "<img id='FUPDPIC-$id' src=\"images/blank_icon.gif\"
  3649. alt=\"Updated\">";
  3650. }
  3651. if (sql_bool_to_bool($line["unread"]) &&
  3652. time() - strtotime($line["updated_noms"]) < $fresh_intl) {
  3653. $update_pic = "<img id='FUPDPIC-$id' src=\"".
  3654. theme_image($link, 'images/fresh_sign.png')."\" alt=\"Fresh\">";
  3655. }
  3656. if ($line["unread"] == "t" || $line["unread"] == "1") {
  3657. $class .= " Unread";
  3658. ++$num_unread;
  3659. $is_unread = true;
  3660. } else {
  3661. $is_unread = false;
  3662. }
  3663. if ($line["marked"] == "t" || $line["marked"] == "1") {
  3664. $marked_pic = "<img id=\"FMPIC-$id\"
  3665. src=\"".theme_image($link, 'images/mark_set.png')."\"
  3666. class=\"markedPic\" alt=\"Unstar article\"
  3667. onclick='javascript:tMark($id)'>";
  3668. } else {
  3669. $marked_pic = "<img id=\"FMPIC-$id\"
  3670. src=\"".theme_image($link, 'images/mark_unset.png')."\"
  3671. class=\"markedPic\" alt=\"Star article\"
  3672. onclick='javascript:tMark($id)'>";
  3673. }
  3674. if ($line["published"] == "t" || $line["published"] == "1") {
  3675. $published_pic = "<img id=\"FPPIC-$id\" src=\"".theme_image($link,
  3676. 'images/pub_set.png')."\"
  3677. class=\"markedPic\"
  3678. alt=\"Unpublish article\" onclick='javascript:tPub($id)'>";
  3679. } else {
  3680. $published_pic = "<img id=\"FPPIC-$id\" src=\"".theme_image($link,
  3681. 'images/pub_unset.png')."\"
  3682. class=\"markedPic\"
  3683. alt=\"Publish article\" onclick='javascript:tPub($id)'>";
  3684. }
  3685. # $content_link = "<a target=\"_blank\" href=\"".$line["link"]."\">" .
  3686. # $line["title"] . "</a>";
  3687. # $content_link = "<a
  3688. # href=\"" . htmlspecialchars($line["link"]) . "\"
  3689. # onclick=\"view($id,$feed_id);\">" .
  3690. # $line["title"] . "</a>";
  3691. # $content_link = "<a href=\"javascript:viewContentUrl('".$line["link"]."');\">" .
  3692. # $line["title"] . "</a>";
  3693. $updated_fmt = make_local_datetime($link, $line["updated_noms"], false);
  3694. if (get_pref($link, 'SHOW_CONTENT_PREVIEW')) {
  3695. $content_preview = truncate_string(strip_tags($line["content_preview"]),
  3696. 100);
  3697. }
  3698. $score = $line["score"];
  3699. $score_pic = theme_image($link,
  3700. "images/" . get_score_pic($score));
  3701. /* $score_title = __("(Click to change)");
  3702. $score_pic = "<img class='hlScorePic' src=\"images/$score_pic\"
  3703. onclick=\"adjustArticleScore($id, $score)\" title=\"$score $score_title\">"; */
  3704. $score_pic = "<img class='hlScorePic' src=\"$score_pic\"
  3705. title=\"$score\">";
  3706. if ($score > 500) {
  3707. $hlc_suffix = "H";
  3708. } else if ($score < -100) {
  3709. $hlc_suffix = "L";
  3710. } else {
  3711. $hlc_suffix = "";
  3712. }
  3713. $entry_author = $line["author"];
  3714. if ($entry_author) {
  3715. $entry_author = " - $entry_author";
  3716. }
  3717. $has_feed_icon = feed_has_icon($feed_id);
  3718. if ($has_feed_icon) {
  3719. $feed_icon_img = "<img class=\"tinyFeedIcon\" src=\"".ICONS_URL."/$feed_id.ico\" alt=\"\">";
  3720. } else {
  3721. $feed_icon_img = "<img class=\"tinyFeedIcon\" src=\"images/feed-icon-12x12.png\" alt=\"\">";
  3722. }
  3723. if (!get_pref($link, 'COMBINED_DISPLAY_MODE')) {
  3724. if (get_pref($link, 'VFEED_GROUP_BY_FEED')) {
  3725. if ($feed_id != $vgroup_last_feed && $line["feed_title"]) {
  3726. $cur_feed_title = $line["feed_title"];
  3727. $vgroup_last_feed = $feed_id;
  3728. $cur_feed_title = htmlspecialchars($cur_feed_title);
  3729. $vf_catchup_link = "(<a onclick='javascript:catchupFeedInGroup($feed_id);' href='#'>".__('mark as read')."</a>)";
  3730. $reply['content'] .= "<div class='cdmFeedTitle'>".
  3731. "<div style=\"float : right\">$feed_icon_img</div>".
  3732. "<a href=\"#\" onclick=\"viewfeed($feed_id)\">".
  3733. $line["feed_title"]."</a> $vf_catchup_link</div>";
  3734. }
  3735. }
  3736. $mouseover_attrs = "onmouseover='postMouseIn($id)'
  3737. onmouseout='postMouseOut($id)'";
  3738. $reply['content'] .= "<div class='$class' id='RROW-$id' $mouseover_attrs>";
  3739. $reply['content'] .= "<div class='hlUpdPic'>$update_pic</div>";
  3740. $reply['content'] .= "<div class='hlLeft'>";
  3741. $reply['content'] .= "<input type=\"checkbox\" onclick=\"tSR(this)\"
  3742. id=\"RCHK-$id\">";
  3743. $reply['content'] .= "$marked_pic";
  3744. $reply['content'] .= "$published_pic";
  3745. $reply['content'] .= "</div>";
  3746. $reply['content'] .= "<div onclick='return hlClicked(event, $id)'
  3747. class=\"hlTitle\"><span class='hlContent$hlc_suffix'>";
  3748. $reply['content'] .= "<a id=\"RTITLE-$id\"
  3749. href=\"" . htmlspecialchars($line["link"]) . "\"
  3750. onclick=\"return false;\">" .
  3751. $line["title"];
  3752. if (get_pref($link, 'SHOW_CONTENT_PREVIEW')) {
  3753. if ($content_preview) {
  3754. $reply['content'] .= "<span class=\"contentPreview\"> - $content_preview</span>";
  3755. }
  3756. }
  3757. $reply['content'] .= "</a></span>";
  3758. $reply['content'] .= $labels_str;
  3759. /* if (!get_pref($link, 'VFEED_GROUP_BY_FEED')) {
  3760. if (@$line["feed_title"]) {
  3761. print "<span class=\"hlFeed\">
  3762. (<a href=\"#\" onclick=\"viewfeed($feed_id)\">".
  3763. $line["feed_title"]."</a>)
  3764. </span>";
  3765. }
  3766. } */
  3767. $reply['content'] .= "</div>";
  3768. $reply['content'] .= "<div class=\"hlRight\">";
  3769. $reply['content'] .= "<span class=\"hlUpdated\">$updated_fmt</span>";
  3770. $reply['content'] .= $score_pic;
  3771. if ($line["feed_title"] && !get_pref($link, 'VFEED_GROUP_BY_FEED')) {
  3772. $reply['content'] .= "<span onclick=\"viewfeed($feed_id)\"
  3773. title=\"".htmlspecialchars($line['feed_title'])."\">
  3774. $feed_icon_img<span>";
  3775. }
  3776. $reply['content'] .= "</div>";
  3777. $reply['content'] .= "</div>";
  3778. } else {
  3779. if (get_pref($link, 'VFEED_GROUP_BY_FEED') && $line["feed_title"]) {
  3780. if ($feed_id != $vgroup_last_feed) {
  3781. $cur_feed_title = $line["feed_title"];
  3782. $vgroup_last_feed = $feed_id;
  3783. $cur_feed_title = htmlspecialchars($cur_feed_title);
  3784. $vf_catchup_link = "(<a onclick='javascript:catchupFeedInGroup($feed_id);' href='#'>".__('mark as read')."</a>)";
  3785. $has_feed_icon = feed_has_icon($feed_id);
  3786. if ($has_feed_icon) {
  3787. $feed_icon_img = "<img class=\"tinyFeedIcon\" src=\"".ICONS_URL."/$feed_id.ico\" alt=\"\">";
  3788. } else {
  3789. //$feed_icon_img = "<img class=\"tinyFeedIcon\" src=\"images/blank_icon.gif\" alt=\"\">";
  3790. }
  3791. $reply['content'] .= "<div class='cdmFeedTitle'>".
  3792. "<div style=\"float : right\">$feed_icon_img</div>".
  3793. "<a href=\"#\" onclick=\"viewfeed($feed_id)\">".
  3794. $line["feed_title"]."</a> $vf_catchup_link</div>";
  3795. }
  3796. }
  3797. $expand_cdm = get_pref($link, 'CDM_EXPANDED');
  3798. $mouseover_attrs = "onmouseover='postMouseIn($id)'
  3799. onmouseout='postMouseOut($id)'";
  3800. $reply['content'] .= "<div class=\"$class\"
  3801. id=\"RROW-$id\" $mouseover_attrs'>";
  3802. $reply['content'] .= "<div class=\"cdmHeader\">";
  3803. $reply['content'] .= "<div style='float : right'>";
  3804. $reply['content'] .= "<span class='updated'>$updated_fmt</span>";
  3805. $reply['content'] .= "$score_pic";
  3806. if (!get_pref($link, "VFEED_GROUP_BY_FEED") && $line["feed_title"]) {
  3807. $reply['content'] .= "<span style=\"cursor : pointer\"
  3808. title=\"".htmlspecialchars($line["feed_title"])."\"
  3809. onclick=\"viewfeed($feed_id)\">$feed_icon_img</span>";
  3810. }
  3811. $reply['content'] .= "<div class=\"updPic\">$update_pic</div>";
  3812. $reply['content'] .= "</div>";
  3813. $reply['content'] .= "<input type=\"checkbox\" onclick=\"toggleSelectRowById(this,
  3814. 'RROW-$id')\" id=\"RCHK-$id\"/>";
  3815. $reply['content'] .= "$marked_pic";
  3816. $reply['content'] .= "$published_pic";
  3817. $reply['content'] .= "<span id=\"RTITLE-$id\"
  3818. onclick=\"return cdmClicked(event, $id);\"
  3819. class=\"titleWrap$hlc_suffix\">
  3820. <a class=\"title\"
  3821. title=\"".htmlspecialchars($line['title'])."\"
  3822. target=\"_blank\" href=\"".
  3823. htmlspecialchars($line["link"])."\">".
  3824. truncate_string($line["title"], 100) .
  3825. " $entry_author</a>";
  3826. $reply['content'] .= $labels_str;
  3827. if (!$expand_cdm)
  3828. $content_hidden = "style=\"display : none\"";
  3829. else
  3830. $excerpt_hidden = "style=\"display : none\"";
  3831. $reply['content'] .= "<span $excerpt_hidden
  3832. id=\"CEXC-$id\" class=\"cdmExcerpt\"> - $content_preview</span>";
  3833. $reply['content'] .= "</span>";
  3834. $reply['content'] .= "</div>";
  3835. $reply['content'] .= "<div class=\"cdmContent\" $content_hidden
  3836. onclick=\"return cdmClicked(event, $id);\"
  3837. id=\"CICD-$id\">";
  3838. $reply['content'] .= "<div class=\"cdmContentInner\">";
  3839. if ($line["orig_feed_id"]) {
  3840. $tmp_result = db_query($link, "SELECT * FROM ttrss_archived_feeds
  3841. WHERE id = ".$line["orig_feed_id"]);
  3842. if (db_num_rows($tmp_result) != 0) {
  3843. $reply['content'] .= "<div clear='both'>";
  3844. $reply['content'] .= __("Originally from:");
  3845. $reply['content'] .= "&nbsp;";
  3846. $tmp_line = db_fetch_assoc($tmp_result);
  3847. $reply['content'] .= "<a target='_blank'
  3848. href=' " . htmlspecialchars($tmp_line['site_url']) . "'>" .
  3849. $tmp_line['title'] . "</a>";
  3850. $reply['content'] .= "&nbsp;";
  3851. $reply['content'] .= "<a target='_blank' href='" . htmlspecialchars($tmp_line['feed_url']) . "'>";
  3852. $reply['content'] .= "<img title='".__('Feed URL')."'class='tinyFeedIcon' src='images/pub_set.gif'></a>";
  3853. $reply['content'] .= "</div>";
  3854. }
  3855. }
  3856. // FIXME: make this less of a hack
  3857. $feed_site_url = false;
  3858. if ($line["feed_id"]) {
  3859. $tmp_result = db_query($link, "SELECT site_url FROM ttrss_feeds
  3860. WHERE id = " . $line["feed_id"]);
  3861. if (db_num_rows($tmp_result) == 1) {
  3862. $feed_site_url = db_fetch_result($tmp_result, 0, "site_url");
  3863. }
  3864. }
  3865. if ($expand_cdm) {
  3866. $article_content = sanitize_rss($link, $line["content_preview"],
  3867. false, false, $feed_site_url);
  3868. if (!$article_content) $article_content = "&nbsp;";
  3869. } else {
  3870. $article_content = '';
  3871. }
  3872. $reply['content'] .= "<div id=\"POSTNOTE-$id\">";
  3873. if ($line['note']) {
  3874. $reply['content'] .= format_article_note($id, $line['note']);
  3875. }
  3876. $reply['content'] .= "</div>";
  3877. $reply['content'] .= "<span id=\"CWRAP-$id\">$article_content</span>";
  3878. $tmp_result = db_query($link, "SELECT always_display_enclosures FROM
  3879. ttrss_feeds WHERE id = ".
  3880. (($line['feed_id'] == null) ? $line['orig_feed_id'] :
  3881. $line['feed_id'])." AND owner_uid = ".$_SESSION["uid"]);
  3882. $always_display_enclosures = sql_bool_to_bool(db_fetch_result($tmp_result,
  3883. 0, "always_display_enclosures"));
  3884. $reply['content'] .= format_article_enclosures($link, $id, $always_display_enclosures,
  3885. $article_content);
  3886. $reply['content'] .= "</div>";
  3887. $reply['content'] .= "<div class=\"cdmFooter\">";
  3888. $tags_str = format_tags_string(get_article_tags($link, $id), $id);
  3889. $reply['content'] .= "<img src='".theme_image($link,
  3890. 'images/tag.png')."' alt='Tags' title='Tags'>
  3891. <span id=\"ATSTR-$id\">$tags_str</span>
  3892. <a title=\"".__('Edit tags for this article')."\"
  3893. href=\"#\" onclick=\"editArticleTags($id, $feed_id, true)\">(+)</a>";
  3894. $reply['content'] .= "<div style=\"float : right\">";
  3895. $reply['content'] .= "<img src=\"images/art-zoom.png\"
  3896. onclick=\"zoomToArticle(event, $id)\"
  3897. style=\"cursor : pointer\"
  3898. alt='Zoom'
  3899. title='".__('Open article in new tab')."'>";
  3900. //$note_escaped = htmlspecialchars($line['note'], ENT_QUOTES);
  3901. $reply['content'] .= "<img src=\"images/art-pub-note.png\"
  3902. style=\"cursor : pointer\" style=\"cursor : pointer\"
  3903. onclick=\"editArticleNote($id)\"
  3904. alt='PubNote' title='".__('Edit article note')."'>";
  3905. if (DIGEST_ENABLE) {
  3906. $reply['content'] .= "<img src=\"".theme_image($link, 'images/art-email.png')."\"
  3907. style=\"cursor : pointer\"
  3908. onclick=\"emailArticle($id)\"
  3909. alt='Zoom' title='".__('Forward by email')."'>";
  3910. }
  3911. if (ENABLE_TWEET_BUTTON) {
  3912. $reply['content'] .= "<img src=\"".theme_image($link, 'images/art-tweet.png')."\"
  3913. class='tagsPic' style=\"cursor : pointer\"
  3914. onclick=\"tweetArticle($id)\"
  3915. alt='Zoom' title='".__('Share on Twitter')."'>";
  3916. }
  3917. $reply['content'] .= "<img src=\"images/digest_checkbox.png\"
  3918. style=\"cursor : pointer\" style=\"cursor : pointer\"
  3919. onclick=\"dismissArticle($id)\"
  3920. alt='Dismiss' title='".__('Dismiss article')."'>";
  3921. $reply['content'] .= "</div>";
  3922. $reply['content'] .= "</div>";
  3923. $reply['content'] .= "</div>";
  3924. $reply['content'] .= "</div>";
  3925. }
  3926. ++$lnum;
  3927. }
  3928. } else {
  3929. $message = "";
  3930. switch ($view_mode) {
  3931. case "unread":
  3932. $message = __("No unread articles found to display.");
  3933. break;
  3934. case "updated":
  3935. $message = __("No updated articles found to display.");
  3936. break;
  3937. case "marked":
  3938. $message = __("No starred articles found to display.");
  3939. break;
  3940. default:
  3941. if ($feed < -10) {
  3942. $message = __("No articles found to display. You can assign articles to labels manually (see the Actions menu above) or use a filter.");
  3943. } else {
  3944. $message = __("No articles found to display.");
  3945. }
  3946. }
  3947. if (!$offset && $message) {
  3948. $reply['content'] .= "<div class='whiteBox'>$message";
  3949. $reply['content'] .= "<p class=\"small\"><span class=\"insensitive\">";
  3950. $result = db_query($link, "SELECT ".SUBSTRING_FOR_DATE."(MAX(last_updated), 1, 19) AS last_updated FROM ttrss_feeds
  3951. WHERE owner_uid = " . $_SESSION['uid']);
  3952. $last_updated = db_fetch_result($result, 0, "last_updated");
  3953. $last_updated = make_local_datetime($link, $last_updated, false);
  3954. $reply['content'] .= sprintf(__("Feeds last updated at %s"), $last_updated);
  3955. $result = db_query($link, "SELECT COUNT(id) AS num_errors
  3956. FROM ttrss_feeds WHERE last_error != '' AND owner_uid = ".$_SESSION["uid"]);
  3957. $num_errors = db_fetch_result($result, 0, "num_errors");
  3958. if ($num_errors > 0) {
  3959. $reply['content'] .= "<br/>";
  3960. $reply['content'] .= "<a class=\"insensitive\" href=\"#\" onclick=\"showFeedsWithErrors()\">".
  3961. __('Some feeds have update errors (click for details)')."</a>";
  3962. }
  3963. $reply['content'] .= "</span></p></div>";
  3964. }
  3965. }
  3966. # if (!$offset) {
  3967. # if ($headlines_count > 0) print "</div>";
  3968. # print "</div>";
  3969. # }
  3970. #print "]]></content>";
  3971. return array($topmost_article_ids, $headlines_count, $feed, $disable_cache,
  3972. $vgroup_last_feed, $reply['content'], $reply['toolbar']);
  3973. }
  3974. // from here: http://www.roscripts.com/Create_tag_cloud-71.html
  3975. function printTagCloud($link) {
  3976. $query = "SELECT tag_name, COUNT(post_int_id) AS count
  3977. FROM ttrss_tags WHERE owner_uid = ".$_SESSION["uid"]."
  3978. GROUP BY tag_name ORDER BY count DESC LIMIT 50";
  3979. $result = db_query($link, $query);
  3980. $tags = array();
  3981. while ($line = db_fetch_assoc($result)) {
  3982. $tags[$line["tag_name"]] = $line["count"];
  3983. }
  3984. ksort($tags);
  3985. $max_size = 32; // max font size in pixels
  3986. $min_size = 11; // min font size in pixels
  3987. // largest and smallest array values
  3988. $max_qty = max(array_values($tags));
  3989. $min_qty = min(array_values($tags));
  3990. // find the range of values
  3991. $spread = $max_qty - $min_qty;
  3992. if ($spread == 0) { // we don't want to divide by zero
  3993. $spread = 1;
  3994. }
  3995. // set the font-size increment
  3996. $step = ($max_size - $min_size) / ($spread);
  3997. // loop through the tag array
  3998. foreach ($tags as $key => $value) {
  3999. // calculate font-size
  4000. // find the $value in excess of $min_qty
  4001. // multiply by the font-size increment ($size)
  4002. // and add the $min_size set above
  4003. $size = round($min_size + (($value - $min_qty) * $step));
  4004. $key_escaped = str_replace("'", "\\'", $key);
  4005. echo "<a href=\"javascript:viewfeed('$key_escaped') \" style=\"font-size: " .
  4006. $size . "px\" title=\"$value articles tagged with " .
  4007. $key . '">' . $key . '</a> ';
  4008. }
  4009. }
  4010. function print_checkpoint($n, $s) {
  4011. $ts = getmicrotime();
  4012. echo sprintf("<!-- CP[$n] %.4f seconds -->", $ts - $s);
  4013. return $ts;
  4014. }
  4015. function sanitize_tag($tag) {
  4016. $tag = trim($tag);
  4017. $tag = mb_strtolower($tag, 'utf-8');
  4018. $tag = preg_replace('/[\"\+\>\<]/', "", $tag);
  4019. // $tag = str_replace('"', "", $tag);
  4020. // $tag = str_replace("+", " ", $tag);
  4021. $tag = str_replace("technorati tag: ", "", $tag);
  4022. return $tag;
  4023. }
  4024. function get_self_url_prefix() {
  4025. $url_path = "";
  4026. if ($_SERVER['HTTPS'] != "on") {
  4027. $url_path = "http://";
  4028. } else {
  4029. $url_path = "https://";
  4030. }
  4031. $url_path .= $_SERVER['HTTP_HOST'].dirname($_SERVER['PHP_SELF']);
  4032. return $url_path;
  4033. }
  4034. function opml_publish_url($link){
  4035. $url_path = get_self_url_prefix();
  4036. $url_path .= "/opml.php?op=publish&key=" .
  4037. get_feed_access_key($link, 'OPML:Publish', false, $_SESSION["uid"]);
  4038. return $url_path;
  4039. }
  4040. /**
  4041. * Purge a feed contents, marked articles excepted.
  4042. *
  4043. * @param mixed $link The database connection.
  4044. * @param integer $id The id of the feed to purge.
  4045. * @return void
  4046. */
  4047. function clear_feed_articles($link, $id) {
  4048. if ($id != 0) {
  4049. $result = db_query($link, "DELETE FROM ttrss_user_entries
  4050. WHERE feed_id = '$id' AND marked = false AND owner_uid = " . $_SESSION["uid"]);
  4051. } else {
  4052. $result = db_query($link, "DELETE FROM ttrss_user_entries
  4053. WHERE feed_id IS NULL AND marked = false AND owner_uid = " . $_SESSION["uid"]);
  4054. }
  4055. $result = db_query($link, "DELETE FROM ttrss_entries WHERE
  4056. (SELECT COUNT(int_id) FROM ttrss_user_entries WHERE ref_id = id) = 0");
  4057. ccache_update($link, $id, $_SESSION['uid']);
  4058. } // function clear_feed_articles
  4059. /**
  4060. * Compute the Mozilla Firefox feed adding URL from server HOST and REQUEST_URI.
  4061. *
  4062. * @return string The Mozilla Firefox feed adding URL.
  4063. */
  4064. function add_feed_url() {
  4065. $url_path = ($_SERVER['HTTPS'] != "on" ? 'http://' : 'https://') . $_SERVER["HTTP_HOST"] . parse_url($_SERVER["REQUEST_URI"], PHP_URL_PATH);
  4066. $url_path .= "?op=pref-feeds&quiet=1&subop=add&feed_url=%s";
  4067. return $url_path;
  4068. } // function add_feed_url
  4069. /**
  4070. * Encrypt a password in SHA1.
  4071. *
  4072. * @param string $pass The password to encrypt.
  4073. * @param string $login A optionnal login.
  4074. * @return string The encrypted password.
  4075. */
  4076. function encrypt_password($pass, $login = '') {
  4077. if ($login) {
  4078. return "SHA1X:" . sha1("$login:$pass");
  4079. } else {
  4080. return "SHA1:" . sha1($pass);
  4081. }
  4082. } // function encrypt_password
  4083. /**
  4084. * Update a feed batch.
  4085. * Used by daemons to update n feeds by run.
  4086. * Only update feed needing a update, and not being processed
  4087. * by another process.
  4088. *
  4089. * @param mixed $link Database link
  4090. * @param integer $limit Maximum number of feeds in update batch. Default to DAEMON_FEED_LIMIT.
  4091. * @param boolean $from_http Set to true if you call this function from http to disable cli specific code.
  4092. * @param boolean $debug Set to false to disable debug output. Default to true.
  4093. * @return void
  4094. */
  4095. function update_daemon_common($link, $limit = DAEMON_FEED_LIMIT, $from_http = false, $debug = true) {
  4096. // Process all other feeds using last_updated and interval parameters
  4097. // Test if the user has loggued in recently. If not, it does not update its feeds.
  4098. if (DAEMON_UPDATE_LOGIN_LIMIT > 0) {
  4099. if (DB_TYPE == "pgsql") {
  4100. $login_thresh_qpart = "AND ttrss_users.last_login >= NOW() - INTERVAL '".DAEMON_UPDATE_LOGIN_LIMIT." days'";
  4101. } else {
  4102. $login_thresh_qpart = "AND ttrss_users.last_login >= DATE_SUB(NOW(), INTERVAL ".DAEMON_UPDATE_LOGIN_LIMIT." DAY)";
  4103. }
  4104. } else {
  4105. $login_thresh_qpart = "";
  4106. }
  4107. // Test if the feed need a update (update interval exceded).
  4108. if (DB_TYPE == "pgsql") {
  4109. $update_limit_qpart = "AND ((
  4110. ttrss_feeds.update_interval = 0
  4111. AND ttrss_feeds.last_updated < NOW() - CAST((ttrss_user_prefs.value || ' minutes') AS INTERVAL)
  4112. ) OR (
  4113. ttrss_feeds.update_interval > 0
  4114. AND ttrss_feeds.last_updated < NOW() - CAST((ttrss_feeds.update_interval || ' minutes') AS INTERVAL)
  4115. ) OR ttrss_feeds.last_updated IS NULL)";
  4116. } else {
  4117. $update_limit_qpart = "AND ((
  4118. ttrss_feeds.update_interval = 0
  4119. AND ttrss_feeds.last_updated < DATE_SUB(NOW(), INTERVAL CONVERT(ttrss_user_prefs.value, SIGNED INTEGER) MINUTE)
  4120. ) OR (
  4121. ttrss_feeds.update_interval > 0
  4122. AND ttrss_feeds.last_updated < DATE_SUB(NOW(), INTERVAL ttrss_feeds.update_interval MINUTE)
  4123. ) OR ttrss_feeds.last_updated IS NULL)";
  4124. }
  4125. // Test if feed is currently being updated by another process.
  4126. if (DB_TYPE == "pgsql") {
  4127. $updstart_thresh_qpart = "AND (ttrss_feeds.last_update_started IS NULL OR ttrss_feeds.last_update_started < NOW() - INTERVAL '120 seconds')";
  4128. } else {
  4129. $updstart_thresh_qpart = "AND (ttrss_feeds.last_update_started IS NULL OR ttrss_feeds.last_update_started < DATE_SUB(NOW(), INTERVAL 120 SECOND))";
  4130. }
  4131. // Test if there is a limit to number of updated feeds
  4132. $query_limit = "";
  4133. if($limit) $query_limit = sprintf("LIMIT %d", $limit);
  4134. $random_qpart = sql_random_function();
  4135. // We search for feed needing update.
  4136. $result = db_query($link, "SELECT ttrss_feeds.feed_url,ttrss_feeds.id, ttrss_feeds.owner_uid,
  4137. ".SUBSTRING_FOR_DATE."(ttrss_feeds.last_updated,1,19) AS last_updated,
  4138. ttrss_feeds.update_interval
  4139. FROM
  4140. ttrss_feeds, ttrss_users, ttrss_user_prefs
  4141. WHERE
  4142. ttrss_feeds.owner_uid = ttrss_users.id
  4143. AND ttrss_users.id = ttrss_user_prefs.owner_uid
  4144. AND ttrss_user_prefs.pref_name = 'DEFAULT_UPDATE_INTERVAL'
  4145. $login_thresh_qpart $update_limit_qpart
  4146. $updstart_thresh_qpart
  4147. ORDER BY $random_qpart $query_limit");
  4148. $user_prefs_cache = array();
  4149. if($debug) _debug(sprintf("Scheduled %d feeds to update...\n", db_num_rows($result)));
  4150. // Here is a little cache magic in order to minimize risk of double feed updates.
  4151. $feeds_to_update = array();
  4152. while ($line = db_fetch_assoc($result)) {
  4153. $feeds_to_update[$line['id']] = $line;
  4154. }
  4155. // We update the feed last update started date before anything else.
  4156. // There is no lag due to feed contents downloads
  4157. // It prevent an other process to update the same feed.
  4158. $feed_ids = array_keys($feeds_to_update);
  4159. if($feed_ids) {
  4160. db_query($link, sprintf("UPDATE ttrss_feeds SET last_update_started = NOW()
  4161. WHERE id IN (%s)", implode(',', $feed_ids)));
  4162. }
  4163. // For each feed, we call the feed update function.
  4164. while ($line = array_pop($feeds_to_update)) {
  4165. if($debug) _debug("Feed: " . $line["feed_url"] . ", " . $line["last_updated"]);
  4166. update_rss_feed($link, $line["id"], true);
  4167. sleep(1); // prevent flood (FIXME make this an option?)
  4168. }
  4169. // Send feed digests by email if needed.
  4170. if (DAEMON_SENDS_DIGESTS) send_headlines_digests($link);
  4171. } // function update_daemon_common
  4172. function sanitize_article_content($text) {
  4173. # we don't support CDATA sections in articles, they break our own escaping
  4174. $text = preg_replace("/\[\[CDATA/", "", $text);
  4175. $text = preg_replace("/\]\]\>/", "", $text);
  4176. return $text;
  4177. }
  4178. function load_filters($link, $feed, $owner_uid, $action_id = false) {
  4179. $filters = array();
  4180. global $memcache;
  4181. $obj_id = md5("FILTER:$feed:$owner_uid:$action_id");
  4182. if ($memcache && $obj = $memcache->get($obj_id)) {
  4183. return $obj;
  4184. } else {
  4185. if ($action_id) $ftype_query_part = "action_id = '$action_id' AND";
  4186. $result = db_query($link, "SELECT reg_exp,
  4187. ttrss_filter_types.name AS name,
  4188. ttrss_filter_actions.name AS action,
  4189. inverse,
  4190. action_param,
  4191. filter_param
  4192. FROM ttrss_filters,ttrss_filter_types,ttrss_filter_actions WHERE
  4193. enabled = true AND
  4194. $ftype_query_part
  4195. owner_uid = $owner_uid AND
  4196. ttrss_filter_types.id = filter_type AND
  4197. ttrss_filter_actions.id = action_id AND
  4198. (feed_id IS NULL OR feed_id = '$feed') ORDER BY reg_exp");
  4199. while ($line = db_fetch_assoc($result)) {
  4200. if (!$filters[$line["name"]]) $filters[$line["name"]] = array();
  4201. $filter["reg_exp"] = $line["reg_exp"];
  4202. $filter["action"] = $line["action"];
  4203. $filter["action_param"] = $line["action_param"];
  4204. $filter["filter_param"] = $line["filter_param"];
  4205. $filter["inverse"] = sql_bool_to_bool($line["inverse"]);
  4206. array_push($filters[$line["name"]], $filter);
  4207. }
  4208. if ($memcache) $memcache->add($obj_id, $filters, 0, 3600*8);
  4209. return $filters;
  4210. }
  4211. }
  4212. function get_score_pic($score) {
  4213. if ($score > 100) {
  4214. return "score_high.png";
  4215. } else if ($score > 0) {
  4216. return "score_half_high.png";
  4217. } else if ($score < -100) {
  4218. return "score_low.png";
  4219. } else if ($score < 0) {
  4220. return "score_half_low.png";
  4221. } else {
  4222. return "score_neutral.png";
  4223. }
  4224. }
  4225. function rounded_table_start($classname, $header = "&nbsp;") {
  4226. print "<table width='100%' class='$classname' cellspacing='0' cellpadding='0'>";
  4227. print "<tr><td class='c1'>&nbsp;</td><td class='top'>$header</td><td class='c2'>&nbsp;</td></tr>";
  4228. print "<tr><td class='left'>&nbsp;</td><td class='content'>";
  4229. }
  4230. function rounded_table_end($footer = "&nbsp;") {
  4231. print "</td><td class='right'>&nbsp;</td></tr>";
  4232. print "<tr><td class='c4'>&nbsp;</td><td class='bottom'>$footer</td><td class='c3'>&nbsp;</td></tr>";
  4233. print "</table>";
  4234. }
  4235. function feed_has_icon($id) {
  4236. return is_file(ICONS_DIR . "/$id.ico") && filesize(ICONS_DIR . "/$id.ico") > 0;
  4237. }
  4238. function init_connection($link) {
  4239. if (DB_TYPE == "pgsql") {
  4240. pg_query($link, "set client_encoding = 'UTF-8'");
  4241. pg_set_client_encoding("UNICODE");
  4242. pg_query($link, "set datestyle = 'ISO, european'");
  4243. pg_query($link, "set TIME ZONE 0");
  4244. } else {
  4245. db_query($link, "SET time_zone = '+0:0'");
  4246. if (defined('MYSQL_CHARSET') && MYSQL_CHARSET) {
  4247. db_query($link, "SET NAMES " . MYSQL_CHARSET);
  4248. // db_query($link, "SET CHARACTER SET " . MYSQL_CHARSET);
  4249. }
  4250. }
  4251. }
  4252. function update_feedbrowser_cache($link) {
  4253. $result = db_query($link, "SELECT feed_url,title, COUNT(id) AS subscribers
  4254. FROM ttrss_feeds WHERE (SELECT COUNT(id) = 0 FROM ttrss_feeds AS tf
  4255. WHERE tf.feed_url = ttrss_feeds.feed_url
  4256. AND (private IS true OR feed_url LIKE '%:%@%/%'))
  4257. GROUP BY feed_url, title ORDER BY subscribers DESC LIMIT 1000");
  4258. db_query($link, "BEGIN");
  4259. db_query($link, "DELETE FROM ttrss_feedbrowser_cache");
  4260. $count = 0;
  4261. while ($line = db_fetch_assoc($result)) {
  4262. $subscribers = db_escape_string($line["subscribers"]);
  4263. $feed_url = db_escape_string($line["feed_url"]);
  4264. $title = db_escape_string($line["title"]);
  4265. $tmp_result = db_query($link, "SELECT subscribers FROM
  4266. ttrss_feedbrowser_cache WHERE feed_url = '$feed_url'");
  4267. if (db_num_rows($tmp_result) == 0) {
  4268. db_query($link, "INSERT INTO ttrss_feedbrowser_cache
  4269. (feed_url, title, subscribers) VALUES ('$feed_url',
  4270. '$title', '$subscribers')");
  4271. ++$count;
  4272. }
  4273. }
  4274. db_query($link, "COMMIT");
  4275. return $count;
  4276. }
  4277. function ccache_zero($link, $feed_id, $owner_uid) {
  4278. db_query($link, "UPDATE ttrss_counters_cache SET
  4279. value = 0, updated = NOW() WHERE
  4280. feed_id = '$feed_id' AND owner_uid = '$owner_uid'");
  4281. }
  4282. function ccache_zero_all($link, $owner_uid) {
  4283. db_query($link, "UPDATE ttrss_counters_cache SET
  4284. value = 0 WHERE owner_uid = '$owner_uid'");
  4285. db_query($link, "UPDATE ttrss_cat_counters_cache SET
  4286. value = 0 WHERE owner_uid = '$owner_uid'");
  4287. }
  4288. function ccache_remove($link, $feed_id, $owner_uid, $is_cat = false) {
  4289. if (!$is_cat) {
  4290. $table = "ttrss_counters_cache";
  4291. } else {
  4292. $table = "ttrss_cat_counters_cache";
  4293. }
  4294. db_query($link, "DELETE FROM $table WHERE
  4295. feed_id = '$feed_id' AND owner_uid = '$owner_uid'");
  4296. }
  4297. function ccache_update_all($link, $owner_uid) {
  4298. if (get_pref($link, 'ENABLE_FEED_CATS', $owner_uid)) {
  4299. $result = db_query($link, "SELECT feed_id FROM ttrss_cat_counters_cache
  4300. WHERE feed_id > 0 AND owner_uid = '$owner_uid'");
  4301. while ($line = db_fetch_assoc($result)) {
  4302. ccache_update($link, $line["feed_id"], $owner_uid, true);
  4303. }
  4304. /* We have to manually include category 0 */
  4305. ccache_update($link, 0, $owner_uid, true);
  4306. } else {
  4307. $result = db_query($link, "SELECT feed_id FROM ttrss_counters_cache
  4308. WHERE feed_id > 0 AND owner_uid = '$owner_uid'");
  4309. while ($line = db_fetch_assoc($result)) {
  4310. print ccache_update($link, $line["feed_id"], $owner_uid);
  4311. }
  4312. }
  4313. }
  4314. function ccache_find($link, $feed_id, $owner_uid, $is_cat = false,
  4315. $no_update = false) {
  4316. if (!is_numeric($feed_id)) return;
  4317. if (!$is_cat) {
  4318. $table = "ttrss_counters_cache";
  4319. if ($feed_id > 0) {
  4320. $tmp_result = db_query($link, "SELECT owner_uid FROM ttrss_feeds
  4321. WHERE id = '$feed_id'");
  4322. $owner_uid = db_fetch_result($tmp_result, 0, "owner_uid");
  4323. }
  4324. } else {
  4325. $table = "ttrss_cat_counters_cache";
  4326. }
  4327. if (DB_TYPE == "pgsql") {
  4328. $date_qpart = "updated > NOW() - INTERVAL '15 minutes'";
  4329. } else if (DB_TYPE == "mysql") {
  4330. $date_qpart = "updated > DATE_SUB(NOW(), INTERVAL 15 MINUTE)";
  4331. }
  4332. $result = db_query($link, "SELECT value FROM $table
  4333. WHERE owner_uid = '$owner_uid' AND feed_id = '$feed_id'
  4334. LIMIT 1");
  4335. if (db_num_rows($result) == 1) {
  4336. return db_fetch_result($result, 0, "value");
  4337. } else {
  4338. if ($no_update) {
  4339. return -1;
  4340. } else {
  4341. return ccache_update($link, $feed_id, $owner_uid, $is_cat);
  4342. }
  4343. }
  4344. }
  4345. function ccache_update($link, $feed_id, $owner_uid, $is_cat = false,
  4346. $update_pcat = true) {
  4347. if (!is_numeric($feed_id)) return;
  4348. if (!$is_cat && $feed_id > 0) {
  4349. $tmp_result = db_query($link, "SELECT owner_uid FROM ttrss_feeds
  4350. WHERE id = '$feed_id'");
  4351. $owner_uid = db_fetch_result($tmp_result, 0, "owner_uid");
  4352. }
  4353. $prev_unread = ccache_find($link, $feed_id, $owner_uid, $is_cat, true);
  4354. /* When updating a label, all we need to do is recalculate feed counters
  4355. * because labels are not cached */
  4356. if ($feed_id < 0) {
  4357. ccache_update_all($link, $owner_uid);
  4358. return;
  4359. }
  4360. if (!$is_cat) {
  4361. $table = "ttrss_counters_cache";
  4362. } else {
  4363. $table = "ttrss_cat_counters_cache";
  4364. }
  4365. if ($is_cat && $feed_id >= 0) {
  4366. if ($feed_id != 0) {
  4367. $cat_qpart = "cat_id = '$feed_id'";
  4368. } else {
  4369. $cat_qpart = "cat_id IS NULL";
  4370. }
  4371. /* Recalculate counters for child feeds */
  4372. $result = db_query($link, "SELECT id FROM ttrss_feeds
  4373. WHERE owner_uid = '$owner_uid' AND $cat_qpart");
  4374. while ($line = db_fetch_assoc($result)) {
  4375. ccache_update($link, $line["id"], $owner_uid, false, false);
  4376. }
  4377. $result = db_query($link, "SELECT SUM(value) AS sv
  4378. FROM ttrss_counters_cache, ttrss_feeds
  4379. WHERE id = feed_id AND $cat_qpart AND
  4380. ttrss_feeds.owner_uid = '$owner_uid'");
  4381. $unread = (int) db_fetch_result($result, 0, "sv");
  4382. } else {
  4383. $unread = (int) getFeedArticles($link, $feed_id, $is_cat, true, $owner_uid);
  4384. }
  4385. db_query($link, "BEGIN");
  4386. $result = db_query($link, "SELECT feed_id FROM $table
  4387. WHERE owner_uid = '$owner_uid' AND feed_id = '$feed_id' LIMIT 1");
  4388. if (db_num_rows($result) == 1) {
  4389. db_query($link, "UPDATE $table SET
  4390. value = '$unread', updated = NOW() WHERE
  4391. feed_id = '$feed_id' AND owner_uid = '$owner_uid'");
  4392. } else {
  4393. db_query($link, "INSERT INTO $table
  4394. (feed_id, value, owner_uid, updated)
  4395. VALUES
  4396. ($feed_id, $unread, $owner_uid, NOW())");
  4397. }
  4398. db_query($link, "COMMIT");
  4399. if ($feed_id > 0 && $prev_unread != $unread) {
  4400. if (!$is_cat) {
  4401. /* Update parent category */
  4402. if ($update_pcat) {
  4403. $result = db_query($link, "SELECT cat_id FROM ttrss_feeds
  4404. WHERE owner_uid = '$owner_uid' AND id = '$feed_id'");
  4405. $cat_id = (int) db_fetch_result($result, 0, "cat_id");
  4406. ccache_update($link, $cat_id, $owner_uid, true);
  4407. }
  4408. }
  4409. } else if ($feed_id < 0) {
  4410. ccache_update_all($link, $owner_uid);
  4411. }
  4412. return $unread;
  4413. }
  4414. function label_find_id($link, $label, $owner_uid) {
  4415. $result = db_query($link,
  4416. "SELECT id FROM ttrss_labels2 WHERE caption = '$label'
  4417. AND owner_uid = '$owner_uid' LIMIT 1");
  4418. if (db_num_rows($result) == 1) {
  4419. return db_fetch_result($result, 0, "id");
  4420. } else {
  4421. return 0;
  4422. }
  4423. }
  4424. function get_article_labels($link, $id) {
  4425. global $memcache;
  4426. $obj_id = md5("LABELS:$id:" . $_SESSION["uid"]);
  4427. $rv = array();
  4428. if ($memcache && $obj = $memcache->get($obj_id)) {
  4429. return $obj;
  4430. } else {
  4431. $result = db_query($link, "SELECT label_cache FROM
  4432. ttrss_user_entries WHERE ref_id = '$id' AND owner_uid = " .
  4433. $_SESSION["uid"]);
  4434. $label_cache = db_fetch_result($result, 0, "label_cache");
  4435. if ($label_cache) {
  4436. $label_cache = json_decode($label_cache, true);
  4437. if ($label_cache["no-labels"] == 1)
  4438. return $rv;
  4439. else
  4440. return $label_cache;
  4441. }
  4442. $result = db_query($link,
  4443. "SELECT DISTINCT label_id,caption,fg_color,bg_color
  4444. FROM ttrss_labels2, ttrss_user_labels2
  4445. WHERE id = label_id
  4446. AND article_id = '$id'
  4447. AND owner_uid = ".$_SESSION["uid"] . "
  4448. ORDER BY caption");
  4449. while ($line = db_fetch_assoc($result)) {
  4450. $rk = array($line["label_id"], $line["caption"], $line["fg_color"],
  4451. $line["bg_color"]);
  4452. array_push($rv, $rk);
  4453. }
  4454. if ($memcache) $memcache->add($obj_id, $rv, 0, 3600);
  4455. if (count($rv) > 0)
  4456. label_update_cache($link, $id, $rv);
  4457. else
  4458. label_update_cache($link, $id, array("no-labels" => 1));
  4459. }
  4460. return $rv;
  4461. }
  4462. function label_find_caption($link, $label, $owner_uid) {
  4463. $result = db_query($link,
  4464. "SELECT caption FROM ttrss_labels2 WHERE id = '$label'
  4465. AND owner_uid = '$owner_uid' LIMIT 1");
  4466. if (db_num_rows($result) == 1) {
  4467. return db_fetch_result($result, 0, "caption");
  4468. } else {
  4469. return "";
  4470. }
  4471. }
  4472. function label_update_cache($link, $id, $labels = false, $force = false) {
  4473. if ($force)
  4474. label_clear_cache($link, $id);
  4475. if (!$labels)
  4476. $labels = get_article_labels($link, $id);
  4477. $labels = db_escape_string(json_encode($labels));
  4478. db_query($link, "UPDATE ttrss_user_entries SET
  4479. label_cache = '$labels' WHERE ref_id = '$id'");
  4480. }
  4481. function label_clear_cache($link, $id) {
  4482. db_query($link, "UPDATE ttrss_user_entries SET
  4483. label_cache = '' WHERE ref_id = '$id'");
  4484. }
  4485. function label_remove_article($link, $id, $label, $owner_uid) {
  4486. $label_id = label_find_id($link, $label, $owner_uid);
  4487. if (!$label_id) return;
  4488. $result = db_query($link,
  4489. "DELETE FROM ttrss_user_labels2
  4490. WHERE
  4491. label_id = '$label_id' AND
  4492. article_id = '$id'");
  4493. label_clear_cache($link, $id);
  4494. }
  4495. function label_add_article($link, $id, $label, $owner_uid) {
  4496. global $memcache;
  4497. if ($memcache) {
  4498. $obj_id = md5("LABELS:$id:$owner_uid");
  4499. $memcache->delete($obj_id);
  4500. }
  4501. $label_id = label_find_id($link, $label, $owner_uid);
  4502. if (!$label_id) return;
  4503. $result = db_query($link,
  4504. "SELECT
  4505. article_id FROM ttrss_labels2, ttrss_user_labels2
  4506. WHERE
  4507. label_id = id AND
  4508. label_id = '$label_id' AND
  4509. article_id = '$id' AND owner_uid = '$owner_uid'
  4510. LIMIT 1");
  4511. if (db_num_rows($result) == 0) {
  4512. db_query($link, "INSERT INTO ttrss_user_labels2
  4513. (label_id, article_id) VALUES ('$label_id', '$id')");
  4514. }
  4515. label_clear_cache($link, $id);
  4516. }
  4517. function label_remove($link, $id, $owner_uid) {
  4518. global $memcache;
  4519. if (!$owner_uid) $owner_uid = $_SESSION["uid"];
  4520. if ($memcache) {
  4521. $obj_id = md5("LABELS:$id:$owner_uid");
  4522. $memcache->delete($obj_id);
  4523. }
  4524. db_query($link, "BEGIN");
  4525. $result = db_query($link, "SELECT caption FROM ttrss_labels2
  4526. WHERE id = '$id'");
  4527. $caption = db_fetch_result($result, 0, "caption");
  4528. $result = db_query($link, "DELETE FROM ttrss_labels2 WHERE id = '$id'
  4529. AND owner_uid = " . $owner_uid);
  4530. if (db_affected_rows($link, $result) != 0 && $caption) {
  4531. /* Remove access key for the label */
  4532. $ext_id = -11 - $id;
  4533. db_query($link, "DELETE FROM ttrss_access_keys WHERE
  4534. feed_id = '$ext_id' AND owner_uid = $owner_uid");
  4535. /* Disable filters that reference label being removed */
  4536. db_query($link, "UPDATE ttrss_filters SET
  4537. enabled = false WHERE action_param = '$caption'
  4538. AND action_id = 7
  4539. AND owner_uid = " . $owner_uid);
  4540. /* Remove cached data */
  4541. db_query($link, "UPDATE ttrss_user_entries SET label_cache = ''
  4542. WHERE label_cache LIKE '%$caption%' AND owner_uid = " . $owner_uid);
  4543. }
  4544. db_query($link, "COMMIT");
  4545. }
  4546. function label_create($link, $caption) {
  4547. db_query($link, "BEGIN");
  4548. $result = false;
  4549. $result = db_query($link, "SELECT id FROM ttrss_labels2
  4550. WHERE caption = '$caption' AND owner_uid = ". $_SESSION["uid"]);
  4551. if (db_num_rows($result) == 0) {
  4552. $result = db_query($link,
  4553. "INSERT INTO ttrss_labels2 (caption,owner_uid)
  4554. VALUES ('$caption', '".$_SESSION["uid"]."')");
  4555. $result = db_affected_rows($link, $result) != 0;
  4556. }
  4557. db_query($link, "COMMIT");
  4558. return $result;
  4559. }
  4560. function print_labels_headlines_dropdown($link, $feed_id) {
  4561. print "<option value=\"addLabel()\">".__("Create label...")."</option>";
  4562. $result = db_query($link, "SELECT id, caption FROM ttrss_labels2 WHERE
  4563. owner_uid = '".$_SESSION["uid"]."' ORDER BY caption");
  4564. while ($line = db_fetch_assoc($result)) {
  4565. $label_id = $line["id"];
  4566. $label_caption = $line["caption"];
  4567. $id = $line["id"];
  4568. if ($feed_id < -10 && $feed_id == -11-$label_id) {
  4569. print "<option id=\"LHDL-$id\"
  4570. value=\"selectionRemoveLabel($label_id)\">".
  4571. __('Remove:') . " $label_caption</option>";
  4572. } else {
  4573. print "<option id=\"LHDL-$id\"
  4574. value=\"selectionAssignLabel($label_id)\">".
  4575. __('Assign:') . " $label_caption</option>";
  4576. }
  4577. }
  4578. }
  4579. function format_tags_string($tags, $id) {
  4580. $tags_str = "";
  4581. $tags_nolinks_str = "";
  4582. $num_tags = 0;
  4583. /* if (get_user_theme($link) == "3pane") {
  4584. $tag_limit = 3;
  4585. } else {
  4586. $tag_limit = 6;
  4587. } */
  4588. $tag_limit = 6;
  4589. $formatted_tags = array();
  4590. foreach ($tags as $tag) {
  4591. $num_tags++;
  4592. $tag_escaped = str_replace("'", "\\'", $tag);
  4593. if (mb_strlen($tag) > 30) {
  4594. $tag = truncate_string($tag, 30);
  4595. }
  4596. $tag_str = "<a href=\"javascript:viewfeed('$tag_escaped')\">$tag</a>";
  4597. array_push($formatted_tags, $tag_str);
  4598. $tmp_tags_str = implode(", ", $formatted_tags);
  4599. if ($num_tags == $tag_limit || mb_strlen($tmp_tags_str) > 150) {
  4600. break;
  4601. }
  4602. }
  4603. $tags_str = implode(", ", $formatted_tags);
  4604. if ($num_tags < count($tags)) {
  4605. $tags_str .= ", &hellip;";
  4606. }
  4607. if ($num_tags == 0) {
  4608. $tags_str = __("no tags");
  4609. }
  4610. return $tags_str;
  4611. }
  4612. function format_article_labels($labels, $id) {
  4613. $labels_str = "";
  4614. foreach ($labels as $l) {
  4615. $labels_str .= sprintf("<span class='hlLabelRef'
  4616. style='color : %s; background-color : %s'>%s</span>",
  4617. $l[2], $l[3], $l[1]);
  4618. }
  4619. return $labels_str;
  4620. }
  4621. function format_article_note($id, $note) {
  4622. $str = "<div class='articleNote' title=\"".__('edit note')."\"
  4623. onclick=\"editArticleNote($id)\">$note</div>";
  4624. return $str;
  4625. }
  4626. function toggle_collapse_cat($link, $cat_id, $mode) {
  4627. if ($cat_id > 0) {
  4628. $mode = bool_to_sql_bool($mode);
  4629. db_query($link, "UPDATE ttrss_feed_categories SET
  4630. collapsed = $mode WHERE id = '$cat_id' AND owner_uid = " .
  4631. $_SESSION["uid"]);
  4632. } else {
  4633. $pref_name = '';
  4634. switch ($cat_id) {
  4635. case -1:
  4636. $pref_name = '_COLLAPSED_SPECIAL';
  4637. break;
  4638. case -2:
  4639. $pref_name = '_COLLAPSED_LABELS';
  4640. break;
  4641. case 0:
  4642. $pref_name = '_COLLAPSED_UNCAT';
  4643. break;
  4644. }
  4645. if ($pref_name) {
  4646. if ($mode) {
  4647. set_pref($link, $pref_name, 'true');
  4648. } else {
  4649. set_pref($link, $pref_name, 'false');
  4650. }
  4651. }
  4652. }
  4653. }
  4654. function remove_feed($link, $id, $owner_uid) {
  4655. if ($id > 0) {
  4656. /* save starred articles in Archived feed */
  4657. db_query($link, "BEGIN");
  4658. /* prepare feed if necessary */
  4659. $result = db_query($link, "SELECT id FROM ttrss_archived_feeds
  4660. WHERE id = '$id'");
  4661. if (db_num_rows($result) == 0) {
  4662. db_query($link, "INSERT INTO ttrss_archived_feeds
  4663. (id, owner_uid, title, feed_url, site_url)
  4664. SELECT id, owner_uid, title, feed_url, site_url from ttrss_feeds
  4665. WHERE id = '$id'");
  4666. }
  4667. db_query($link, "UPDATE ttrss_user_entries SET feed_id = NULL,
  4668. orig_feed_id = '$id' WHERE feed_id = '$id' AND
  4669. marked = true AND owner_uid = $owner_uid");
  4670. /* Remove access key for the feed */
  4671. db_query($link, "DELETE FROM ttrss_access_keys WHERE
  4672. feed_id = '$id' AND owner_uid = $owner_uid");
  4673. /* remove the feed */
  4674. db_query($link, "DELETE FROM ttrss_feeds
  4675. WHERE id = '$id' AND owner_uid = $owner_uid");
  4676. db_query($link, "COMMIT");
  4677. /* if (file_exists(ICONS_DIR . "/$id.ico")) {
  4678. unlink(ICONS_DIR . "/$id.ico");
  4679. } */
  4680. ccache_remove($link, $id, $owner_uid);
  4681. } else {
  4682. label_remove($link, -11-$id, $owner_uid);
  4683. ccache_remove($link, -11-$id, $owner_uid);
  4684. }
  4685. }
  4686. function add_feed_category($link, $feed_cat) {
  4687. if (!$feed_cat) return false;
  4688. db_query($link, "BEGIN");
  4689. $result = db_query($link,
  4690. "SELECT id FROM ttrss_feed_categories
  4691. WHERE title = '$feed_cat' AND owner_uid = ".$_SESSION["uid"]);
  4692. if (db_num_rows($result) == 0) {
  4693. $result = db_query($link,
  4694. "INSERT INTO ttrss_feed_categories (owner_uid,title)
  4695. VALUES ('".$_SESSION["uid"]."', '$feed_cat')");
  4696. db_query($link, "COMMIT");
  4697. return true;
  4698. }
  4699. return false;
  4700. }
  4701. function remove_feed_category($link, $id, $owner_uid) {
  4702. db_query($link, "DELETE FROM ttrss_feed_categories
  4703. WHERE id = '$id' AND owner_uid = $owner_uid");
  4704. ccache_remove($link, $id, $owner_uid, true);
  4705. }
  4706. function archive_article($link, $id, $owner_uid) {
  4707. db_query($link, "BEGIN");
  4708. $result = db_query($link, "SELECT feed_id FROM ttrss_user_entries
  4709. WHERE ref_id = '$id' AND owner_uid = $owner_uid");
  4710. if (db_num_rows($result) != 0) {
  4711. /* prepare the archived table */
  4712. $feed_id = (int) db_fetch_result($result, 0, "feed_id");
  4713. if ($feed_id) {
  4714. $result = db_query($link, "SELECT id FROM ttrss_archived_feeds
  4715. WHERE id = '$feed_id'");
  4716. if (db_num_rows($result) == 0) {
  4717. db_query($link, "INSERT INTO ttrss_archived_feeds
  4718. (id, owner_uid, title, feed_url, site_url)
  4719. SELECT id, owner_uid, title, feed_url, site_url from ttrss_feeds
  4720. WHERE id = '$feed_id'");
  4721. }
  4722. db_query($link, "UPDATE ttrss_user_entries
  4723. SET orig_feed_id = feed_id, feed_id = NULL
  4724. WHERE ref_id = '$id' AND owner_uid = " . $_SESSION["uid"]);
  4725. }
  4726. }
  4727. db_query($link, "COMMIT");
  4728. }
  4729. function getArticleFeed($link, $id) {
  4730. $result = db_query($link, "SELECT feed_id FROM ttrss_user_entries
  4731. WHERE ref_id = '$id' AND owner_uid = " . $_SESSION["uid"]);
  4732. if (db_num_rows($result) != 0) {
  4733. return db_fetch_result($result, 0, "feed_id");
  4734. } else {
  4735. return 0;
  4736. }
  4737. }
  4738. function make_url_from_parts($parts) {
  4739. $url = $parts['scheme'] . '://' . $parts['host'];
  4740. if ($parts['path']) $url .= $parts['path'];
  4741. if ($parts['query']) $url .= '?' . $parts['query'];
  4742. return $url;
  4743. }
  4744. /**
  4745. * Fixes incomplete URLs by prepending "http://".
  4746. * Also replaces feed:// with http://, and
  4747. * prepends a trailing slash if the url is a domain name only.
  4748. *
  4749. * @param string $url Possibly incomplete URL
  4750. *
  4751. * @return string Fixed URL.
  4752. */
  4753. function fix_url($url) {
  4754. if (strpos($url, '://') === false) {
  4755. $url = 'http://' . $url;
  4756. } else if (substr($url, 0, 5) == 'feed:') {
  4757. $url = 'http:' . substr($url, 5);
  4758. }
  4759. //prepend slash if the URL has no slash in it
  4760. // "http://www.example" -> "http://www.example/"
  4761. if (strpos($url, '/', strpos($url, ':') + 3) === false) {
  4762. $url .= '/';
  4763. }
  4764. if ($url != "http:///")
  4765. return $url;
  4766. else
  4767. return '';
  4768. }
  4769. function validate_feed_url($url) {
  4770. $parts = parse_url($url);
  4771. return ($parts['scheme'] == 'http' || $parts['scheme'] == 'feed' || $parts['scheme'] == 'https');
  4772. }
  4773. function get_article_enclosures($link, $id) {
  4774. global $memcache;
  4775. $query = "SELECT * FROM ttrss_enclosures
  4776. WHERE post_id = '$id' AND content_url != ''";
  4777. $obj_id = md5("ENCLOSURES:$id");
  4778. $rv = array();
  4779. if ($memcache && $obj = $memcache->get($obj_id)) {
  4780. $rv = $obj;
  4781. } else {
  4782. $result = db_query($link, $query);
  4783. if (db_num_rows($result) > 0) {
  4784. while ($line = db_fetch_assoc($result)) {
  4785. array_push($rv, $line);
  4786. }
  4787. if ($memcache) $memcache->add($obj_id, $rv, 0, 3600);
  4788. }
  4789. }
  4790. return $rv;
  4791. }
  4792. function api_get_feeds($link, $cat_id, $unread_only, $limit, $offset) {
  4793. $feeds = array();
  4794. /* Labels */
  4795. if ($cat_id == -4 || $cat_id == -2) {
  4796. $counters = getLabelCounters($link, true);
  4797. foreach (array_values($counters) as $cv) {
  4798. $unread = $cv["counter"];
  4799. if ($unread || !$unread_only) {
  4800. $row = array(
  4801. "id" => $cv["id"],
  4802. "title" => $cv["description"],
  4803. "unread" => $cv["counter"],
  4804. "cat_id" => -2,
  4805. );
  4806. array_push($feeds, $row);
  4807. }
  4808. }
  4809. }
  4810. /* Virtual feeds */
  4811. if ($cat_id == -4 || $cat_id == -1) {
  4812. foreach (array(-1, -2, -3, -4, 0) as $i) {
  4813. $unread = getFeedUnread($link, $i);
  4814. if ($unread || !$unread_only) {
  4815. $title = getFeedTitle($link, $i);
  4816. $row = array(
  4817. "id" => $i,
  4818. "title" => $title,
  4819. "unread" => $unread,
  4820. "cat_id" => -1,
  4821. );
  4822. array_push($feeds, $row);
  4823. }
  4824. }
  4825. }
  4826. /* Real feeds */
  4827. if ($limit) {
  4828. $limit_qpart = "LIMIT $limit OFFSET $offset";
  4829. } else {
  4830. $limit_qpart = "";
  4831. }
  4832. if ($cat_id == -4 || $cat_id == -3) {
  4833. $result = db_query($link, "SELECT
  4834. id, feed_url, cat_id, title, ".
  4835. SUBSTRING_FOR_DATE."(last_updated,1,19) AS last_updated
  4836. FROM ttrss_feeds WHERE owner_uid = " . $_SESSION["uid"] .
  4837. " ORDER BY cat_id, title " . $limit_qpart);
  4838. } else {
  4839. if ($cat_id)
  4840. $cat_qpart = "cat_id = '$cat_id'";
  4841. else
  4842. $cat_qpart = "cat_id IS NULL";
  4843. $result = db_query($link, "SELECT
  4844. id, feed_url, cat_id, title, ".
  4845. SUBSTRING_FOR_DATE."(last_updated,1,19) AS last_updated
  4846. FROM ttrss_feeds WHERE
  4847. $cat_qpart AND owner_uid = " . $_SESSION["uid"] .
  4848. " ORDER BY cat_id, title " . $limit_qpart);
  4849. }
  4850. while ($line = db_fetch_assoc($result)) {
  4851. $unread = getFeedUnread($link, $line["id"]);
  4852. $has_icon = feed_has_icon($line['id']);
  4853. if ($unread || !$unread_only) {
  4854. $row = array(
  4855. "feed_url" => $line["feed_url"],
  4856. "title" => $line["title"],
  4857. "id" => (int)$line["id"],
  4858. "unread" => (int)$unread,
  4859. "has_icon" => $has_icon,
  4860. "cat_id" => (int)$line["cat_id"],
  4861. "last_updated" => strtotime($line["last_updated"])
  4862. );
  4863. array_push($feeds, $row);
  4864. }
  4865. }
  4866. return $feeds;
  4867. }
  4868. function api_get_headlines($link, $feed_id, $limit, $offset,
  4869. $filter, $is_cat, $show_excerpt, $show_content, $view_mode, $order) {
  4870. /* do not rely on params below */
  4871. $search = db_escape_string($_REQUEST["search"]);
  4872. $search_mode = db_escape_string($_REQUEST["search_mode"]);
  4873. $match_on = db_escape_string($_REQUEST["match_on"]);
  4874. $qfh_ret = queryFeedHeadlines($link, $feed_id, $limit,
  4875. $view_mode, $is_cat, $search, $search_mode, $match_on,
  4876. $order, $offset);
  4877. $result = $qfh_ret[0];
  4878. $feed_title = $qfh_ret[1];
  4879. $headlines = array();
  4880. while ($line = db_fetch_assoc($result)) {
  4881. $is_updated = ($line["last_read"] == "" &&
  4882. ($line["unread"] != "t" && $line["unread"] != "1"));
  4883. $headline_row = array(
  4884. "id" => (int)$line["id"],
  4885. "unread" => sql_bool_to_bool($line["unread"]),
  4886. "marked" => sql_bool_to_bool($line["marked"]),
  4887. "published" => sql_bool_to_bool($line["published"]),
  4888. "updated" => strtotime($line["updated"]),
  4889. "is_updated" => $is_updated,
  4890. "title" => $line["title"],
  4891. "link" => $line["link"],
  4892. "feed_id" => $line["feed_id"],
  4893. "tags" => get_article_tags($link, $line["id"]),
  4894. );
  4895. if ($show_excerpt) {
  4896. $excerpt = truncate_string(strip_tags($line["content_preview"]), 100);
  4897. $headline_row["excerpt"] = $excerpt;
  4898. }
  4899. if ($show_content) {
  4900. $headline_row["content"] = $line["content_preview"];
  4901. }
  4902. array_push($headlines, $headline_row);
  4903. }
  4904. return $headlines;
  4905. }
  4906. function generate_error_feed($link, $error) {
  4907. $reply = array();
  4908. $reply['headlines']['id'] = -6;
  4909. $reply['headlines']['is_cat'] = false;
  4910. $reply['headlines']['toolbar'] = '';
  4911. $reply['headlines']['content'] = "<div class='whiteBox'>". $error . "</div>";
  4912. $reply['headlines-info'] = array("count" => 0,
  4913. "vgroup_last_feed" => '',
  4914. "unread" => 0,
  4915. "disable_cache" => true);
  4916. return $reply;
  4917. }
  4918. function generate_dashboard_feed($link) {
  4919. $reply = array();
  4920. $reply['headlines']['id'] = -5;
  4921. $reply['headlines']['is_cat'] = false;
  4922. $reply['headlines']['toolbar'] = '';
  4923. $reply['headlines']['content'] = "<div class='whiteBox'>".__('No feed selected.');
  4924. $reply['headlines']['content'] .= "<p class=\"small\"><span class=\"insensitive\">";
  4925. $result = db_query($link, "SELECT ".SUBSTRING_FOR_DATE."(MAX(last_updated), 1, 19) AS last_updated FROM ttrss_feeds
  4926. WHERE owner_uid = " . $_SESSION['uid']);
  4927. $last_updated = db_fetch_result($result, 0, "last_updated");
  4928. $last_updated = make_local_datetime($link, $last_updated, false);
  4929. $reply['headlines']['content'] .= sprintf(__("Feeds last updated at %s"), $last_updated);
  4930. $result = db_query($link, "SELECT COUNT(id) AS num_errors
  4931. FROM ttrss_feeds WHERE last_error != '' AND owner_uid = ".$_SESSION["uid"]);
  4932. $num_errors = db_fetch_result($result, 0, "num_errors");
  4933. if ($num_errors > 0) {
  4934. $reply['headlines']['content'] .= "<br/>";
  4935. $reply['headlines']['content'] .= "<a class=\"insensitive\" href=\"#\" onclick=\"showFeedsWithErrors()\">".
  4936. __('Some feeds have update errors (click for details)')."</a>";
  4937. }
  4938. $reply['headlines']['content'] .= "</span></p>";
  4939. $reply['headlines-info'] = array("count" => 0,
  4940. "vgroup_last_feed" => '',
  4941. "unread" => 0,
  4942. "disable_cache" => true);
  4943. return $reply;
  4944. }
  4945. function save_email_address($link, $email) {
  4946. // FIXME: implement persistent storage of emails
  4947. if (!$_SESSION['stored_emails'])
  4948. $_SESSION['stored_emails'] = array();
  4949. if (!in_array($email, $_SESSION['stored_emails']))
  4950. array_push($_SESSION['stored_emails'], $email);
  4951. }
  4952. function update_feed_access_key($link, $feed_id, $is_cat, $owner_uid = false) {
  4953. if (!$owner_uid) $owner_uid = $_SESSION["uid"];
  4954. $sql_is_cat = bool_to_sql_bool($is_cat);
  4955. $result = db_query($link, "SELECT access_key FROM ttrss_access_keys
  4956. WHERE feed_id = '$feed_id' AND is_cat = $sql_is_cat
  4957. AND owner_uid = " . $owner_uid);
  4958. if (db_num_rows($result) == 1) {
  4959. $key = db_escape_string(sha1(uniqid(rand(), true)));
  4960. db_query($link, "UPDATE ttrss_access_keys SET access_key = '$key'
  4961. WHERE feed_id = '$feed_id' AND is_cat = $sql_is_cat
  4962. AND owner_uid = " . $owner_uid);
  4963. return $key;
  4964. } else {
  4965. return get_feed_access_key($link, $feed_id, $is_cat, $owner_uid);
  4966. }
  4967. }
  4968. function get_feed_access_key($link, $feed_id, $is_cat, $owner_uid = false) {
  4969. if (!$owner_uid) $owner_uid = $_SESSION["uid"];
  4970. $sql_is_cat = bool_to_sql_bool($is_cat);
  4971. $result = db_query($link, "SELECT access_key FROM ttrss_access_keys
  4972. WHERE feed_id = '$feed_id' AND is_cat = $sql_is_cat
  4973. AND owner_uid = " . $owner_uid);
  4974. if (db_num_rows($result) == 1) {
  4975. return db_fetch_result($result, 0, "access_key");
  4976. } else {
  4977. $key = db_escape_string(sha1(uniqid(rand(), true)));
  4978. $result = db_query($link, "INSERT INTO ttrss_access_keys
  4979. (access_key, feed_id, is_cat, owner_uid)
  4980. VALUES ('$key', '$feed_id', $sql_is_cat, '$owner_uid')");
  4981. return $key;
  4982. }
  4983. return false;
  4984. }
  4985. /**
  4986. * Extracts RSS/Atom feed URLs from the given HTML URL.
  4987. *
  4988. * @param string $url HTML page URL
  4989. *
  4990. * @return array Array of feeds. Key is the full URL, value the title
  4991. */
  4992. function get_feeds_from_html($url, $login = false, $pass = false)
  4993. {
  4994. $url = fix_url($url);
  4995. $baseUrl = substr($url, 0, strrpos($url, '/') + 1);
  4996. libxml_use_internal_errors(true);
  4997. $content = @fetch_file_contents($url, false, $login, $pass);
  4998. $doc = new DOMDocument();
  4999. $doc->loadHTML($content);
  5000. $xpath = new DOMXPath($doc);
  5001. $entries = $xpath->query('/html/head/link[@rel="alternate"]');
  5002. $feedUrls = array();
  5003. foreach ($entries as $entry) {
  5004. if ($entry->hasAttribute('href')) {
  5005. $title = $entry->getAttribute('title');
  5006. if ($title == '') {
  5007. $title = $entry->getAttribute('type');
  5008. }
  5009. $feedUrl = rewrite_relative_url(
  5010. $baseUrl, $entry->getAttribute('href')
  5011. );
  5012. $feedUrls[$feedUrl] = $title;
  5013. }
  5014. }
  5015. return $feedUrls;
  5016. }
  5017. /**
  5018. * Checks if the content behind the given URL is a HTML file
  5019. *
  5020. * @param string $url URL to check
  5021. *
  5022. * @return boolean True if the URL contains HTML content
  5023. */
  5024. function url_is_html($url, $login = false, $pass = false) {
  5025. $content = substr(fetch_file_contents($url, false, $login, $pass), 0, 1000);
  5026. if (stripos($content, '<html>') === false
  5027. && stripos($content, '<html ') === false
  5028. ) {
  5029. return false;
  5030. }
  5031. return true;
  5032. }
  5033. function print_label_select($link, $name, $value, $attributes = "") {
  5034. $result = db_query($link, "SELECT caption FROM ttrss_labels2
  5035. WHERE owner_uid = '".$_SESSION["uid"]."' ORDER BY caption");
  5036. print "<select default=\"$value\" name=\"" . htmlspecialchars($name) .
  5037. "\" $attributes onchange=\"labelSelectOnChange(this)\" >";
  5038. while ($line = db_fetch_assoc($result)) {
  5039. $issel = ($line["caption"] == $value) ? "selected=\"1\"" : "";
  5040. print "<option value=\"".htmlspecialchars($line["caption"])."\"
  5041. $issel>" . htmlspecialchars($line["caption"]) . "</option>";
  5042. }
  5043. # print "<option value=\"ADD_LABEL\">" .__("Add label...") . "</option>";
  5044. print "</select>";
  5045. }
  5046. function format_article_enclosures($link, $id, $always_display_enclosures,
  5047. $article_content) {
  5048. $result = get_article_enclosures($link, $id);
  5049. $rv = '';
  5050. if (count($result) > 0) {
  5051. $entries_html = array();
  5052. $entries = array();
  5053. foreach ($result as $line) {
  5054. $url = $line["content_url"];
  5055. $ctype = $line["content_type"];
  5056. if (!$ctype) $ctype = __("unknown type");
  5057. # $filename = substr($url, strrpos($url, "/")+1);
  5058. $entry = format_inline_player($link, $url, $ctype);
  5059. # $entry .= " <a target=\"_blank\" href=\"" . htmlspecialchars($url) . "\">" .
  5060. # $filename . " (" . $ctype . ")" . "</a>";
  5061. array_push($entries_html, $entry);
  5062. $entry = array();
  5063. $entry["type"] = $ctype;
  5064. $entry["filename"] = $filename;
  5065. $entry["url"] = $url;
  5066. array_push($entries, $entry);
  5067. }
  5068. $rv .= "<div class=\"postEnclosures\">";
  5069. if (!get_pref($link, "STRIP_IMAGES")) {
  5070. if ($always_display_enclosures ||
  5071. !preg_match("/<img/i", $article_content)) {
  5072. foreach ($entries as $entry) {
  5073. if (preg_match("/image/", $entry["type"]) ||
  5074. preg_match("/\.(jpg|png|gif|bmp)/i", $entry["filename"])) {
  5075. $rv .= "<p><img
  5076. alt=\"".htmlspecialchars($entry["filename"])."\"
  5077. src=\"" .htmlspecialchars($entry["url"]) . "\"/></p>";
  5078. }
  5079. }
  5080. }
  5081. }
  5082. if (count($entries) == 1) {
  5083. $rv .= __("Attachment:") . " ";
  5084. } else {
  5085. $rv .= __("Attachments:") . " ";
  5086. }
  5087. $rv .= join(", ", $entries_html);
  5088. $rv .= "</div>";
  5089. }
  5090. return $rv;
  5091. }
  5092. function getLastArticleId($link) {
  5093. $result = db_query($link, "SELECT MAX(ref_id) AS id FROM ttrss_user_entries
  5094. WHERE owner_uid = " . $_SESSION["uid"]);
  5095. if (db_num_rows($result) == 1) {
  5096. return db_fetch_result($result, 0, "id");
  5097. } else {
  5098. return -1;
  5099. }
  5100. }
  5101. function build_url($parts) {
  5102. return $parts['scheme'] . "://" . $parts['host'] . $parts['path'];
  5103. }
  5104. /**
  5105. * Converts a (possibly) relative URL to a absolute one.
  5106. *
  5107. * @param string $url Base URL (i.e. from where the document is)
  5108. * @param string $rel_url Possibly relative URL in the document
  5109. *
  5110. * @return string Absolute URL
  5111. */
  5112. function rewrite_relative_url($url, $rel_url) {
  5113. if (strpos($rel_url, "://") !== false) {
  5114. return $rel_url;
  5115. } else if (strpos($rel_url, "/") === 0)
  5116. {
  5117. $parts = parse_url($url);
  5118. $parts['path'] = $rel_url;
  5119. return build_url($parts);
  5120. } else {
  5121. $parts = parse_url($url);
  5122. if (!isset($parts['path'])) {
  5123. $parts['path'] = '/';
  5124. }
  5125. $dir = $parts['path'];
  5126. if (substr($dir, -1) !== '/') {
  5127. $dir = dirname($parts['path']);
  5128. $dir !== '/' && $dir .= '/';
  5129. }
  5130. $parts['path'] = $dir . $rel_url;
  5131. return build_url($parts);
  5132. }
  5133. }
  5134. function sphinx_search($query, $offset = 0, $limit = 30) {
  5135. $sphinxClient = new SphinxClient();
  5136. $sphinxClient->SetServer('localhost', 9312);
  5137. $sphinxClient->SetConnectTimeout(1);
  5138. $sphinxClient->SetFieldWeights(array('title' => 70, 'content' => 30,
  5139. 'feed_title' => 20));
  5140. $sphinxClient->SetMatchMode(SPH_MATCH_EXTENDED2);
  5141. $sphinxClient->SetRankingMode(SPH_RANK_PROXIMITY_BM25);
  5142. $sphinxClient->SetLimits($offset, $limit, 1000);
  5143. $sphinxClient->SetArrayResult(false);
  5144. $sphinxClient->SetFilter('owner_uid', array($_SESSION['uid']));
  5145. $result = $sphinxClient->Query($query, SPHINX_INDEX);
  5146. $ids = array();
  5147. if (is_array($result['matches'])) {
  5148. foreach (array_keys($result['matches']) as $int_id) {
  5149. $ref_id = $result['matches'][$int_id]['attrs']['ref_id'];
  5150. array_push($ids, $ref_id);
  5151. }
  5152. }
  5153. return $ids;
  5154. }
  5155. function cleanup_tags($link, $days = 14, $limit = 1000) {
  5156. if (DB_TYPE == "pgsql") {
  5157. $interval_query = "date_updated < NOW() - INTERVAL '$days days'";
  5158. } else if (DB_TYPE == "mysql") {
  5159. $interval_query = "date_updated < DATE_SUB(NOW(), INTERVAL $days DAY)";
  5160. }
  5161. $tags_deleted = 0;
  5162. while ($limit > 0) {
  5163. $limit_part = 500;
  5164. $query = "SELECT ttrss_tags.id AS id
  5165. FROM ttrss_tags, ttrss_user_entries, ttrss_entries
  5166. WHERE post_int_id = int_id AND $interval_query AND
  5167. ref_id = ttrss_entries.id AND tag_cache != '' LIMIT $limit_part";
  5168. $result = db_query($link, $query);
  5169. $ids = array();
  5170. while ($line = db_fetch_assoc($result)) {
  5171. array_push($ids, $line['id']);
  5172. }
  5173. if (count($ids) > 0) {
  5174. $ids = join(",", $ids);
  5175. print ".";
  5176. $tmp_result = db_query($link, "DELETE FROM ttrss_tags WHERE id IN ($ids)");
  5177. $tags_deleted += db_affected_rows($link, $tmp_result);
  5178. } else {
  5179. break;
  5180. }
  5181. $limit -= $limit_part;
  5182. }
  5183. print "\n";
  5184. return $tags_deleted;
  5185. }
  5186. function feedlist_init_cat($link, $cat_id, $hidden = false) {
  5187. $obj = array();
  5188. $cat_id = (int) $cat_id;
  5189. if ($cat_id > 0) {
  5190. $cat_unread = ccache_find($link, $cat_id, $_SESSION["uid"], true);
  5191. } else if ($cat_id == 0 || $cat_id == -2) {
  5192. $cat_unread = getCategoryUnread($link, $cat_id);
  5193. }
  5194. $obj['id'] = 'CAT:' . $cat_id;
  5195. $obj['items'] = array();
  5196. $obj['name'] = getCategoryTitle($link, $cat_id);
  5197. $obj['type'] = 'feed';
  5198. $obj['unread'] = (int) $cat_unread;
  5199. $obj['hidden'] = $hidden;
  5200. $obj['bare_id'] = $cat_id;
  5201. return $obj;
  5202. }
  5203. function feedlist_init_feed($link, $feed_id, $title = false, $unread = false, $error = '', $updated = '') {
  5204. $obj = array();
  5205. $feed_id = (int) $feed_id;
  5206. if (!$title)
  5207. $title = getFeedTitle($link, $feed_id, false);
  5208. if ($unread === false)
  5209. $unread = getFeedUnread($link, $feed_id, false);
  5210. $obj['id'] = 'FEED:' . $feed_id;
  5211. $obj['name'] = $title;
  5212. $obj['unread'] = (int) $unread;
  5213. $obj['type'] = 'feed';
  5214. $obj['error'] = $error;
  5215. $obj['updated'] = $updated;
  5216. $obj['icon'] = getFeedIcon($feed_id);
  5217. $obj['bare_id'] = $feed_id;
  5218. return $obj;
  5219. }
  5220. function fetch_twitter_rss($link, $url, $owner_uid) {
  5221. $result = db_query($link, "SELECT twitter_oauth FROM ttrss_users
  5222. WHERE id = $owner_uid");
  5223. $access_token = json_decode(db_fetch_result($result, 0, 'twitter_oauth'), true);
  5224. $url_escaped = db_escape_string($url);
  5225. if ($access_token) {
  5226. $tmhOAuth = new tmhOAuth(array(
  5227. 'consumer_key' => CONSUMER_KEY,
  5228. 'consumer_secret' => CONSUMER_SECRET,
  5229. 'user_token' => $access_token['oauth_token'],
  5230. 'user_secret' => $access_token['oauth_token_secret'],
  5231. ));
  5232. $code = $tmhOAuth->request('GET', $url);
  5233. if ($code == 200) {
  5234. $content = $tmhOAuth->response['response'];
  5235. $rss = new MagpieRSS($content, MAGPIE_OUTPUT_ENCODING,
  5236. MAGPIE_INPUT_ENCODING, MAGPIE_DETECT_ENCODING );
  5237. return $rss;
  5238. } else {
  5239. db_query($link, "UPDATE ttrss_feeds
  5240. SET last_error = 'OAuth authorization failed ($code).'
  5241. WHERE feed_url = '$url_escaped' AND owner_uid = $owner_uid");
  5242. }
  5243. } else {
  5244. db_query($link, "UPDATE ttrss_feeds
  5245. SET last_error = 'OAuth information not found.'
  5246. WHERE feed_url = '$url_escaped' AND owner_uid = $owner_uid");
  5247. return false;
  5248. }
  5249. }
  5250. function print_user_stylesheet($link) {
  5251. $value = get_pref($link, 'USER_STYLESHEET');
  5252. if ($value) {
  5253. print "<style type=\"text/css\">";
  5254. print str_replace("<br/>", "\n", $value);
  5255. print "</style>";
  5256. }
  5257. }
  5258. function rewrite_urls($line) {
  5259. global $url_regex;
  5260. $urls = null;
  5261. $result = preg_replace("/((?<!=.)((http|https|ftp)+):\/\/[^ ,!]+)/i",
  5262. "<a target=\"_blank\" href=\"\\1\">\\1</a>", $line);
  5263. return $result;
  5264. }
  5265. ?>